Subversion Repositories Kolibri OS

Rev

Rev 4364 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright 2007 James Bursa <bursa@users.sourceforge.net>
  3.  * Copyright 2010 Michael Drake <tlsa@netsurf-browser.org>
  4.  *
  5.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  6.  *
  7.  * NetSurf is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; version 2 of the License.
  10.  *
  11.  * NetSurf is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  */
  19.  
  20. /** \file
  21.  * Content for text/html (implementation).
  22.  */
  23.  
  24. #include <assert.h>
  25. #include <ctype.h>
  26. #include <stdint.h>
  27. #include <string.h>
  28. #include <strings.h>
  29. #include <stdlib.h>
  30.  
  31. #include "utils/config.h"
  32. #include "content/content_protected.h"
  33. #include "content/fetch.h"
  34. #include "content/hlcache.h"
  35. #include "desktop/options.h"
  36. #include "desktop/selection.h"
  37. #include "desktop/scrollbar.h"
  38. #include "image/bitmap.h"
  39. #include "render/box.h"
  40. #include "render/font.h"
  41. #include "render/form.h"
  42. #include "render/html_internal.h"
  43. #include "render/imagemap.h"
  44. #include "render/layout.h"
  45. #include "render/search.h"
  46. #include "javascript/js.h"
  47. #include "utils/corestrings.h"
  48. #include "utils/http.h"
  49. #include "utils/libdom.h"
  50. #include "utils/log.h"
  51. #include "utils/messages.h"
  52. #include "utils/schedule.h"
  53. #include "utils/talloc.h"
  54. #include "utils/url.h"
  55. #include "utils/utf8.h"
  56. #include "utils/utils.h"
  57.  
  58. #define CHUNK 4096
  59.  
  60. /* Change these to 1 to cause a dump to stderr of the frameset or box
  61.  * when the trees have been built.
  62.  */
  63. #define ALWAYS_DUMP_FRAMESET 0
  64. #define ALWAYS_DUMP_BOX 0
  65.  
  66. static const char *html_types[] = {
  67.         "application/xhtml+xml",
  68.         "text/html"
  69. };
  70.  
  71. /* forward declared functions */
  72. static void html_object_refresh(void *p);
  73.  
  74. /* pre-interned character set */
  75. static lwc_string *html_charset;
  76.  
  77. static nsurl *html_default_stylesheet_url;
  78. static nsurl *html_adblock_stylesheet_url;
  79. static nsurl *html_quirks_stylesheet_url;
  80. static nsurl *html_user_stylesheet_url;
  81.  
  82. static nserror css_error_to_nserror(css_error error)
  83. {
  84.         switch (error) {
  85.         case CSS_OK:
  86.                 return NSERROR_OK;
  87.  
  88.         case CSS_NOMEM:
  89.                 return NSERROR_NOMEM;
  90.  
  91.         case CSS_BADPARM:
  92.                 return NSERROR_BAD_PARAMETER;
  93.  
  94.         case CSS_INVALID:
  95.                 return NSERROR_INVALID;
  96.  
  97.         case CSS_FILENOTFOUND:
  98.                 return NSERROR_NOT_FOUND;
  99.  
  100.         case CSS_NEEDDATA:
  101.                 return NSERROR_NEED_DATA;
  102.  
  103.         case CSS_BADCHARSET:
  104.                 return NSERROR_BAD_ENCODING;
  105.  
  106.         case CSS_EOF:
  107.         case CSS_IMPORTS_PENDING:
  108.         case CSS_PROPERTY_NOT_SET:
  109.         default:
  110.                 break;
  111.         }
  112.         return NSERROR_CSS;
  113. }
  114.  
  115.  
  116. static void html_destroy_objects(html_content *html)
  117. {
  118.         while (html->object_list != NULL) {
  119.                 struct content_html_object *victim = html->object_list;
  120.  
  121.                 if (victim->content != NULL) {
  122.                         LOG(("object %p", victim->content));
  123.  
  124.                         if (content_get_type(victim->content) == CONTENT_HTML)
  125.                                 schedule_remove(html_object_refresh, victim);
  126.  
  127.                         hlcache_handle_release(victim->content);
  128.                 }
  129.  
  130.                 html->object_list = victim->next;
  131.                 free(victim);
  132.         }
  133. }
  134.  
  135. /**
  136.  * Perform post-box-creation conversion of a document
  137.  *
  138.  * \param c        HTML content to complete conversion of
  139.  * \param success  Whether box tree construction was successful
  140.  */
  141. static void html_box_convert_done(html_content *c, bool success)
  142. {
  143.         nserror err;
  144.         dom_exception exc; /* returned by libdom functions */
  145.         dom_node *html;
  146.  
  147.         LOG(("Done XML to box (%p)", c));
  148.  
  149.         /* Clean up and report error if unsuccessful or aborted */
  150.         if ((success == false) || (c->aborted)) {
  151.                 html_destroy_objects(c);
  152.  
  153.                 if (success == false) {
  154.                         content_broadcast_errorcode(&c->base, NSERROR_BOX_CONVERT);
  155.                 } else {
  156.                         content_broadcast_errorcode(&c->base, NSERROR_STOPPED);
  157.                 }
  158.  
  159.                 content_set_error(&c->base);
  160.                 return;
  161.         }
  162.  
  163.  
  164. #if ALWAYS_DUMP_BOX
  165.         box_dump(stderr, c->layout->children, 0);
  166. #endif
  167. #if ALWAYS_DUMP_FRAMESET
  168.         if (c->frameset)
  169.                 html_dump_frameset(c->frameset, 0);
  170. #endif
  171.  
  172.         exc = dom_document_get_document_element(c->document, (void *) &html);
  173.         if ((exc != DOM_NO_ERR) || (html == NULL)) {
  174.                 /** @todo should this call html_destroy_objects(c);
  175.                  * like the other error paths
  176.                  */
  177.                 LOG(("error retrieving html element from dom"));
  178.                 content_broadcast_errorcode(&c->base, NSERROR_DOM);
  179.                 content_set_error(&c->base);
  180.                 return;
  181.         }
  182.  
  183.         /* extract image maps - can't do this sensibly in dom_to_box */
  184.         err = imagemap_extract(c);
  185.         if (err != NSERROR_OK) {
  186.                 LOG(("imagemap extraction failed"));
  187.                 html_destroy_objects(c);
  188.                 content_broadcast_errorcode(&c->base, err);
  189.                 content_set_error(&c->base);
  190.                 dom_node_unref(html);
  191.                 return;
  192.         }
  193.         /*imagemap_dump(c);*/
  194.  
  195.         /* Destroy the parser binding */
  196.         dom_hubbub_parser_destroy(c->parser);
  197.         c->parser = NULL;
  198.  
  199.         content_set_ready(&c->base);
  200.  
  201.         if (c->base.active == 0) {
  202.                 content_set_done(&c->base);
  203.         }
  204.  
  205.         html_set_status(c, "");
  206.         dom_node_unref(html);
  207. }
  208.  
  209.  
  210. /**
  211.  * Complete conversion of an HTML document
  212.  *
  213.  * \param c  Content to convert
  214.  */
  215. void html_finish_conversion(html_content *c)
  216. {
  217.         union content_msg_data msg_data;
  218.         dom_exception exc; /* returned by libdom functions */
  219.         dom_node *html;
  220.         uint32_t i;
  221.         css_error css_ret;
  222.         nserror error;
  223.  
  224.         /* Bail out if we've been aborted */
  225.         if (c->aborted) {
  226.                 content_broadcast_errorcode(&c->base, NSERROR_STOPPED);
  227.                 content_set_error(&c->base);
  228.                 return;
  229.         }
  230.  
  231.         /* check that the base stylesheet loaded; layout fails without it */
  232.         if (c->stylesheets[STYLESHEET_BASE].data.external == NULL) {
  233.                 content_broadcast_errorcode(&c->base, NSERROR_CSS_BASE);
  234.                 content_set_error(&c->base);
  235.                 return;
  236.         }
  237.  
  238.         /* Create selection context */
  239.         css_ret = css_select_ctx_create(ns_realloc, c, &c->select_ctx);
  240.         if (css_ret != CSS_OK) {
  241.                 content_broadcast_errorcode(&c->base,
  242.                                             css_error_to_nserror(css_ret));
  243.                 content_set_error(&c->base);
  244.                 return;
  245.         }
  246.  
  247.         /* Add sheets to it */
  248.         for (i = STYLESHEET_BASE; i != c->stylesheet_count; i++) {
  249.                 const struct html_stylesheet *hsheet = &c->stylesheets[i];
  250.                 css_stylesheet *sheet;
  251.                 css_origin origin = CSS_ORIGIN_AUTHOR;
  252.  
  253.                 if (i < STYLESHEET_USER)
  254.                         origin = CSS_ORIGIN_UA;
  255.                 else if (i < STYLESHEET_START)
  256.                         origin = CSS_ORIGIN_USER;
  257.  
  258.                 if (hsheet->type == HTML_STYLESHEET_EXTERNAL &&
  259.                                 hsheet->data.external != NULL) {
  260.                         sheet = nscss_get_stylesheet(hsheet->data.external);
  261.                 } else if (hsheet->type == HTML_STYLESHEET_INTERNAL) {
  262.                         sheet = hsheet->data.internal->sheet;
  263.                 } else {
  264.                         sheet = NULL;
  265.                 }
  266.  
  267.                 if (sheet != NULL) {
  268.                         css_ret = css_select_ctx_append_sheet(c->select_ctx,
  269.                                                               sheet,
  270.                                                               origin,
  271.                                                               CSS_MEDIA_SCREEN);
  272.                         if (css_ret != CSS_OK) {
  273.                                 content_broadcast_errorcode(&c->base,
  274.                                                 css_error_to_nserror(css_ret));
  275.                                 content_set_error(&c->base);
  276.                                 return;
  277.                         }
  278.                 }
  279.         }
  280.  
  281.         /* fire a simple event named load at the Document's Window
  282.          * object, but with its target set to the Document object (and
  283.          * the currentTarget set to the Window object)
  284.          */
  285.         js_fire_event(c->jscontext, "load", c->document, NULL);
  286.  
  287.         /* convert dom tree to box tree */
  288.         LOG(("DOM to box (%p)", c));
  289.         content_set_status(&c->base, messages_get("Processing"));
  290.         /* LOG(("After content_set_status")); */
  291.         msg_data.explicit_status_text = NULL;
  292.         content_broadcast(&c->base, CONTENT_MSG_STATUS, msg_data);
  293.         /* LOG(("After content_broadcast")); */
  294.  
  295.         exc = dom_document_get_document_element(c->document, (void *) &html);
  296.         /* LOG(("After get_document_element")); */
  297.  
  298.         if ((exc != DOM_NO_ERR) || (html == NULL)) {
  299.                 LOG(("error retrieving html element from dom"));
  300.                 content_broadcast_errorcode(&c->base, NSERROR_DOM);
  301.                 content_set_error(&c->base);
  302.                 return;
  303.         }
  304.        
  305.         error = dom_to_box(html, c, html_box_convert_done);
  306.         /* LOG(("After dom_to_box")); */
  307.  
  308.         if (error != NSERROR_OK) {
  309.                 dom_node_unref(html);
  310.                 html_destroy_objects(c);
  311.                 content_broadcast_errorcode(&c->base, error);
  312.                 content_set_error(&c->base);
  313.                 return;
  314.         }
  315.  
  316.         dom_node_unref(html);
  317. }
  318.  
  319.  
  320.  
  321. static nserror
  322. html_create_html_data(html_content *c, const http_parameter *params)
  323. {
  324.         lwc_string *charset;
  325.         nserror nerror;
  326.         dom_hubbub_parser_params parse_params;
  327.         dom_hubbub_error error;
  328.  
  329.         c->parser = NULL;
  330.         c->document = NULL;
  331.         c->quirks = DOM_DOCUMENT_QUIRKS_MODE_NONE;
  332.         c->encoding = NULL;
  333.         c->base_url = nsurl_ref(content_get_url(&c->base));
  334.         c->base_target = NULL;
  335.         c->aborted = false;
  336.         c->bctx = NULL;
  337.         c->layout = NULL;
  338.         c->background_colour = NS_TRANSPARENT;
  339.         c->stylesheet_count = 0;
  340.         c->stylesheets = NULL;
  341.         c->select_ctx = NULL;
  342.         c->universal = NULL;
  343.         c->num_objects = 0;
  344.         c->object_list = NULL;
  345.         c->forms = NULL;
  346.         c->imagemaps = NULL;
  347.         c->bw = NULL;
  348.         c->frameset = NULL;
  349.         c->iframe = NULL;
  350.         c->page = NULL;
  351.         c->font_func = &nsfont;
  352.         c->scrollbar = NULL;
  353.         c->scripts_count = 0;
  354.         c->scripts = NULL;
  355.         c->jscontext = NULL;
  356.  
  357.         c->base.active = 1; /* The html content itself is active */
  358.  
  359.         if (lwc_intern_string("*", SLEN("*"), &c->universal) != lwc_error_ok) {
  360.                 return NSERROR_NOMEM;
  361.         }
  362.  
  363.         selection_prepare(&c->sel, (struct content *)c, true);
  364.  
  365.         nerror = http_parameter_list_find_item(params, html_charset, &charset);
  366.         if (nerror == NSERROR_OK) {
  367.                 c->encoding = strdup(lwc_string_data(charset));
  368.  
  369.                 lwc_string_unref(charset);
  370.  
  371.                 if (c->encoding == NULL) {
  372.                         lwc_string_unref(c->universal);
  373.                         c->universal = NULL;
  374.                         return NSERROR_NOMEM;
  375.  
  376.                 }
  377.                 c->encoding_source = DOM_HUBBUB_ENCODING_SOURCE_HEADER;
  378.         }
  379.  
  380.         /* Create the parser binding */
  381.         parse_params.enc = c->encoding;
  382.         parse_params.fix_enc = true;
  383.         parse_params.enable_script = nsoption_bool(enable_javascript);
  384.         parse_params.msg = NULL;
  385.         parse_params.script = html_process_script;
  386.         parse_params.ctx = c;
  387.         parse_params.daf = NULL;
  388.  
  389.         error = dom_hubbub_parser_create(&parse_params,
  390.                                          &c->parser,
  391.                                          &c->document);
  392.         if ((error != DOM_HUBBUB_OK) && (c->encoding != NULL)) {
  393.                 /* Ok, we don't support the declared encoding. Bailing out
  394.                  * isn't exactly user-friendly, so fall back to autodetect */
  395.                 free(c->encoding);
  396.                 c->encoding = NULL;
  397.  
  398.                 parse_params.enc = c->encoding;
  399.  
  400.                 error = dom_hubbub_parser_create(&parse_params,
  401.                                                  &c->parser,
  402.                                                  &c->document);
  403.         }
  404.  
  405.         if (error != DOM_HUBBUB_OK) {
  406.                 nsurl_unref(c->base_url);
  407.                 c->base_url = NULL;
  408.  
  409.                 lwc_string_unref(c->universal);
  410.                 c->universal = NULL;
  411.  
  412.                 return libdom_hubbub_error_to_nserror(error);
  413.         }
  414.  
  415.         return NSERROR_OK;
  416.  
  417. }
  418.  
  419. /**
  420.  * Create a CONTENT_HTML.
  421.  *
  422.  * The content_html_data structure is initialized and the HTML parser is
  423.  * created.
  424.  */
  425.  
  426. static nserror
  427. html_create(const content_handler *handler,
  428.             lwc_string *imime_type,
  429.             const http_parameter *params,
  430.             llcache_handle *llcache,
  431.             const char *fallback_charset,
  432.             bool quirks,
  433.             struct content **c)
  434. {
  435.         html_content *html;
  436.         nserror error;
  437.  
  438.         html = calloc(1, sizeof(html_content));
  439.         if (html == NULL)
  440.                 return NSERROR_NOMEM;
  441.  
  442.         error = content__init(&html->base, handler, imime_type, params,
  443.                         llcache, fallback_charset, quirks);
  444.         if (error != NSERROR_OK) {
  445.                 free(html);
  446.                 return error;
  447.         }
  448.  
  449.         error = html_create_html_data(html, params);
  450.         if (error != NSERROR_OK) {
  451.                 content_broadcast_errorcode(&html->base, error);
  452.                 free(html);
  453.                 return error;
  454.         }
  455.  
  456.         *c = (struct content *) html;
  457.  
  458.         return NSERROR_OK;
  459. }
  460.  
  461.  
  462.  
  463. static nserror
  464. html_process_encoding_change(struct content *c,
  465.                              const char *data,
  466.                              unsigned int size)
  467. {
  468.         html_content *html = (html_content *) c;
  469.         dom_hubbub_parser_params parse_params;
  470.         dom_hubbub_error error;
  471.         const char *encoding;
  472.         const char *source_data;
  473.         unsigned long source_size;
  474.  
  475.         /* Retrieve new encoding */
  476.         encoding = dom_hubbub_parser_get_encoding(html->parser,
  477.                                                   &html->encoding_source);
  478.         if (encoding == NULL) {
  479.                 return NSERROR_NOMEM;
  480.         }
  481.  
  482.         if (html->encoding != NULL) {
  483.                 free(html->encoding);
  484.         }
  485.  
  486.         html->encoding = strdup(encoding);
  487.         if (html->encoding == NULL) {
  488.                 return NSERROR_NOMEM;
  489.         }
  490.  
  491.         /* Destroy binding */
  492.         dom_hubbub_parser_destroy(html->parser);
  493.         html->parser = NULL;
  494.  
  495.         if (html->document != NULL) {
  496.                 dom_node_unref(html->document);
  497.         }
  498.  
  499.         parse_params.enc = html->encoding;
  500.         parse_params.fix_enc = true;
  501.         parse_params.enable_script = nsoption_bool(enable_javascript);
  502.         parse_params.msg = NULL;
  503.         parse_params.script = html_process_script;
  504.         parse_params.ctx = html;
  505.         parse_params.daf = NULL;
  506.  
  507.         /* Create new binding, using the new encoding */
  508.         error = dom_hubbub_parser_create(&parse_params,
  509.                                          &html->parser,
  510.                                          &html->document);
  511.         if (error != DOM_HUBBUB_OK) {
  512.                 /* Ok, we don't support the declared encoding. Bailing out
  513.                  * isn't exactly user-friendly, so fall back to Windows-1252 */
  514.                 free(html->encoding);
  515.                 html->encoding = strdup("Windows-1252");
  516.                 if (html->encoding == NULL) {
  517.                         return NSERROR_NOMEM;
  518.                 }
  519.                 parse_params.enc = html->encoding;
  520.  
  521.                 error = dom_hubbub_parser_create(&parse_params,
  522.                                                  &html->parser,
  523.                                                  &html->document);
  524.  
  525.                 if (error != DOM_HUBBUB_OK) {
  526.                         return libdom_hubbub_error_to_nserror(error);
  527.                 }
  528.  
  529.         }
  530.  
  531.         source_data = content__get_source_data(c, &source_size);
  532.  
  533.         /* Reprocess all the data.  This is safe because
  534.          * the encoding is now specified at parser start which means
  535.          * it cannot be changed again.
  536.          */
  537.         error = dom_hubbub_parser_parse_chunk(html->parser,
  538.                                               (const uint8_t *)source_data,
  539.                                               source_size);
  540.  
  541.         return libdom_hubbub_error_to_nserror(error);
  542. }
  543.  
  544.  
  545. /**
  546.  * Process data for CONTENT_HTML.
  547.  */
  548.  
  549. static bool
  550. html_process_data(struct content *c, const char *data, unsigned int size)
  551. {
  552.         html_content *html = (html_content *) c;
  553.         dom_hubbub_error dom_ret;
  554.         nserror err = NSERROR_OK; /* assume its all going to be ok */
  555.  
  556.         dom_ret = dom_hubbub_parser_parse_chunk(html->parser,
  557.                                               (const uint8_t *) data,
  558.                                               size);
  559.  
  560.         err = libdom_hubbub_error_to_nserror(dom_ret);
  561.  
  562.         /* deal with encoding change */
  563.         if (err == NSERROR_ENCODING_CHANGE) {
  564.                  err = html_process_encoding_change(c, data, size);
  565.         }
  566.  
  567.         /* broadcast the error if necessary */
  568.         if (err != NSERROR_OK) {
  569.                 content_broadcast_errorcode(c, err);
  570.                 return false;
  571.         }
  572.  
  573.         return true;   
  574. }
  575.  
  576.  
  577. /** process link node */
  578. static bool html_process_link(html_content *c, dom_node *node)
  579. {
  580.         struct content_rfc5988_link link; /* the link added to the content */
  581.         dom_exception exc; /* returned by libdom functions */
  582.         dom_string *atr_string;
  583.         nserror error;
  584.  
  585.         memset(&link, 0, sizeof(struct content_rfc5988_link));
  586.  
  587.         /* check that the relation exists - w3c spec says must be present */
  588.         exc = dom_element_get_attribute(node, corestring_dom_rel, &atr_string);
  589.         if ((exc != DOM_NO_ERR) || (atr_string == NULL)) {
  590.                 return false;
  591.         }
  592.         /* get a lwc string containing the link relation */
  593.         exc = dom_string_intern(atr_string, &link.rel);
  594.         dom_string_unref(atr_string);
  595.         if (exc != DOM_NO_ERR) {
  596.                 return false;
  597.         }
  598.  
  599.         /* check that the href exists - w3c spec says must be present */
  600.         exc = dom_element_get_attribute(node, corestring_dom_href, &atr_string);
  601.         if ((exc != DOM_NO_ERR) || (atr_string == NULL)) {
  602.                 lwc_string_unref(link.rel);
  603.                 return false;
  604.         }
  605.  
  606.         /* get nsurl */
  607.         error = nsurl_join(c->base_url, dom_string_data(atr_string),
  608.                         &link.href);
  609.         dom_string_unref(atr_string);
  610.         if (error != NSERROR_OK) {
  611.                 lwc_string_unref(link.rel);
  612.                 return false;
  613.         }
  614.  
  615.         /* look for optional properties -- we don't care if internment fails */
  616.  
  617.         exc = dom_element_get_attribute(node,
  618.                         corestring_dom_hreflang, &atr_string);
  619.         if ((exc == DOM_NO_ERR) && (atr_string != NULL)) {
  620.                 /* get a lwc string containing the href lang */
  621.                 exc = dom_string_intern(atr_string, &link.hreflang);
  622.                 dom_string_unref(atr_string);
  623.         }
  624.  
  625.         exc = dom_element_get_attribute(node,
  626.                         corestring_dom_type, &atr_string);
  627.         if ((exc == DOM_NO_ERR) && (atr_string != NULL)) {
  628.                 /* get a lwc string containing the type */
  629.                 exc = dom_string_intern(atr_string, &link.type);
  630.                 dom_string_unref(atr_string);
  631.         }
  632.  
  633.         exc = dom_element_get_attribute(node,
  634.                         corestring_dom_media, &atr_string);
  635.         if ((exc == DOM_NO_ERR) && (atr_string != NULL)) {
  636.                 /* get a lwc string containing the media */
  637.                 exc = dom_string_intern(atr_string, &link.media);
  638.                 dom_string_unref(atr_string);
  639.         }
  640.  
  641.         exc = dom_element_get_attribute(node,
  642.                         corestring_dom_sizes, &atr_string);
  643.         if ((exc == DOM_NO_ERR) && (atr_string != NULL)) {
  644.                 /* get a lwc string containing the sizes */
  645.                 exc = dom_string_intern(atr_string, &link.sizes);
  646.                 dom_string_unref(atr_string);
  647.         }
  648.  
  649.         /* add to content */
  650.         content__add_rfc5988_link(&c->base, &link);
  651.  
  652.         if (link.sizes != NULL)
  653.                 lwc_string_unref(link.sizes);
  654.         if (link.media != NULL)
  655.                 lwc_string_unref(link.media);
  656.         if (link.type != NULL)
  657.                 lwc_string_unref(link.type);
  658.         if (link.hreflang != NULL)
  659.                 lwc_string_unref(link.hreflang);
  660.  
  661.         nsurl_unref(link.href);
  662.         lwc_string_unref(link.rel);
  663.  
  664.         return true;
  665. }
  666.  
  667. /** process title node */
  668. static bool html_process_title(html_content *c, dom_node *node)
  669. {
  670.         dom_exception exc; /* returned by libdom functions */
  671.         dom_string *title;
  672.         char *title_str;
  673.         bool success;
  674.  
  675.         if (c->base.title != NULL)
  676.                 return true;
  677.  
  678.         exc = dom_node_get_text_content(node, &title);
  679.         if ((exc != DOM_NO_ERR) || (title == NULL)) {
  680.                 return false;
  681.         }
  682.  
  683.         title_str = squash_whitespace(dom_string_data(title));
  684.         dom_string_unref(title);
  685.  
  686.         if (title_str == NULL) {
  687.                 return false;
  688.         }
  689.  
  690.         success = content__set_title(&c->base, title_str);
  691.  
  692.         free(title_str);
  693.  
  694.         return success;
  695. }
  696.  
  697. static bool html_process_base(html_content *c, dom_node *node)
  698. {
  699.         dom_exception exc; /* returned by libdom functions */
  700.         dom_string *atr_string;
  701.  
  702.         /* get href attribute if present */
  703.         exc = dom_element_get_attribute(node,
  704.                         corestring_dom_href, &atr_string);
  705.         if ((exc == DOM_NO_ERR) && (atr_string != NULL)) {
  706.                 nsurl *url;
  707.                 nserror error;
  708.  
  709.                 /* get url from string */
  710.                 error = nsurl_create(dom_string_data(atr_string), &url);
  711.                 dom_string_unref(atr_string);
  712.                 if (error == NSERROR_OK) {
  713.                         if (c->base_url != NULL)
  714.                                 nsurl_unref(c->base_url);
  715.                         c->base_url = url;
  716.                 }
  717.         }
  718.  
  719.  
  720.         /* get target attribute if present and not already set */
  721.         if (c->base_target != NULL) {
  722.                 return true;
  723.         }
  724.  
  725.         exc = dom_element_get_attribute(node,
  726.                         corestring_dom_target, &atr_string);
  727.         if ((exc == DOM_NO_ERR) && (atr_string != NULL)) {
  728.                 /* Validation rules from the HTML5 spec for the base element:
  729.                  *  The target must be one of _blank, _self, _parent, or
  730.                  *  _top or any identifier which does not begin with an
  731.                  *  underscore
  732.                  */
  733.                 if (*dom_string_data(atr_string) != '_' ||
  734.                     dom_string_caseless_lwc_isequal(atr_string,
  735.                                 corestring_lwc__blank) ||
  736.                     dom_string_caseless_lwc_isequal(atr_string,
  737.                                 corestring_lwc__self) ||
  738.                     dom_string_caseless_lwc_isequal(atr_string,
  739.                                 corestring_lwc__parent) ||
  740.                     dom_string_caseless_lwc_isequal(atr_string,
  741.                                 corestring_lwc__top)) {
  742.                         c->base_target = strdup(dom_string_data(atr_string));
  743.                 }
  744.                 dom_string_unref(atr_string);
  745.         }
  746.  
  747.         return true;
  748. }
  749.  
  750. /**
  751.  * Process elements in <head>.
  752.  *
  753.  * \param  c     content structure
  754.  * \param  head  xml node of head element
  755.  * \return  true on success, false on memory exhaustion
  756.  *
  757.  * The title and base href are extracted if present.
  758.  */
  759.  
  760. static nserror html_head(html_content *c, dom_node *head)
  761. {
  762.         dom_node *node;
  763.         dom_exception exc; /* returned by libdom functions */
  764.         dom_string *node_name;
  765.         dom_node_type node_type;
  766.         dom_node *next_node;
  767.  
  768.         exc = dom_node_get_first_child(head, &node);
  769.         if (exc != DOM_NO_ERR) {
  770.                 return NSERROR_DOM;
  771.         }
  772.  
  773.         while (node != NULL) {
  774.                 exc = dom_node_get_node_type(node, &node_type);
  775.  
  776.                 if ((exc == DOM_NO_ERR) && (node_type == DOM_ELEMENT_NODE)) {
  777.                         exc = dom_node_get_node_name(node, &node_name);
  778.  
  779.                         if ((exc == DOM_NO_ERR) && (node_name != NULL)) {
  780.                                 if (dom_string_caseless_lwc_isequal(
  781.                                                 node_name,
  782.                                                 corestring_lwc_title)) {
  783.                                         html_process_title(c, node);
  784.                                 } else if (dom_string_caseless_lwc_isequal(
  785.                                                 node_name,
  786.                                                 corestring_lwc_base)) {
  787.                                         html_process_base(c, node);
  788.                                 } else if (dom_string_caseless_lwc_isequal(
  789.                                                 node_name,
  790.                                                 corestring_lwc_link)) {
  791.                                         html_process_link(c, node);
  792.                                 }
  793.                         }
  794.                         if (node_name != NULL) {
  795.                                 dom_string_unref(node_name);
  796.                         }
  797.                 }
  798.  
  799.                 /* move to next node */
  800.                 exc = dom_node_get_next_sibling(node, &next_node);
  801.                 dom_node_unref(node);
  802.                 if (exc == DOM_NO_ERR) {
  803.                         node = next_node;
  804.                 } else {
  805.                         node = NULL;
  806.                 }
  807.         }
  808.  
  809.         return NSERROR_OK;
  810. }
  811.  
  812. static nserror html_meta_refresh_process_element(html_content *c, dom_node *n)
  813. {
  814.         union content_msg_data msg_data;
  815.         const char *url, *end, *refresh = NULL;
  816.         char *new_url;
  817.         char quote = '\0';
  818.         dom_string *equiv, *content;
  819.         dom_exception exc;
  820.         nsurl *nsurl;
  821.         nserror error = NSERROR_OK;
  822.  
  823.         exc = dom_element_get_attribute(n, corestring_dom_http_equiv, &equiv);
  824.         if (exc != DOM_NO_ERR) {
  825.                 return NSERROR_DOM;
  826.         }
  827.  
  828.         if (equiv == NULL) {
  829.                 return NSERROR_OK;
  830.         }
  831.  
  832.         if (!dom_string_caseless_lwc_isequal(equiv, corestring_lwc_refresh)) {
  833.                 dom_string_unref(equiv);
  834.                 return NSERROR_OK;
  835.         }
  836.  
  837.         dom_string_unref(equiv);
  838.  
  839.         exc = dom_element_get_attribute(n, corestring_dom_content, &content);
  840.         if (exc != DOM_NO_ERR) {
  841.                 return NSERROR_DOM;
  842.         }
  843.  
  844.         if (content == NULL) {
  845.                 return NSERROR_OK;
  846.         }
  847.  
  848.         end = dom_string_data(content) + dom_string_byte_length(content);
  849.  
  850.         /* content  := *LWS intpart fracpart? *LWS [';' *LWS *1url *LWS]
  851.          * intpart  := 1*DIGIT
  852.          * fracpart := 1*('.' | DIGIT)
  853.          * url      := "url" *LWS '=' *LWS (url-nq | url-sq | url-dq)
  854.          * url-nq   := *urlchar
  855.          * url-sq   := "'" *(urlchar | '"') "'"
  856.          * url-dq   := '"' *(urlchar | "'") '"'
  857.          * urlchar  := [#x9#x21#x23-#x26#x28-#x7E] | nonascii
  858.          * nonascii := [#x80-#xD7FF#xE000-#xFFFD#x10000-#x10FFFF]
  859.          */
  860.  
  861.         url = dom_string_data(content);
  862.  
  863.         /* *LWS */
  864.         while (url < end && isspace(*url)) {
  865.                 url++;
  866.         }
  867.  
  868.         /* intpart */
  869.         if (url == end || (*url < '0' || '9' < *url)) {
  870.                 /* Empty content, or invalid timeval */
  871.                 dom_string_unref(content);
  872.                 return NSERROR_OK;
  873.         }
  874.  
  875.         msg_data.delay = (int) strtol(url, &new_url, 10);
  876.         /* a very small delay and self-referencing URL can cause a loop
  877.          * that grinds machines to a halt. To prevent this we set a
  878.          * minimum refresh delay of 1s. */
  879.         if (msg_data.delay < 1) {
  880.                 msg_data.delay = 1;
  881.         }
  882.  
  883.         url = new_url;
  884.  
  885.         /* fracpart? (ignored, as delay is integer only) */
  886.         while (url < end && (('0' <= *url && *url <= '9') ||
  887.                         *url == '.')) {
  888.                 url++;
  889.         }
  890.  
  891.         /* *LWS */
  892.         while (url < end && isspace(*url)) {
  893.                 url++;
  894.         }
  895.  
  896.         /* ';' */
  897.         if (url < end && *url == ';')
  898.                 url++;
  899.  
  900.         /* *LWS */
  901.         while (url < end && isspace(*url)) {
  902.                 url++;
  903.         }
  904.  
  905.         if (url == end) {
  906.                 /* Just delay specified, so refresh current page */
  907.                 dom_string_unref(content);
  908.  
  909.                 c->base.refresh = nsurl_ref(
  910.                                 content_get_url(&c->base));
  911.  
  912.                 content_broadcast(&c->base, CONTENT_MSG_REFRESH, msg_data);
  913.  
  914.                 return NSERROR_OK;
  915.         }
  916.  
  917.         /* "url" */
  918.         if (url <= end - 3) {
  919.                 if (strncasecmp(url, "url", 3) == 0) {
  920.                         url += 3;
  921.                 } else {
  922.                         /* Unexpected input, ignore this header */
  923.                         dom_string_unref(content);
  924.                         return NSERROR_OK;
  925.                 }
  926.         } else {
  927.                 /* Insufficient input, ignore this header */
  928.                 dom_string_unref(content);
  929.                 return NSERROR_OK;
  930.         }
  931.  
  932.         /* *LWS */
  933.         while (url < end && isspace(*url)) {
  934.                 url++;
  935.         }
  936.  
  937.         /* '=' */
  938.         if (url < end) {
  939.                 if (*url == '=') {
  940.                         url++;
  941.                 } else {
  942.                         /* Unexpected input, ignore this header */
  943.                         dom_string_unref(content);
  944.                         return NSERROR_OK;
  945.                 }
  946.         } else {
  947.                 /* Insufficient input, ignore this header */
  948.                 dom_string_unref(content);
  949.                 return NSERROR_OK;
  950.         }
  951.  
  952.         /* *LWS */
  953.         while (url < end && isspace(*url)) {
  954.                 url++;
  955.         }
  956.  
  957.         /* '"' or "'" */
  958.         if (url < end && (*url == '"' || *url == '\'')) {
  959.                 quote = *url;
  960.                 url++;
  961.         }
  962.  
  963.         /* Start of URL */
  964.         refresh = url;
  965.  
  966.         if (quote != 0) {
  967.                 /* url-sq | url-dq */
  968.                 while (url < end && *url != quote)
  969.                         url++;
  970.         } else {
  971.                 /* url-nq */
  972.                 while (url < end && !isspace(*url))
  973.                         url++;
  974.         }
  975.  
  976.         /* '"' or "'" or *LWS (we don't care) */
  977.         if (url > refresh) {
  978.                 /* There's a URL */
  979.                 new_url = strndup(refresh, url - refresh);
  980.                 if (new_url == NULL) {
  981.                         dom_string_unref(content);
  982.                         return NSERROR_NOMEM;
  983.                 }
  984.  
  985.                 error = nsurl_join(c->base_url, new_url, &nsurl);
  986.                 if (error == NSERROR_OK) {
  987.                         /* broadcast valid refresh url */
  988.  
  989.                         c->base.refresh = nsurl;
  990.  
  991.                         content_broadcast(&c->base, CONTENT_MSG_REFRESH, msg_data);
  992.                 }
  993.  
  994.                 free(new_url);
  995.  
  996.         }
  997.  
  998.         dom_string_unref(content);
  999.  
  1000.         return error;
  1001. }
  1002.  
  1003. /**
  1004.  * Search for meta refresh
  1005.  *
  1006.  * http://wp.netscape.com/assist/net_sites/pushpull.html
  1007.  *
  1008.  * \param c content structure
  1009.  * \param head xml node of head element
  1010.  * \return true on success, false otherwise (error reported)
  1011.  */
  1012.  
  1013. static nserror html_meta_refresh(html_content *c, dom_node *head)
  1014. {
  1015.         dom_node *n, *next;
  1016.         dom_exception exc;
  1017.         nserror ns_error = NSERROR_OK;
  1018.  
  1019.         if (head == NULL) {
  1020.                 return ns_error;
  1021.         }
  1022.  
  1023.         exc = dom_node_get_first_child(head, &n);
  1024.         if (exc != DOM_NO_ERR) {
  1025.                 return NSERROR_DOM;
  1026.         }
  1027.  
  1028.         while (n != NULL) {
  1029.                 dom_node_type type;
  1030.  
  1031.                 exc = dom_node_get_node_type(n, &type);
  1032.                 if (exc != DOM_NO_ERR) {
  1033.                         dom_node_unref(n);
  1034.                         return NSERROR_DOM;
  1035.                 }
  1036.  
  1037.                 if (type == DOM_ELEMENT_NODE) {
  1038.                         dom_string *name;
  1039.  
  1040.                         exc = dom_node_get_node_name(n, &name);
  1041.                         if (exc != DOM_NO_ERR) {
  1042.                                 dom_node_unref(n);
  1043.                                 return NSERROR_DOM;
  1044.                         }
  1045.  
  1046.                         /* Recurse into noscript elements */
  1047.                         if (dom_string_caseless_lwc_isequal(name, corestring_lwc_noscript)) {
  1048.                                 ns_error = html_meta_refresh(c, n);
  1049.                                 if (ns_error != NSERROR_OK) {
  1050.                                         /* Some error occurred */
  1051.                                         dom_string_unref(name);
  1052.                                         dom_node_unref(n);
  1053.                                         return ns_error;
  1054.                                 } else if (c->base.refresh != NULL) {
  1055.                                         /* Meta refresh found - stop */
  1056.                                         dom_string_unref(name);
  1057.                                         dom_node_unref(n);
  1058.                                         return NSERROR_OK;
  1059.                                 }
  1060.                         } else if (dom_string_caseless_lwc_isequal(name, corestring_lwc_meta)) {
  1061.                                 ns_error = html_meta_refresh_process_element(c, n);
  1062.                                 if (ns_error != NSERROR_OK) {
  1063.                                         /* Some error occurred */
  1064.                                         dom_string_unref(name);
  1065.                                         dom_node_unref(n);
  1066.                                         return ns_error;
  1067.                                 } else if (c->base.refresh != NULL) {
  1068.                                         /* Meta refresh found - stop */
  1069.                                         dom_string_unref(name);
  1070.                                         dom_node_unref(n);
  1071.                                         return NSERROR_OK;
  1072.                                 }
  1073.                         }
  1074.                         dom_string_unref(name);
  1075.                 }
  1076.  
  1077.                 exc = dom_node_get_next_sibling(n, &next);
  1078.                 if (exc != DOM_NO_ERR) {
  1079.                         dom_node_unref(n);
  1080.                         return NSERROR_DOM;
  1081.                 }
  1082.  
  1083.                 dom_node_unref(n);
  1084.                 n = next;
  1085.         }
  1086.  
  1087.         return ns_error;
  1088. }
  1089.  
  1090. /**
  1091.  * Update a box whose content has completed rendering.
  1092.  */
  1093.  
  1094. static void
  1095. html_object_done(struct box *box,
  1096.                  hlcache_handle *object,
  1097.                  bool background)
  1098. {
  1099.         struct box *b;
  1100.  
  1101.         if (background) {
  1102.                 box->background = object;
  1103.                 return;
  1104.         }
  1105.  
  1106.         box->object = object;
  1107.  
  1108.         if (!(box->flags & REPLACE_DIM)) {
  1109.                 /* invalidate parent min, max widths */
  1110.                 for (b = box; b; b = b->parent)
  1111.                         b->max_width = UNKNOWN_MAX_WIDTH;
  1112.  
  1113.                 /* delete any clones of this box */
  1114.                 while (box->next && (box->next->flags & CLONE)) {
  1115.                         /* box_free_box(box->next); */
  1116.                         box->next = box->next->next;
  1117.                 }
  1118.         }
  1119. }
  1120.  
  1121. /**
  1122.  * Handle object fetching or loading failure.
  1123.  *
  1124.  * \param  box         box containing object which failed to load
  1125.  * \param  content     document of type CONTENT_HTML
  1126.  * \param  background  the object was the background image for the box
  1127.  */
  1128.  
  1129. static void
  1130. html_object_failed(struct box *box, html_content *content, bool background)
  1131. {
  1132.         /* Nothing to do */
  1133.         return;
  1134. }
  1135.  
  1136. /**
  1137.  * Callback for hlcache_handle_retrieve() for objects.
  1138.  */
  1139.  
  1140. static nserror
  1141. html_object_callback(hlcache_handle *object,
  1142.                      const hlcache_event *event,
  1143.                      void *pw)
  1144. {
  1145.         struct content_html_object *o = pw;
  1146.         html_content *c = (html_content *) o->parent;
  1147.         int x, y;
  1148.         struct box *box;
  1149.  
  1150.         assert(c->base.status != CONTENT_STATUS_ERROR);
  1151.  
  1152.         box = o->box;
  1153.  
  1154.         switch (event->type) {
  1155.         case CONTENT_MSG_LOADING:
  1156.                 if (c->base.status != CONTENT_STATUS_LOADING && c->bw != NULL)
  1157.                         content_open(object,
  1158.                                         c->bw, &c->base,
  1159.                                         box->object_params);
  1160.                 break;
  1161.  
  1162.         case CONTENT_MSG_READY:
  1163.                 if (content_can_reformat(object)) {
  1164.                         /* TODO: avoid knowledge of box internals here */
  1165.                         content_reformat(object, false,
  1166.                                         box->max_width != UNKNOWN_MAX_WIDTH ?
  1167.                                                         box->width : 0,
  1168.                                         box->max_width != UNKNOWN_MAX_WIDTH ?
  1169.                                                         box->height : 0);
  1170.  
  1171.                         /* Adjust parent content for new object size */
  1172.                         html_object_done(box, object, o->background);
  1173.                         if (c->base.status == CONTENT_STATUS_READY ||
  1174.                                         c->base.status == CONTENT_STATUS_DONE)
  1175.                                 content__reformat(&c->base, false,
  1176.                                                 c->base.available_width,
  1177.                                                 c->base.height);
  1178.                 }
  1179.                 break;
  1180.  
  1181.         case CONTENT_MSG_DONE:
  1182.                 c->base.active--;
  1183.                 LOG(("%d fetches active", c->base.active));
  1184.  
  1185.                 html_object_done(box, object, o->background);
  1186.  
  1187.                 if (c->base.status != CONTENT_STATUS_LOADING &&
  1188.                                 box->flags & REPLACE_DIM) {
  1189.                         union content_msg_data data;
  1190.  
  1191.                         if (!box_visible(box))
  1192.                                 break;
  1193.  
  1194.                         box_coords(box, &x, &y);
  1195.  
  1196.                         data.redraw.x = x + box->padding[LEFT];
  1197.                         data.redraw.y = y + box->padding[TOP];
  1198.                         data.redraw.width = box->width;
  1199.                         data.redraw.height = box->height;
  1200.                         data.redraw.full_redraw = true;
  1201.  
  1202.                         content_broadcast(&c->base, CONTENT_MSG_REDRAW, data);
  1203.                 }
  1204.                 break;
  1205.  
  1206.         case CONTENT_MSG_ERROR:
  1207.                 hlcache_handle_release(object);
  1208.  
  1209.                 o->content = NULL;
  1210.  
  1211.                 c->base.active--;
  1212.                 LOG(("%d fetches active", c->base.active));
  1213.  
  1214.                 content_add_error(&c->base, "?", 0);
  1215.                 html_object_failed(box, c, o->background);
  1216.                 break;
  1217.  
  1218.         case CONTENT_MSG_STATUS:
  1219.                 if (event->data.explicit_status_text == NULL) {
  1220.                         /* Object content's status text updated */
  1221.                         union content_msg_data data;
  1222.                         data.explicit_status_text =
  1223.                                         content_get_status_message(object);
  1224.                         html_set_status(c, data.explicit_status_text);
  1225.                         content_broadcast(&c->base, CONTENT_MSG_STATUS, data);
  1226.                 } else {
  1227.                         /* Object content wants to set explicit message */
  1228.                         content_broadcast(&c->base, CONTENT_MSG_STATUS,
  1229.                                         event->data);
  1230.                 }
  1231.                 break;
  1232.  
  1233.         case CONTENT_MSG_REFORMAT:
  1234.                 break;
  1235.  
  1236.         case CONTENT_MSG_REDRAW:
  1237.                 if (c->base.status != CONTENT_STATUS_LOADING) {
  1238.                         union content_msg_data data = event->data;
  1239.  
  1240.                         if (!box_visible(box))
  1241.                                 break;
  1242.  
  1243.                         box_coords(box, &x, &y);
  1244.  
  1245.                         if (hlcache_handle_get_content(object) ==
  1246.                                         event->data.redraw.object) {
  1247.                                 data.redraw.x = data.redraw.x *
  1248.                                         box->width / content_get_width(object);
  1249.                                 data.redraw.y = data.redraw.y *
  1250.                                         box->height /
  1251.                                         content_get_height(object);
  1252.                                 data.redraw.width = data.redraw.width *
  1253.                                         box->width / content_get_width(object);
  1254.                                 data.redraw.height = data.redraw.height *
  1255.                                         box->height /
  1256.                                         content_get_height(object);
  1257.                                 data.redraw.object_width = box->width;
  1258.                                 data.redraw.object_height = box->height;
  1259.                         }
  1260.  
  1261.                         data.redraw.x += x + box->padding[LEFT];
  1262.                         data.redraw.y += y + box->padding[TOP];
  1263.                         data.redraw.object_x += x + box->padding[LEFT];
  1264.                         data.redraw.object_y += y + box->padding[TOP];
  1265.  
  1266.                         content_broadcast(&c->base, CONTENT_MSG_REDRAW, data);
  1267.                 }
  1268.                 break;
  1269.  
  1270.         case CONTENT_MSG_REFRESH:
  1271.                 if (content_get_type(object) == CONTENT_HTML) {
  1272.                         /* only for HTML objects */
  1273.                         schedule(event->data.delay * 100,
  1274.                                         html_object_refresh, o);
  1275.                 }
  1276.  
  1277.                 break;
  1278.  
  1279.         case CONTENT_MSG_LINK:
  1280.                 /* Don't care about favicons that aren't on top level content */
  1281.                 break;
  1282.  
  1283.         case CONTENT_MSG_GETCTX:
  1284.                 *(event->data.jscontext) = NULL;
  1285.                 break;
  1286.  
  1287.         case CONTENT_MSG_SCROLL:
  1288.                 if (box->scroll_x != NULL)
  1289.                         scrollbar_set(box->scroll_x, event->data.scroll.x0,
  1290.                                         false);
  1291.                 if (box->scroll_y != NULL)
  1292.                         scrollbar_set(box->scroll_y, event->data.scroll.y0,
  1293.                                         false);
  1294.                 break;
  1295.  
  1296.         case CONTENT_MSG_DRAGSAVE:
  1297.         {
  1298.                 union content_msg_data msg_data;
  1299.                 if (event->data.dragsave.content == NULL)
  1300.                         msg_data.dragsave.content = object;
  1301.                 else
  1302.                         msg_data.dragsave.content =
  1303.                                         event->data.dragsave.content;
  1304.  
  1305.                 content_broadcast(&c->base, CONTENT_MSG_DRAGSAVE, msg_data);
  1306.         }
  1307.                 break;
  1308.  
  1309.         case CONTENT_MSG_SAVELINK:
  1310.         case CONTENT_MSG_POINTER:
  1311.                 /* These messages are for browser window layer.
  1312.                  * we're not interested, so pass them on. */
  1313.                 content_broadcast(&c->base, event->type, event->data);
  1314.                 break;
  1315.  
  1316.         default:
  1317.                 assert(0);
  1318.         }
  1319.  
  1320.         if (c->base.status == CONTENT_STATUS_READY && c->base.active == 0 &&
  1321.                         (event->type == CONTENT_MSG_LOADING ||
  1322.                         event->type == CONTENT_MSG_DONE ||
  1323.                         event->type == CONTENT_MSG_ERROR)) {
  1324.                 /* all objects have arrived */
  1325.                 content__reformat(&c->base, false, c->base.available_width,
  1326.                                 c->base.height);
  1327.                 html_set_status(c, "");
  1328.                 content_set_done(&c->base);
  1329.         }
  1330.  
  1331.         /* If  1) the configuration option to reflow pages while objects are
  1332.          *        fetched is set
  1333.          *     2) an object is newly fetched & converted,
  1334.          *     3) the box's dimensions need to change due to being replaced
  1335.          *     4) the object's parent HTML is ready for reformat,
  1336.          *     5) the time since the previous reformat is more than the
  1337.          *        configured minimum time between reformats
  1338.          * then reformat the page to display newly fetched objects */
  1339.         else if (nsoption_bool(incremental_reflow) &&
  1340.                         event->type == CONTENT_MSG_DONE &&
  1341.                         !(box->flags & REPLACE_DIM) &&
  1342.                         (c->base.status == CONTENT_STATUS_READY ||
  1343.                          c->base.status == CONTENT_STATUS_DONE) &&
  1344.                         (wallclock() > c->base.reformat_time)) {
  1345.                 content__reformat(&c->base, false, c->base.available_width,
  1346.                                 c->base.height);
  1347.         }
  1348.  
  1349.         return NSERROR_OK;
  1350. }
  1351.  
  1352. /**
  1353.  * Start a fetch for an object required by a page, replacing an existing object.
  1354.  *
  1355.  * \param  object          Object to replace
  1356.  * \param  url             URL of object to fetch (copied)
  1357.  * \return  true on success, false on memory exhaustion
  1358.  */
  1359.  
  1360. static bool html_replace_object(struct content_html_object *object, nsurl *url)
  1361. {
  1362.         html_content *c;
  1363.         hlcache_child_context child;
  1364.         html_content *page;
  1365.         nserror error;
  1366.  
  1367.         assert(object != NULL);
  1368.  
  1369.         c = (html_content *) object->parent;
  1370.  
  1371.         child.charset = c->encoding;
  1372.         child.quirks = c->base.quirks;
  1373.  
  1374.         if (object->content != NULL) {
  1375.                 /* remove existing object */
  1376.                 if (content_get_status(object->content) != CONTENT_STATUS_DONE) {
  1377.                         c->base.active--;
  1378.                         LOG(("%d fetches active", c->base.active));
  1379.                 }
  1380.  
  1381.                 hlcache_handle_release(object->content);
  1382.                 object->content = NULL;
  1383.  
  1384.                 object->box->object = NULL;
  1385.         }
  1386.  
  1387.         /* initialise fetch */
  1388.         error = hlcache_handle_retrieve(url, HLCACHE_RETRIEVE_SNIFF_TYPE,
  1389.                         content_get_url(&c->base), NULL,
  1390.                         html_object_callback, object, &child,
  1391.                         object->permitted_types,
  1392.                         &object->content);
  1393.  
  1394.         if (error != NSERROR_OK)
  1395.                 return false;
  1396.  
  1397.         for (page = c; page != NULL; page = page->page) {
  1398.                 page->base.active++;
  1399.                 LOG(("%d fetches active", c->base.active));
  1400.  
  1401.                 page->base.status = CONTENT_STATUS_READY;
  1402.         }
  1403.  
  1404.         return true;
  1405. }
  1406.  
  1407. /**
  1408.  * schedule() callback for object refresh
  1409.  */
  1410.  
  1411. static void html_object_refresh(void *p)
  1412. {
  1413.         struct content_html_object *object = p;
  1414.         nsurl *refresh_url;
  1415.  
  1416.         assert(content_get_type(object->content) == CONTENT_HTML);
  1417.  
  1418.         refresh_url = content_get_refresh_url(object->content);
  1419.  
  1420.         /* Ignore if refresh URL has gone
  1421.          * (may happen if fetch errored) */
  1422.         if (refresh_url == NULL)
  1423.                 return;
  1424.  
  1425.         content_invalidate_reuse_data(object->content);
  1426.  
  1427.         if (!html_replace_object(object, refresh_url)) {
  1428.                 /** \todo handle memory exhaustion */
  1429.         }
  1430. }
  1431.  
  1432.  
  1433.  
  1434.  
  1435.  
  1436. /**
  1437.  * Callback for fetchcache() for linked stylesheets.
  1438.  */
  1439.  
  1440. static nserror
  1441. html_convert_css_callback(hlcache_handle *css,
  1442.                           const hlcache_event *event,
  1443.                           void *pw)
  1444. {
  1445.         html_content *parent = pw;
  1446.         unsigned int i;
  1447.         struct html_stylesheet *s;
  1448.  
  1449.         /* Find sheet */
  1450.         for (i = 0, s = parent->stylesheets;
  1451.                         i != parent->stylesheet_count; i++, s++) {
  1452.                 if (s->type == HTML_STYLESHEET_EXTERNAL &&
  1453.                                 s->data.external == css)
  1454.                         break;
  1455.         }
  1456.  
  1457.         assert(i != parent->stylesheet_count);
  1458.  
  1459.         switch (event->type) {
  1460.         case CONTENT_MSG_LOADING:
  1461.                 break;
  1462.  
  1463.         case CONTENT_MSG_READY:
  1464.                 break;
  1465.  
  1466.         case CONTENT_MSG_DONE:
  1467.                 LOG(("done stylesheet slot %d '%s'", i,
  1468.                                 nsurl_access(hlcache_handle_get_url(css))));
  1469.                 LOG(("Decrementing parent"));
  1470.                 parent->base.active--;
  1471.                 LOG(("%d fetches active", parent->base.active));
  1472.                 break;
  1473.  
  1474.         case CONTENT_MSG_ERROR:
  1475.                 LOG(("stylesheet %s failed: %s",
  1476.                                 nsurl_access(hlcache_handle_get_url(css)),
  1477.                                 event->data.error));
  1478.                 hlcache_handle_release(css);
  1479.                 s->data.external = NULL;
  1480.                 parent->base.active--;
  1481.                 LOG(("%d fetches active", parent->base.active));
  1482.                 content_add_error(&parent->base, "?", 0);
  1483.                 break;
  1484.  
  1485.         case CONTENT_MSG_STATUS:
  1486.                 if (event->data.explicit_status_text == NULL) {
  1487.                         /* Object content's status text updated */
  1488.                         html_set_status(parent,
  1489.                                         content_get_status_message(css));
  1490.                         content_broadcast(&parent->base, CONTENT_MSG_STATUS,
  1491.                                         event->data);
  1492.                 } else {
  1493.                         /* Object content wants to set explicit message */
  1494.                         content_broadcast(&parent->base, CONTENT_MSG_STATUS,
  1495.                                         event->data);
  1496.                 }
  1497.                 break;
  1498.  
  1499.         default:
  1500.                 assert(0);
  1501.         }
  1502.  
  1503.         if (parent->base.active == 0)
  1504.           {
  1505.             /* LOG(("parent->base.active == 0")); */
  1506.             html_finish_conversion(parent);
  1507.           }
  1508.        
  1509.         /* LOG(("Returning NSERROR_OK from html_redraw")); */
  1510.  
  1511.         return NSERROR_OK;
  1512. }
  1513.  
  1514. /**
  1515.  * Handle notification of inline style completion
  1516.  *
  1517.  * \param css  Inline style object
  1518.  * \param pw   Private data
  1519.  */
  1520. static void html_inline_style_done(struct content_css_data *css, void *pw)
  1521. {
  1522.         html_content *html = pw;
  1523.  
  1524.         if (--html->base.active == 0)
  1525.                 html_finish_conversion(html);
  1526. }
  1527.  
  1528. /**
  1529.  * Process an inline stylesheet in the document.
  1530.  *
  1531.  * \param  c      content structure
  1532.  * \param  index  Index of stylesheet in stylesheet_content array,
  1533.  *                updated if successful
  1534.  * \param  style  xml node of style element
  1535.  * \return  true on success, false if an error occurred
  1536.  */
  1537.  
  1538. static bool
  1539. html_process_style_element(html_content *c,
  1540.                            unsigned int *index,
  1541.                            dom_node *style)
  1542. {
  1543.         dom_node *child, *next;
  1544.         dom_string *val;
  1545.         dom_exception exc;
  1546.         struct html_stylesheet *stylesheets;
  1547.         struct content_css_data *sheet;
  1548.         nserror error;
  1549.  
  1550.         /* type='text/css', or not present (invalid but common) */
  1551.         exc = dom_element_get_attribute(style, corestring_dom_type, &val);
  1552.         if (exc == DOM_NO_ERR && val != NULL) {
  1553.                 if (!dom_string_caseless_lwc_isequal(val,
  1554.                                 corestring_lwc_text_css)) {
  1555.                         dom_string_unref(val);
  1556.                         return true;
  1557.                 }
  1558.                 dom_string_unref(val);
  1559.         }
  1560.  
  1561.         /* media contains 'screen' or 'all' or not present */
  1562.         exc = dom_element_get_attribute(style, corestring_dom_media, &val);
  1563.         if (exc == DOM_NO_ERR && val != NULL) {
  1564.                 if (strcasestr(dom_string_data(val), "screen") == NULL &&
  1565.                                 strcasestr(dom_string_data(val),
  1566.                                                 "all") == NULL) {
  1567.                         dom_string_unref(val);
  1568.                         return true;
  1569.                 }
  1570.                 dom_string_unref(val);
  1571.         }
  1572.  
  1573.         /* Extend array */
  1574.         stylesheets = realloc(c->stylesheets,
  1575.                               sizeof(struct html_stylesheet) * (*index + 1));
  1576.         if (stylesheets == NULL)
  1577.                 goto no_memory;
  1578.  
  1579.         c->stylesheets = stylesheets;
  1580.         c->stylesheet_count++;
  1581.  
  1582.         c->stylesheets[(*index)].type = HTML_STYLESHEET_INTERNAL;
  1583.         c->stylesheets[(*index)].data.internal = NULL;
  1584.  
  1585.         /* create stylesheet */
  1586.         sheet = calloc(1, sizeof(struct content_css_data));
  1587.         if (sheet == NULL) {
  1588.                 c->stylesheet_count--;
  1589.                 goto no_memory;
  1590.         }
  1591.  
  1592.         error = nscss_create_css_data(sheet,
  1593.                 nsurl_access(c->base_url), NULL, c->quirks,
  1594.                 html_inline_style_done, c);
  1595.         if (error != NSERROR_OK) {
  1596.                 free(sheet);
  1597.                 c->stylesheet_count--;
  1598.                 content_broadcast_errorcode(&c->base, error);
  1599.                 return false;
  1600.         }
  1601.  
  1602.         /* can't just use xmlNodeGetContent(style), because that won't
  1603.          * give the content of comments which may be used to 'hide'
  1604.          * the content */
  1605.         exc = dom_node_get_first_child(style, &child);
  1606.         if (exc != DOM_NO_ERR) {
  1607.                 nscss_destroy_css_data(sheet);
  1608.                 free(sheet);
  1609.                 c->stylesheet_count--;
  1610.                 goto no_memory;
  1611.         }
  1612.  
  1613.         while (child != NULL) {
  1614.                 dom_string *data;
  1615.  
  1616.                 exc = dom_node_get_text_content(child, &data);
  1617.                 if (exc != DOM_NO_ERR) {
  1618.                         dom_node_unref(child);
  1619.                         nscss_destroy_css_data(sheet);
  1620.                         free(sheet);
  1621.                         c->stylesheet_count--;
  1622.                         goto no_memory;
  1623.                 }
  1624.  
  1625.                 if (nscss_process_css_data(sheet, dom_string_data(data),
  1626.                                 dom_string_byte_length(data)) == false) {
  1627.                         dom_string_unref(data);
  1628.                         dom_node_unref(child);
  1629.                         nscss_destroy_css_data(sheet);
  1630.                         free(sheet);
  1631.                         c->stylesheet_count--;
  1632.                         goto no_memory;
  1633.                 }
  1634.  
  1635.                 dom_string_unref(data);
  1636.  
  1637.                 exc = dom_node_get_next_sibling(child, &next);
  1638.                 if (exc != DOM_NO_ERR) {
  1639.                         dom_node_unref(child);
  1640.                         nscss_destroy_css_data(sheet);
  1641.                         free(sheet);
  1642.                         c->stylesheet_count--;
  1643.                         goto no_memory;
  1644.                 }
  1645.  
  1646.                 dom_node_unref(child);
  1647.                 child = next;
  1648.         }
  1649.  
  1650.         c->base.active++;
  1651.         LOG(("%d fetches active", c->base.active));
  1652.  
  1653.         /* Convert the content -- manually, as we want the result */
  1654.         if (nscss_convert_css_data(sheet) != CSS_OK) {
  1655.                 /* conversion failed */
  1656.                 c->base.active--;
  1657.                 LOG(("%d fetches active", c->base.active));
  1658.                 nscss_destroy_css_data(sheet);
  1659.                 free(sheet);
  1660.                 sheet = NULL;
  1661.         }
  1662.  
  1663.         /* Update index */
  1664.         c->stylesheets[(*index)].data.internal = sheet;
  1665.         (*index)++;
  1666.  
  1667.         return true;
  1668.  
  1669. no_memory:
  1670.         content_broadcast_errorcode(&c->base, NSERROR_NOMEM);
  1671.         return false;
  1672. }
  1673.  
  1674.  
  1675.  
  1676. struct find_stylesheet_ctx {
  1677.         unsigned int count;
  1678.         html_content *c;
  1679. };
  1680.  
  1681. /** callback to process stylesheet elements
  1682.  */
  1683. static bool
  1684. html_process_stylesheet(dom_node *node, dom_string *name, void *vctx)
  1685. {
  1686.         struct find_stylesheet_ctx *ctx = (struct find_stylesheet_ctx *)vctx;
  1687.         dom_string *rel, *type_attr, *media, *href;
  1688.         struct html_stylesheet *stylesheets;
  1689.         nsurl *joined;
  1690.         dom_exception exc;
  1691.         nserror ns_error;
  1692.         hlcache_child_context child;
  1693.  
  1694.         /* deal with style nodes */
  1695.         if (dom_string_caseless_lwc_isequal(name, corestring_lwc_style)) {
  1696.                 if (!html_process_style_element(ctx->c, &ctx->count, node))
  1697.                         return false;
  1698.                 return true;
  1699.         }
  1700.  
  1701.         /* if it is not a link node skip it */
  1702.         if (!dom_string_caseless_lwc_isequal(name, corestring_lwc_link)) {
  1703.                 return true;
  1704.         }
  1705.  
  1706.         /* rel=<space separated list, including 'stylesheet'> */
  1707.         exc = dom_element_get_attribute(node,
  1708.                                         corestring_dom_rel, &rel);
  1709.         if (exc != DOM_NO_ERR || rel == NULL)
  1710.                 return true;
  1711.  
  1712.         if (strcasestr(dom_string_data(rel), "stylesheet") == 0) {
  1713.                 dom_string_unref(rel);
  1714.                 return true;
  1715.         } else if (strcasestr(dom_string_data(rel), "alternate") != 0) {
  1716.                 /* Ignore alternate stylesheets */
  1717.                 dom_string_unref(rel);
  1718.                 return true;
  1719.         }
  1720.         dom_string_unref(rel);
  1721.  
  1722.         /* type='text/css' or not present */
  1723.         exc = dom_element_get_attribute(node, corestring_dom_type, &type_attr);
  1724.         if (exc == DOM_NO_ERR && type_attr != NULL) {
  1725.                 if (!dom_string_caseless_lwc_isequal(type_attr,
  1726.                                 corestring_lwc_text_css)) {
  1727.                         dom_string_unref(type_attr);
  1728.                         return true;
  1729.                 }
  1730.                 dom_string_unref(type_attr);
  1731.         }
  1732.  
  1733.         /* media contains 'screen' or 'all' or not present */
  1734.         exc = dom_element_get_attribute(node, corestring_dom_media, &media);
  1735.         if (exc == DOM_NO_ERR && media != NULL) {
  1736.                 if (strcasestr(dom_string_data(media), "screen") == NULL &&
  1737.                     strcasestr(dom_string_data(media), "all") == NULL) {
  1738.                         dom_string_unref(media);
  1739.                         return true;
  1740.                 }
  1741.                 dom_string_unref(media);
  1742.         }
  1743.  
  1744.         /* href='...' */
  1745.         exc = dom_element_get_attribute(node, corestring_dom_href, &href);
  1746.         if (exc != DOM_NO_ERR || href == NULL)
  1747.                 return true;
  1748.  
  1749.         /* TODO: only the first preferred stylesheets (ie.
  1750.          * those with a title attribute) should be loaded
  1751.          * (see HTML4 14.3) */
  1752.  
  1753.         ns_error = nsurl_join(ctx->c->base_url, dom_string_data(href), &joined);
  1754.         if (ns_error != NSERROR_OK) {
  1755.                 dom_string_unref(href);
  1756.                 goto no_memory;
  1757.         }
  1758.         dom_string_unref(href);
  1759.  
  1760.         LOG(("linked stylesheet %i '%s'", ctx->count, nsurl_access(joined)));
  1761.  
  1762.         /* start fetch */
  1763.         stylesheets = realloc(ctx->c->stylesheets,
  1764.                               sizeof(struct html_stylesheet) * (ctx->count + 1));
  1765.         if (stylesheets == NULL) {
  1766.                 nsurl_unref(joined);
  1767.                 ns_error = NSERROR_NOMEM;
  1768.                 goto no_memory;
  1769.         }
  1770.  
  1771.         ctx->c->stylesheets = stylesheets;
  1772.         ctx->c->stylesheet_count++;
  1773.         ctx->c->stylesheets[ctx->count].type = HTML_STYLESHEET_EXTERNAL;
  1774.  
  1775.         child.charset = ctx->c->encoding;
  1776.         child.quirks = ctx->c->base.quirks;
  1777.  
  1778.         ns_error = hlcache_handle_retrieve(joined,
  1779.                                            0,
  1780.                                            content_get_url(&ctx->c->base),
  1781.                                            NULL,
  1782.                                            html_convert_css_callback,
  1783.                                            ctx->c,
  1784.                                            &child,
  1785.                                            CONTENT_CSS,
  1786.                                            &ctx->c->stylesheets[ctx->count].data.external);
  1787.  
  1788.         nsurl_unref(joined);
  1789.  
  1790.         if (ns_error != NSERROR_OK)
  1791.                 goto no_memory;
  1792.  
  1793.         ctx->c->base.active++;
  1794.         LOG(("%d fetches active", ctx->c->base.active));
  1795.  
  1796.         ctx->count++;
  1797.  
  1798.         return true;
  1799.  
  1800. no_memory:
  1801.         content_broadcast_errorcode(&ctx->c->base, ns_error);
  1802.         return false;
  1803. }
  1804.  
  1805.  
  1806. /**
  1807.  * Process inline stylesheets and fetch linked stylesheets.
  1808.  *
  1809.  * Uses STYLE and LINK elements inside and outside HEAD
  1810.  *
  1811.  * \param c content structure
  1812.  * \param html dom node of html element
  1813.  * \return true on success, false if an error occurred
  1814.  */
  1815.  
  1816. static bool html_find_stylesheets(html_content *c, dom_node *html)
  1817. {
  1818.         nserror ns_error;
  1819.         bool result;
  1820.         struct find_stylesheet_ctx ctx;
  1821.         hlcache_child_context child;
  1822.  
  1823.         /* setup context */
  1824.         ctx.c = c;
  1825.         ctx.count = STYLESHEET_START;
  1826.  
  1827.         /* stylesheet 0 is the base style sheet,
  1828.          * stylesheet 1 is the quirks mode style sheet,
  1829.          * stylesheet 2 is the adblocking stylesheet,
  1830.          * stylesheet 3 is the user stylesheet */
  1831.         c->stylesheets = calloc(STYLESHEET_START, sizeof(struct html_stylesheet));
  1832.         if (c->stylesheets == NULL) {
  1833.                 ns_error = NSERROR_NOMEM;
  1834.                 goto html_find_stylesheets_no_memory;
  1835.         }
  1836.  
  1837.         c->stylesheets[STYLESHEET_BASE].type = HTML_STYLESHEET_EXTERNAL;
  1838.         c->stylesheets[STYLESHEET_BASE].data.external = NULL;
  1839.         c->stylesheets[STYLESHEET_QUIRKS].type = HTML_STYLESHEET_EXTERNAL;
  1840.         c->stylesheets[STYLESHEET_QUIRKS].data.external = NULL;
  1841.         c->stylesheets[STYLESHEET_ADBLOCK].type = HTML_STYLESHEET_EXTERNAL;
  1842.         c->stylesheets[STYLESHEET_ADBLOCK].data.external = NULL;
  1843.         c->stylesheets[STYLESHEET_USER].type = HTML_STYLESHEET_EXTERNAL;
  1844.         c->stylesheets[STYLESHEET_USER].data.external = NULL;
  1845.         c->stylesheet_count = STYLESHEET_START;
  1846.  
  1847.         child.charset = c->encoding;
  1848.         child.quirks = c->base.quirks;
  1849.  
  1850.         ns_error = hlcache_handle_retrieve(html_default_stylesheet_url, 0,
  1851.                         content_get_url(&c->base), NULL,
  1852.                         html_convert_css_callback, c, &child, CONTENT_CSS,
  1853.                         &c->stylesheets[STYLESHEET_BASE].data.external);
  1854.         if (ns_error != NSERROR_OK)
  1855.                 goto html_find_stylesheets_no_memory;
  1856.  
  1857.         c->base.active++;
  1858.         LOG(("%d fetches active", c->base.active));
  1859.  
  1860.         if (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL) {
  1861.                 ns_error = hlcache_handle_retrieve(html_quirks_stylesheet_url,
  1862.                                 0, content_get_url(&c->base), NULL,
  1863.                                 html_convert_css_callback, c, &child,
  1864.                                 CONTENT_CSS,
  1865.                                 &c->stylesheets[STYLESHEET_QUIRKS].data.external);
  1866.                 if (ns_error != NSERROR_OK)
  1867.                         goto html_find_stylesheets_no_memory;
  1868.  
  1869.                 c->base.active++;
  1870.                 LOG(("%d fetches active", c->base.active));
  1871.  
  1872.         }
  1873.  
  1874.         if (nsoption_bool(block_ads)) {
  1875.                 ns_error = hlcache_handle_retrieve(html_adblock_stylesheet_url,
  1876.                                 0, content_get_url(&c->base), NULL,
  1877.                                 html_convert_css_callback, c, &child, CONTENT_CSS,
  1878.                                 &c->stylesheets[STYLESHEET_ADBLOCK].
  1879.                                                 data.external);
  1880.                 if (ns_error != NSERROR_OK)
  1881.                         goto html_find_stylesheets_no_memory;
  1882.  
  1883.                 c->base.active++;
  1884.                 LOG(("%d fetches active", c->base.active));
  1885.  
  1886.         }
  1887.  
  1888.         ns_error = hlcache_handle_retrieve(html_user_stylesheet_url, 0,
  1889.                         content_get_url(&c->base), NULL,
  1890.                         html_convert_css_callback, c, &child, CONTENT_CSS,
  1891.                         &c->stylesheets[STYLESHEET_USER].data.external);
  1892.         if (ns_error != NSERROR_OK)
  1893.                 goto html_find_stylesheets_no_memory;
  1894.  
  1895.         c->base.active++;
  1896.         LOG(("%d fetches active", c->base.active));
  1897.  
  1898.         result = libdom_treewalk(html, html_process_stylesheet, &ctx);
  1899.  
  1900.         assert(c->stylesheet_count == ctx.count);
  1901.  
  1902.         return result;
  1903.  
  1904. html_find_stylesheets_no_memory:
  1905.         content_broadcast_errorcode(&c->base, ns_error);
  1906.         return false;
  1907. }
  1908.  
  1909. /**
  1910.  * Convert a CONTENT_HTML for display.
  1911.  *
  1912.  * The following steps are carried out in order:
  1913.  *
  1914.  *  - parsing to an XML tree is completed
  1915.  *  - stylesheets are fetched
  1916.  *  - the XML tree is converted to a box tree and object fetches are started
  1917.  *
  1918.  * On exit, the content status will be either CONTENT_STATUS_DONE if the
  1919.  * document is completely loaded or CONTENT_STATUS_READY if objects are still
  1920.  * being fetched.
  1921.  */
  1922.  
  1923. static bool html_convert(struct content *c)
  1924. {
  1925.         html_content *htmlc = (html_content *) c;
  1926.  
  1927.         htmlc->base.active--; /* the html fetch is no longer active */
  1928.         LOG(("%d fetches active", htmlc->base.active));
  1929.  
  1930.         /* if there are no active fetches in progress no scripts are
  1931.          * being fetched or they completed already.
  1932.          */
  1933.         if (htmlc->base.active == 0) {
  1934.                 return html_begin_conversion(htmlc);
  1935.         }
  1936.         return true;
  1937. }
  1938.  
  1939. bool
  1940. html_begin_conversion(html_content *htmlc)
  1941. {
  1942.         dom_node *html, *head;
  1943.         nserror ns_error;
  1944.         struct form *f;
  1945.         dom_exception exc; /* returned by libdom functions */
  1946.         dom_string *node_name = NULL;
  1947.         dom_hubbub_error error;
  1948.  
  1949.         /* complete parsing */
  1950.         error = dom_hubbub_parser_completed(htmlc->parser);
  1951.         if (error != DOM_HUBBUB_OK) {
  1952.                 LOG(("Parsing failed"));
  1953.  
  1954.                 content_broadcast_errorcode(&htmlc->base,
  1955.                                             libdom_hubbub_error_to_nserror(error));
  1956.  
  1957.                 return false;
  1958.         }
  1959.  
  1960.         /* Give up processing if we've been aborted */
  1961.         if (htmlc->aborted) {
  1962.                 content_broadcast_errorcode(&htmlc->base, NSERROR_STOPPED);
  1963.                 return false;
  1964.         }
  1965.  
  1966.  
  1967.         /* complete script execution */
  1968.         html_scripts_exec(htmlc);
  1969.  
  1970.         /* fire a simple event that bubbles named DOMContentLoaded at
  1971.          * the Document.
  1972.          */
  1973.  
  1974.         /* quirks mode */
  1975.         exc = dom_document_get_quirks_mode(htmlc->document, &htmlc->quirks);
  1976.         if (exc != DOM_NO_ERR) {
  1977.                 LOG(("error retrieving quirks"));
  1978.                 /** @todo should this be fatal to the conversion? */
  1979.         }
  1980.         LOG(("quirks set to %d", htmlc->quirks));
  1981.  
  1982.         /* get encoding */
  1983.         if (htmlc->encoding == NULL) {
  1984.                 const char *encoding;
  1985.  
  1986.                 encoding = dom_hubbub_parser_get_encoding(htmlc->parser,
  1987.                                         &htmlc->encoding_source);
  1988.                 if (encoding == NULL) {
  1989.                         content_broadcast_errorcode(&htmlc->base,
  1990.                                                     NSERROR_NOMEM);
  1991.                         return false;
  1992.                 }
  1993.  
  1994.                 htmlc->encoding = strdup(encoding);
  1995.                 if (htmlc->encoding == NULL) {
  1996.                         content_broadcast_errorcode(&htmlc->base,
  1997.                                                     NSERROR_NOMEM);
  1998.                         return false;
  1999.                 }
  2000.         }
  2001.  
  2002.         /* locate root element and ensure it is html */
  2003.         exc = dom_document_get_document_element(htmlc->document, (void *) &html);
  2004.         if ((exc != DOM_NO_ERR) || (html == NULL)) {
  2005.                 LOG(("error retrieving html element from dom"));
  2006.                 content_broadcast_errorcode(&htmlc->base, NSERROR_DOM);
  2007.                 return false;
  2008.         }
  2009.  
  2010.         exc = dom_node_get_node_name(html, &node_name);
  2011.         if ((exc != DOM_NO_ERR) ||
  2012.             (node_name == NULL) ||
  2013.             (!dom_string_caseless_lwc_isequal(node_name,
  2014.                         corestring_lwc_html))) {
  2015.                 LOG(("root element not html"));
  2016.                 content_broadcast_errorcode(&htmlc->base, NSERROR_DOM);
  2017.                 dom_node_unref(html);
  2018.                 return false;
  2019.         }
  2020.         dom_string_unref(node_name);
  2021.  
  2022.         head = libdom_find_first_element(html, corestring_lwc_head);
  2023.         if (head != NULL) {
  2024.                 ns_error = html_head(htmlc, head);
  2025.                 if (ns_error != NSERROR_OK) {
  2026.                         content_broadcast_errorcode(&htmlc->base, ns_error);
  2027.  
  2028.                         dom_node_unref(html);
  2029.                         dom_node_unref(head);
  2030.                         return false;
  2031.                 }
  2032.  
  2033.                 /* handle meta refresh */
  2034.                 ns_error = html_meta_refresh(htmlc, head);
  2035.                 if (ns_error != NSERROR_OK) {
  2036.                         content_broadcast_errorcode(&htmlc->base, ns_error);
  2037.  
  2038.                         dom_node_unref(html);
  2039.                         dom_node_unref(head);
  2040.                         return false;
  2041.                 }
  2042.         }
  2043.  
  2044.         /* Retrieve forms from parser */
  2045.         htmlc->forms = html_forms_get_forms(htmlc->encoding,
  2046.                         (dom_html_document *) htmlc->document);
  2047.         for (f = htmlc->forms; f != NULL; f = f->prev) {
  2048.                 nsurl *action;
  2049.  
  2050.                 /* Make all actions absolute */
  2051.                 if (f->action == NULL || f->action[0] == '\0') {
  2052.                         /* HTML5 4.10.22.3 step 9 */
  2053.                         nsurl *doc_addr = content_get_url(&htmlc->base);
  2054.                         ns_error = nsurl_join(htmlc->base_url,
  2055.                                               nsurl_access(doc_addr),
  2056.                                               &action);
  2057.                 } else {
  2058.                         ns_error = nsurl_join(htmlc->base_url,
  2059.                                               f->action,
  2060.                                               &action);
  2061.                 }
  2062.  
  2063.                 if (ns_error != NSERROR_OK) {
  2064.                         content_broadcast_errorcode(&htmlc->base, ns_error);
  2065.  
  2066.                         dom_node_unref(html);
  2067.                         dom_node_unref(head);
  2068.                         return false;
  2069.                 }
  2070.  
  2071.                 free(f->action);
  2072.                 f->action = strdup(nsurl_access(action));
  2073.                 nsurl_unref(action);
  2074.                 if (f->action == NULL) {
  2075.                         content_broadcast_errorcode(&htmlc->base,
  2076.                                                     NSERROR_NOMEM);
  2077.  
  2078.                         dom_node_unref(html);
  2079.                         dom_node_unref(head);
  2080.                         return false;
  2081.                 }
  2082.  
  2083.                 /* Ensure each form has a document encoding */
  2084.                 if (f->document_charset == NULL) {
  2085.                         f->document_charset = strdup(htmlc->encoding);
  2086.                         if (f->document_charset == NULL) {
  2087.                                 content_broadcast_errorcode(&htmlc->base,
  2088.                                                             NSERROR_NOMEM);
  2089.                                 dom_node_unref(html);
  2090.                                 dom_node_unref(head);
  2091.                                 return false;
  2092.                         }
  2093.                 }
  2094.         }
  2095.  
  2096.         dom_node_unref(head);
  2097.  
  2098.         /* get stylesheets */
  2099.         if (html_find_stylesheets(htmlc, html) == false) {
  2100.                 dom_node_unref(html);
  2101.                 return false;
  2102.         }
  2103.  
  2104.         dom_node_unref(html);
  2105.  
  2106.         if (htmlc->base.active == 0) {
  2107.                 html_finish_conversion(htmlc);
  2108.         }
  2109.  
  2110.         return true;
  2111. }
  2112.  
  2113.  
  2114.  
  2115.  
  2116. /**
  2117.  * Start a fetch for an object required by a page.
  2118.  *
  2119.  * \param  c                 content of type CONTENT_HTML
  2120.  * \param  url               URL of object to fetch (copied)
  2121.  * \param  box               box that will contain the object
  2122.  * \param  permitted_types   bitmap of acceptable types
  2123.  * \param  available_width   estimate of width of object
  2124.  * \param  available_height  estimate of height of object
  2125.  * \param  background        this is a background image
  2126.  * \return  true on success, false on memory exhaustion
  2127.  */
  2128.  
  2129. bool html_fetch_object(html_content *c, nsurl *url, struct box *box,
  2130.                 content_type permitted_types,
  2131.                 int available_width, int available_height,
  2132.                 bool background)
  2133. {
  2134.         struct content_html_object *object;
  2135.         hlcache_child_context child;
  2136.         nserror error;
  2137.  
  2138.         /* If we've already been aborted, don't bother attempting the fetch */
  2139.         if (c->aborted)
  2140.                 return true;
  2141.  
  2142.         child.charset = c->encoding;
  2143.         child.quirks = c->base.quirks;
  2144.  
  2145.         object = calloc(1, sizeof(struct content_html_object));
  2146.         if (object == NULL) {
  2147.                 return false;
  2148.         }
  2149.  
  2150.         object->parent = (struct content *) c;
  2151.         object->next = NULL;
  2152.         object->content = NULL;
  2153.         object->box = box;
  2154.         object->permitted_types = permitted_types;
  2155.         object->background = background;
  2156.  
  2157.         error = hlcache_handle_retrieve(url,
  2158.                         HLCACHE_RETRIEVE_SNIFF_TYPE,
  2159.                         content_get_url(&c->base), NULL,
  2160.                         html_object_callback, object, &child,
  2161.                         object->permitted_types, &object->content);
  2162.         if (error != NSERROR_OK) {
  2163.                 free(object);
  2164.                 return error != NSERROR_NOMEM;
  2165.         }
  2166.  
  2167.         /* add to content object list */
  2168.         object->next = c->object_list;
  2169.         c->object_list = object;
  2170.  
  2171.         c->num_objects++;
  2172.         c->base.active++;
  2173.         LOG(("%d fetches active", c->base.active));
  2174.  
  2175.         return true;
  2176. }
  2177.  
  2178.  
  2179.  
  2180.  
  2181.  
  2182. /**
  2183.  * Stop loading a CONTENT_HTML.
  2184.  */
  2185.  
  2186. static void html_stop(struct content *c)
  2187. {
  2188.         html_content *htmlc = (html_content *) c;
  2189.         struct content_html_object *object;
  2190.  
  2191.         switch (c->status) {
  2192.         case CONTENT_STATUS_LOADING:
  2193.                 /* Still loading; simply flag that we've been aborted
  2194.                  * html_convert/html_finish_conversion will do the rest */
  2195.                 htmlc->aborted = true;
  2196.                 break;
  2197.         case CONTENT_STATUS_READY:
  2198.                 for (object = htmlc->object_list; object != NULL;
  2199.                                 object = object->next) {
  2200.                         if (object->content == NULL)
  2201.                                 continue;
  2202.  
  2203.                         if (content_get_status(object->content) ==
  2204.                                         CONTENT_STATUS_DONE)
  2205.                                 ; /* already loaded: do nothing */
  2206.                         else if (content_get_status(object->content) ==
  2207.                                         CONTENT_STATUS_READY)
  2208.                                 hlcache_handle_abort(object->content);
  2209.                                 /* Active count will be updated when
  2210.                                  * html_object_callback receives
  2211.                                  * CONTENT_MSG_DONE from this object */
  2212.                         else {
  2213.                                 hlcache_handle_abort(object->content);
  2214.                                 hlcache_handle_release(object->content);
  2215.                                 object->content = NULL;
  2216.  
  2217.                                 c->active--;
  2218.                                 LOG(("%d fetches active", c->active));
  2219.  
  2220.                         }
  2221.                 }
  2222.  
  2223.                 /* If there are no further active fetches and we're still
  2224.                  * in the READY state, transition to the DONE state. */
  2225.                 if (c->status == CONTENT_STATUS_READY && c->active == 0) {
  2226.                         html_set_status(htmlc, "");
  2227.                         content_set_done(c);
  2228.                 }
  2229.  
  2230.                 break;
  2231.         case CONTENT_STATUS_DONE:
  2232.                 /* Nothing to do */
  2233.                 break;
  2234.         default:
  2235.                 LOG(("Unexpected status %d", c->status));
  2236.                 assert(0);
  2237.         }
  2238. }
  2239.  
  2240.  
  2241. /**
  2242.  * Reformat a CONTENT_HTML to a new width.
  2243.  */
  2244.  
  2245. static void html_reformat(struct content *c, int width, int height)
  2246. {
  2247.         html_content *htmlc = (html_content *) c;
  2248.         struct box *layout;
  2249.         unsigned int time_before, time_taken;
  2250.  
  2251.         time_before = wallclock();
  2252.  
  2253.         layout_document(htmlc, width, height);
  2254.         layout = htmlc->layout;
  2255.  
  2256.         /* width and height are at least margin box of document */
  2257.         c->width = layout->x + layout->padding[LEFT] + layout->width +
  2258.                         layout->padding[RIGHT] + layout->border[RIGHT].width +
  2259.                         layout->margin[RIGHT];
  2260.         c->height = layout->y + layout->padding[TOP] + layout->height +
  2261.                         layout->padding[BOTTOM] + layout->border[BOTTOM].width +
  2262.                         layout->margin[BOTTOM];
  2263.  
  2264.         /* if boxes overflow right or bottom edge, expand to contain it */
  2265.         if (c->width < layout->x + layout->descendant_x1)
  2266.                 c->width = layout->x + layout->descendant_x1;
  2267.         if (c->height < layout->y + layout->descendant_y1)
  2268.                 c->height = layout->y + layout->descendant_y1;
  2269.  
  2270.         selection_reinit(&htmlc->sel, htmlc->layout);
  2271.  
  2272.         time_taken = wallclock() - time_before;
  2273.         c->reformat_time = wallclock() +
  2274.                 ((time_taken * 3 < nsoption_int(min_reflow_period) ?
  2275.                   nsoption_int(min_reflow_period) : time_taken * 3));
  2276. }
  2277.  
  2278.  
  2279. /**
  2280.  * Redraw a box.
  2281.  *
  2282.  * \param  h    content containing the box, of type CONTENT_HTML
  2283.  * \param  box  box to redraw
  2284.  */
  2285.  
  2286. void html_redraw_a_box(hlcache_handle *h, struct box *box)
  2287. {
  2288.         int x, y;
  2289.  
  2290.         box_coords(box, &x, &y);
  2291.  
  2292.         content_request_redraw(h, x, y,
  2293.                         box->padding[LEFT] + box->width + box->padding[RIGHT],
  2294.                         box->padding[TOP] + box->height + box->padding[BOTTOM]);
  2295. }
  2296.  
  2297.  
  2298. /**
  2299.  * Redraw a box.
  2300.  *
  2301.  * \param  h    content containing the box, of type CONTENT_HTML
  2302.  * \param  box  box to redraw
  2303.  */
  2304.  
  2305. void html__redraw_a_box(struct html_content *html, struct box *box)
  2306. {
  2307.         int x, y;
  2308.  
  2309.         box_coords(box, &x, &y);
  2310.  
  2311.         content__request_redraw((struct content *)html, x, y,
  2312.                         box->padding[LEFT] + box->width + box->padding[RIGHT],
  2313.                         box->padding[TOP] + box->height + box->padding[BOTTOM]);
  2314. }
  2315.  
  2316. static void html_destroy_frameset(struct content_html_frames *frameset)
  2317. {
  2318.         int i;
  2319.  
  2320.         if (frameset->name) {
  2321.                 talloc_free(frameset->name);
  2322.                 frameset->name = NULL;
  2323.         }
  2324.         if (frameset->url) {
  2325.                 talloc_free(frameset->url);
  2326.                 frameset->url = NULL;
  2327.         }
  2328.         if (frameset->children) {
  2329.                 for (i = 0; i < (frameset->rows * frameset->cols); i++) {
  2330.                         if (frameset->children[i].name) {
  2331.                                 talloc_free(frameset->children[i].name);
  2332.                                 frameset->children[i].name = NULL;
  2333.                         }
  2334.                         if (frameset->children[i].url) {
  2335.                                 nsurl_unref(frameset->children[i].url);
  2336.                                 frameset->children[i].url = NULL;
  2337.                         }
  2338.                         if (frameset->children[i].children)
  2339.                                 html_destroy_frameset(&frameset->children[i]);
  2340.                 }
  2341.                 talloc_free(frameset->children);
  2342.                 frameset->children = NULL;
  2343.         }
  2344. }
  2345.  
  2346. static void html_destroy_iframe(struct content_html_iframe *iframe)
  2347. {
  2348.         struct content_html_iframe *next;
  2349.         next = iframe;
  2350.         while ((iframe = next) != NULL) {
  2351.                 next = iframe->next;
  2352.                 if (iframe->name)
  2353.                         talloc_free(iframe->name);
  2354.                 if (iframe->url) {
  2355.                         nsurl_unref(iframe->url);
  2356.                         iframe->url = NULL;
  2357.                 }
  2358.                 talloc_free(iframe);
  2359.         }
  2360. }
  2361.  
  2362.  
  2363. static void html_free_layout(html_content *htmlc)
  2364. {
  2365.         if (htmlc->bctx != NULL) {
  2366.                 /* freeing talloc context should let the entire box
  2367.                  * set be destroyed
  2368.                  */
  2369.                 talloc_free(htmlc->bctx);
  2370.         }
  2371. }
  2372.  
  2373. /**
  2374.  * Destroy a CONTENT_HTML and free all resources it owns.
  2375.  */
  2376.  
  2377. static void html_destroy(struct content *c)
  2378. {
  2379.         html_content *html = (html_content *) c;
  2380.         unsigned int i;
  2381.         struct form *f, *g;
  2382.  
  2383.         LOG(("content %p", c));
  2384.  
  2385.         /* Destroy forms */
  2386.         for (f = html->forms; f != NULL; f = g) {
  2387.                 g = f->prev;
  2388.  
  2389.                 form_free(f);
  2390.         }
  2391.  
  2392.         imagemap_destroy(html);
  2393.  
  2394.         if (c->refresh)
  2395.                 nsurl_unref(c->refresh);
  2396.  
  2397.         if (html->base_url)
  2398.                 nsurl_unref(html->base_url);
  2399.  
  2400.         if (html->parser != NULL) {
  2401.                 dom_hubbub_parser_destroy(html->parser);
  2402.                 html->parser = NULL;
  2403.         }
  2404.  
  2405.         if (html->document != NULL) {
  2406.                 dom_node_unref(html->document);
  2407.         }
  2408.  
  2409.         /* Free base target */
  2410.         if (html->base_target != NULL) {
  2411.                 free(html->base_target);
  2412.                 html->base_target = NULL;
  2413.         }
  2414.  
  2415.         /* Free frameset */
  2416.         if (html->frameset != NULL) {
  2417.                 html_destroy_frameset(html->frameset);
  2418.                 talloc_free(html->frameset);
  2419.                 html->frameset = NULL;
  2420.         }
  2421.  
  2422.         /* Free iframes */
  2423.         if (html->iframe != NULL) {
  2424.                 html_destroy_iframe(html->iframe);
  2425.                 html->iframe = NULL;
  2426.         }
  2427.  
  2428.         /* Destroy selection context */
  2429.         if (html->select_ctx != NULL) {
  2430.                 css_select_ctx_destroy(html->select_ctx);
  2431.                 html->select_ctx = NULL;
  2432.         }
  2433.  
  2434.         if (html->universal != NULL) {
  2435.                 lwc_string_unref(html->universal);
  2436.                 html->universal = NULL;
  2437.         }
  2438.  
  2439.         /* Free stylesheets */
  2440.         for (i = 0; i != html->stylesheet_count; i++) {
  2441.                 if (html->stylesheets[i].type == HTML_STYLESHEET_EXTERNAL &&
  2442.                                 html->stylesheets[i].data.external != NULL) {
  2443.                         hlcache_handle_release(
  2444.                                         html->stylesheets[i].data.external);
  2445.                 } else if (html->stylesheets[i].type ==
  2446.                                 HTML_STYLESHEET_INTERNAL &&
  2447.                                 html->stylesheets[i].data.internal != NULL) {
  2448.                         nscss_destroy_css_data(
  2449.                                         html->stylesheets[i].data.internal);
  2450.                 }
  2451.         }
  2452.         free(html->stylesheets);
  2453.  
  2454.         /* Free scripts */
  2455.         html_free_scripts(html);
  2456.  
  2457.         /* Free objects */
  2458.         html_destroy_objects(html);
  2459.  
  2460.         /* free layout */
  2461.         html_free_layout(html);
  2462. }
  2463.  
  2464.  
  2465. static nserror html_clone(const struct content *old, struct content **newc)
  2466. {
  2467.         /** \todo Clone HTML specifics */
  2468.  
  2469.         /* In the meantime, we should never be called, as HTML contents
  2470.          * cannot be shared and we're not intending to fix printing's
  2471.          * cloning of documents. */
  2472.         assert(0 && "html_clone should never be called");
  2473.  
  2474.         return true;
  2475. }
  2476.  
  2477. /**
  2478.  * Set the content status.
  2479.  */
  2480.  
  2481. void html_set_status(html_content *c, const char *extra)
  2482. {
  2483.         content_set_status(&c->base, extra);
  2484. }
  2485.  
  2486.  
  2487. /**
  2488.  * Handle a window containing a CONTENT_HTML being opened.
  2489.  */
  2490.  
  2491. static void
  2492. html_open(struct content *c,
  2493.           struct browser_window *bw,
  2494.           struct content *page,
  2495.           struct object_params *params)
  2496. {
  2497.         html_content *html = (html_content *) c;
  2498.         struct content_html_object *object, *next;
  2499.  
  2500.         html->bw = bw;
  2501.         html->page = (html_content *) page;
  2502.  
  2503.         /* text selection */
  2504.         selection_init(&html->sel, html->layout);
  2505.  
  2506.         for (object = html->object_list; object != NULL; object = next) {
  2507.                 next = object->next;
  2508.  
  2509.                 if (object->content == NULL)
  2510.                         continue;
  2511.  
  2512.                 if (content_get_type(object->content) == CONTENT_NONE)
  2513.                         continue;
  2514.  
  2515.                 content_open(object->content,
  2516.                                 bw, c,
  2517.                                 object->box->object_params);
  2518.         }
  2519. }
  2520.  
  2521.  
  2522. /**
  2523.  * Handle a window containing a CONTENT_HTML being closed.
  2524.  */
  2525.  
  2526. static void html_close(struct content *c)
  2527. {
  2528.         html_content *html = (html_content *) c;
  2529.         struct content_html_object *object, *next;
  2530.  
  2531.         if (html->search != NULL)
  2532.                 search_destroy_context(html->search);
  2533.  
  2534.         html->bw = NULL;
  2535.  
  2536.         for (object = html->object_list; object != NULL; object = next) {
  2537.                 next = object->next;
  2538.  
  2539.                 if (object->content == NULL)
  2540.                         continue;
  2541.  
  2542.                 if (content_get_type(object->content) == CONTENT_NONE)
  2543.                         continue;
  2544.  
  2545.                 if (content_get_type(object->content) == CONTENT_HTML)
  2546.                         schedule_remove(html_object_refresh, object);
  2547.  
  2548.                 content_close(object->content);
  2549.         }
  2550. }
  2551.  
  2552.  
  2553. /**
  2554.  * Return an HTML content's selection context
  2555.  */
  2556.  
  2557. static struct selection *html_get_selection(struct content *c)
  2558. {
  2559.         html_content *html = (html_content *) c;
  2560.  
  2561.         return &html->sel;
  2562. }
  2563.  
  2564.  
  2565. /**
  2566.  * Get access to any content, link URLs and objects (images) currently
  2567.  * at the given (x, y) coordinates.
  2568.  *
  2569.  * \param c     html content to look inside
  2570.  * \param x     x-coordinate of point of interest
  2571.  * \param y     y-coordinate of point of interest
  2572.  * \param data  pointer to contextual_content struct.  Its fields are updated
  2573.  *              with pointers to any relevent content, or set to NULL if none.
  2574.  */
  2575. static void
  2576. html_get_contextual_content(struct content *c,
  2577.                             int x,
  2578.                             int y,
  2579.                             struct contextual_content *data)
  2580. {
  2581.         html_content *html = (html_content *) c;
  2582.  
  2583.         struct box *box = html->layout;
  2584.         struct box *next;
  2585.         int box_x = 0, box_y = 0;
  2586.  
  2587.         while ((next = box_at_point(box, x, y, &box_x, &box_y)) != NULL) {
  2588.                 box = next;
  2589.  
  2590.                 if (box->style && css_computed_visibility(box->style) ==
  2591.                                 CSS_VISIBILITY_HIDDEN)
  2592.                         continue;
  2593.  
  2594.                 if (box->iframe)
  2595.                         browser_window_get_contextual_content(box->iframe,
  2596.                                         x - box_x, y - box_y, data);
  2597.  
  2598.                 if (box->object)
  2599.                         content_get_contextual_content(box->object,
  2600.                                         x - box_x, y - box_y, data);
  2601.  
  2602.                 if (box->object)
  2603.                         data->object = box->object;
  2604.  
  2605.                 if (box->href)
  2606.                         data->link_url = nsurl_access(box->href);
  2607.  
  2608.                 if (box->usemap) {
  2609.                         const char *target = NULL;
  2610.                         nsurl *url = imagemap_get(html, box->usemap, box_x,
  2611.                                         box_y, x, y, &target);
  2612.                         /* Box might have imagemap, but no actual link area
  2613.                          * at point */
  2614.                         if (url != NULL)
  2615.                                 data->link_url = nsurl_access(url);
  2616.                 }
  2617.                 if (box->gadget) {
  2618.                         switch (box->gadget->type) {
  2619.                         case GADGET_TEXTBOX:
  2620.                         case GADGET_TEXTAREA:
  2621.                         case GADGET_PASSWORD:
  2622.                                 data->form_features = CTX_FORM_TEXT;
  2623.                                 break;
  2624.  
  2625.                         case GADGET_FILE:
  2626.                                 data->form_features = CTX_FORM_FILE;
  2627.                                 break;
  2628.  
  2629.                         default:
  2630.                                 data->form_features = CTX_FORM_NONE;
  2631.                                 break;
  2632.                         }
  2633.                 }
  2634.         }
  2635. }
  2636.  
  2637.  
  2638. /**
  2639.  * Scroll deepest thing within the content which can be scrolled at given point
  2640.  *
  2641.  * \param c     html content to look inside
  2642.  * \param x     x-coordinate of point of interest
  2643.  * \param y     y-coordinate of point of interest
  2644.  * \param scrx  x-coordinate of point of interest
  2645.  * \param scry  y-coordinate of point of interest
  2646.  * \return true iff scroll was consumed by something in the content
  2647.  */
  2648. static bool
  2649. html_scroll_at_point(struct content *c, int x, int y, int scrx, int scry)
  2650. {
  2651.         html_content *html = (html_content *) c;
  2652.  
  2653.         struct box *box = html->layout;
  2654.         struct box *next;
  2655.         int box_x = 0, box_y = 0;
  2656.         bool handled_scroll = false;
  2657.  
  2658.         /* TODO: invert order; visit deepest box first */
  2659.  
  2660.         while ((next = box_at_point(box, x, y, &box_x, &box_y)) != NULL) {
  2661.                 box = next;
  2662.  
  2663.                 if (box->style && css_computed_visibility(box->style) ==
  2664.                                 CSS_VISIBILITY_HIDDEN)
  2665.                         continue;
  2666.  
  2667.                 /* Pass into iframe */
  2668.                 if (box->iframe && browser_window_scroll_at_point(box->iframe,
  2669.                                 x - box_x, y - box_y, scrx, scry) == true)
  2670.                         return true;
  2671.  
  2672.                 /* Pass into object */
  2673.                 if (box->object != NULL && content_scroll_at_point(
  2674.                                 box->object, x - box_x, y - box_y,
  2675.                                 scrx, scry) == true)
  2676.                         return true;
  2677.  
  2678.                 /* Handle box scrollbars */
  2679.                 if (box->scroll_y && scrollbar_scroll(box->scroll_y, scry))
  2680.                         handled_scroll = true;
  2681.  
  2682.                 if (box->scroll_x && scrollbar_scroll(box->scroll_x, scrx))
  2683.                         handled_scroll = true;
  2684.  
  2685.                 if (handled_scroll == true)
  2686.                         return true;
  2687.         }
  2688.  
  2689.         return false;
  2690. }
  2691.  
  2692.  
  2693. /**
  2694.  * Drop a file onto a content at a particular point, or determine if a file
  2695.  * may be dropped onto the content at given point.
  2696.  *
  2697.  * \param c     html content to look inside
  2698.  * \param x     x-coordinate of point of interest
  2699.  * \param y     y-coordinate of point of interest
  2700.  * \param file  path to file to be dropped, or NULL to know if drop allowed
  2701.  * \return true iff file drop has been handled, or if drop possible (NULL file)
  2702.  */
  2703. static bool html_drop_file_at_point(struct content *c, int x, int y, char *file)
  2704. {
  2705.         html_content *html = (html_content *) c;
  2706.  
  2707.         struct box *box = html->layout;
  2708.         struct box *next;
  2709.         struct box *file_box = NULL;
  2710.         struct box *text_box = NULL;
  2711.         int box_x = 0, box_y = 0;
  2712.  
  2713.         /* Scan box tree for boxes that can handle drop */
  2714.         while ((next = box_at_point(box, x, y, &box_x, &box_y)) != NULL) {
  2715.                 box = next;
  2716.  
  2717.                 if (box->style && css_computed_visibility(box->style) ==
  2718.                                 CSS_VISIBILITY_HIDDEN)
  2719.                         continue;
  2720.  
  2721.                 if (box->iframe)
  2722.                         return browser_window_drop_file_at_point(box->iframe,
  2723.                                         x - box_x, y - box_y, file);
  2724.  
  2725.                 if (box->object && content_drop_file_at_point(box->object,
  2726.                                         x - box_x, y - box_y, file) == true)
  2727.                         return true;
  2728.  
  2729.                 if (box->gadget) {
  2730.                         switch (box->gadget->type) {
  2731.                                 case GADGET_FILE:
  2732.                                         file_box = box;
  2733.                                 break;
  2734.  
  2735.                                 case GADGET_TEXTBOX:
  2736.                                 case GADGET_TEXTAREA:
  2737.                                 case GADGET_PASSWORD:
  2738.                                         text_box = box;
  2739.                                         break;
  2740.  
  2741.                                 default:        /* appease compiler */
  2742.                                         break;
  2743.                         }
  2744.                 }
  2745.         }
  2746.  
  2747.         if (!file_box && !text_box)
  2748.                 /* No box capable of handling drop */
  2749.                 return false;
  2750.  
  2751.         if (file == NULL)
  2752.                 /* There is a box capable of handling drop here */
  2753.                 return true;
  2754.  
  2755.         /* Handle the drop */
  2756.         if (file_box) {
  2757.                 /* File dropped on file input */
  2758.                 utf8_convert_ret ret;
  2759.                 char *utf8_fn;
  2760.  
  2761.                 ret = utf8_from_local_encoding(file, 0,
  2762.                                 &utf8_fn);
  2763.                 if (ret != UTF8_CONVERT_OK) {
  2764.                         /* A bad encoding should never happen */
  2765.                         assert(ret != UTF8_CONVERT_BADENC);
  2766.                         LOG(("utf8_from_local_encoding failed"));
  2767.                         /* Load was for us - just no memory */
  2768.                         return true;
  2769.                 }
  2770.  
  2771.                 /* Found: update form input */
  2772.                 free(file_box->gadget->value);
  2773.                 file_box->gadget->value = utf8_fn;
  2774.  
  2775.                 /* Redraw box. */
  2776.                 html__redraw_a_box(html, file_box);
  2777.  
  2778.         } else if (html->bw != NULL) {
  2779.                 /* File dropped on text input */
  2780.  
  2781.                 size_t file_len;
  2782.                 FILE *fp = NULL;
  2783.                 char *buffer;
  2784.                 char *utf8_buff;
  2785.                 utf8_convert_ret ret;
  2786.                 unsigned int size;
  2787.                 struct browser_window *bw;
  2788.  
  2789.                 /* Open file */
  2790.                 fp = fopen(file, "rb");
  2791.                 if (fp == NULL) {
  2792.                         /* Couldn't open file, but drop was for us */
  2793.                         return true;
  2794.                 }
  2795.  
  2796.                 /* Get filesize */
  2797.                 fseek(fp, 0, SEEK_END);
  2798.                 file_len = ftell(fp);
  2799.                 fseek(fp, 0, SEEK_SET);
  2800.  
  2801.                 /* Allocate buffer for file data */
  2802.                 buffer = malloc(file_len + 1);
  2803.                 if (buffer == NULL) {
  2804.                         /* No memory, but drop was for us */
  2805.                         fclose(fp);
  2806.                         return true;
  2807.                 }
  2808.  
  2809.                 /* Stick file into buffer */
  2810.                 if (file_len != fread(buffer, 1, file_len, fp)) {
  2811.                         /* Failed, but drop was for us */
  2812.                         free(buffer);
  2813.                         fclose(fp);
  2814.                         return true;
  2815.                 }
  2816.  
  2817.                 /* Done with file */
  2818.                 fclose(fp);
  2819.  
  2820.                 /* Ensure buffer's string termination */
  2821.                 buffer[file_len] = '\0';
  2822.  
  2823.                 /* TODO: Sniff for text? */
  2824.  
  2825.                 /* Convert to UTF-8 */
  2826.                 ret = utf8_from_local_encoding(buffer, file_len, &utf8_buff);
  2827.                 if (ret != UTF8_CONVERT_OK) {
  2828.                         /* bad encoding shouldn't happen */
  2829.                         assert(ret != UTF8_CONVERT_BADENC);
  2830.                         LOG(("utf8_from_local_encoding failed"));
  2831.                         free(buffer);
  2832.                         warn_user("NoMemory", NULL);
  2833.                         return true;
  2834.                 }
  2835.  
  2836.                 /* Done with buffer */
  2837.                 free(buffer);
  2838.  
  2839.                 /* Get new length */
  2840.                 size = strlen(utf8_buff);
  2841.  
  2842.                 /* Simulate a click over the input box, to place caret */
  2843.                 browser_window_mouse_click(html->bw,
  2844.                                 BROWSER_MOUSE_PRESS_1, x, y);
  2845.  
  2846.                 bw = browser_window_get_root(html->bw);
  2847.  
  2848.                 /* Paste the file as text */
  2849.                 browser_window_paste_text(bw, utf8_buff, size, true);
  2850.  
  2851.                 free(utf8_buff);
  2852.         }
  2853.  
  2854.         return true;
  2855. }
  2856.  
  2857.  
  2858. /**
  2859.  * Dump debug info concerning the html_content
  2860.  *
  2861.  * \param  bw    The browser window
  2862.  * \param  bw    The file to dump to
  2863.  */
  2864. static void html_debug_dump(struct content *c, FILE *f)
  2865. {
  2866.         html_content *html = (html_content *) c;
  2867.  
  2868.         assert(html != NULL);
  2869.         assert(html->layout != NULL);
  2870.  
  2871.         box_dump(f, html->layout, 0);
  2872. }
  2873.  
  2874.  
  2875. /**
  2876.  * Set an HTML content's search context
  2877.  *
  2878.  * \param c     content of type html
  2879.  * \param s     search context, or NULL if none
  2880.  */
  2881.  
  2882. void html_set_search(struct content *c, struct search_context *s)
  2883. {
  2884.         html_content *html = (html_content *) c;
  2885.  
  2886.         html->search = s;
  2887. }
  2888.  
  2889.  
  2890. /**
  2891.  * Return an HTML content's search context
  2892.  *
  2893.  * \param c     content of type html
  2894.  * \return content's search context, or NULL if none
  2895.  */
  2896.  
  2897. struct search_context *html_get_search(struct content *c)
  2898. {
  2899.         html_content *html = (html_content *) c;
  2900.  
  2901.         return html->search;
  2902. }
  2903.  
  2904.  
  2905. #if ALWAYS_DUMP_FRAMESET
  2906. /**
  2907.  * Print a frameset tree to stderr.
  2908.  */
  2909.  
  2910. static void
  2911. html_dump_frameset(struct content_html_frames *frame, unsigned int depth)
  2912. {
  2913.         unsigned int i;
  2914.         int row, col, index;
  2915.         const char *unit[] = {"px", "%", "*"};
  2916.         const char *scrolling[] = {"auto", "yes", "no"};
  2917.  
  2918.         assert(frame);
  2919.  
  2920.         fprintf(stderr, "%p ", frame);
  2921.  
  2922.         fprintf(stderr, "(%i %i) ", frame->rows, frame->cols);
  2923.  
  2924.         fprintf(stderr, "w%g%s ", frame->width.value, unit[frame->width.unit]);
  2925.         fprintf(stderr, "h%g%s ", frame->height.value,unit[frame->height.unit]);
  2926.         fprintf(stderr, "(margin w%i h%i) ",
  2927.                         frame->margin_width, frame->margin_height);
  2928.  
  2929.         if (frame->name)
  2930.                 fprintf(stderr, "'%s' ", frame->name);
  2931.         if (frame->url)
  2932.                 fprintf(stderr, "<%s> ", frame->url);
  2933.  
  2934.         if (frame->no_resize)
  2935.                 fprintf(stderr, "noresize ");
  2936.         fprintf(stderr, "(scrolling %s) ", scrolling[frame->scrolling]);
  2937.         if (frame->border)
  2938.                 fprintf(stderr, "border %x ",
  2939.                                 (unsigned int) frame->border_colour);
  2940.  
  2941.         fprintf(stderr, "\n");
  2942.  
  2943.         if (frame->children) {
  2944.                 for (row = 0; row != frame->rows; row++) {
  2945.                         for (col = 0; col != frame->cols; col++) {
  2946.                                 for (i = 0; i != depth; i++)
  2947.                                         fprintf(stderr, "  ");
  2948.                                 fprintf(stderr, "(%i %i): ", row, col);
  2949.                                 index = (row * frame->cols) + col;
  2950.                                 html_dump_frameset(&frame->children[index],
  2951.                                                 depth + 1);
  2952.                         }
  2953.                 }
  2954.         }
  2955. }
  2956.  
  2957. #endif
  2958.  
  2959. /**
  2960.  * Retrieve HTML document tree
  2961.  *
  2962.  * \param h  HTML content to retrieve document tree from
  2963.  * \return Pointer to document tree
  2964.  */
  2965. dom_document *html_get_document(hlcache_handle *h)
  2966. {
  2967.         html_content *c = (html_content *) hlcache_handle_get_content(h);
  2968.  
  2969.         assert(c != NULL);
  2970.  
  2971.         return c->document;
  2972. }
  2973.  
  2974. /**
  2975.  * Retrieve box tree
  2976.  *
  2977.  * \param h  HTML content to retrieve tree from
  2978.  * \return Pointer to box tree
  2979.  *
  2980.  * \todo This API must die, as must all use of the box tree outside render/
  2981.  */
  2982. struct box *html_get_box_tree(hlcache_handle *h)
  2983. {
  2984.         html_content *c = (html_content *) hlcache_handle_get_content(h);
  2985.  
  2986.         assert(c != NULL);
  2987.  
  2988.         return c->layout;
  2989. }
  2990.  
  2991. /**
  2992.  * Retrieve the charset of an HTML document
  2993.  *
  2994.  * \param h  Content to retrieve charset from
  2995.  * \return Pointer to charset, or NULL
  2996.  */
  2997. const char *html_get_encoding(hlcache_handle *h)
  2998. {
  2999.         html_content *c = (html_content *) hlcache_handle_get_content(h);
  3000.  
  3001.         assert(c != NULL);
  3002.  
  3003.         return c->encoding;
  3004. }
  3005.  
  3006. /**
  3007.  * Retrieve the charset of an HTML document
  3008.  *
  3009.  * \param h  Content to retrieve charset from
  3010.  * \return Pointer to charset, or NULL
  3011.  */
  3012. dom_hubbub_encoding_source html_get_encoding_source(hlcache_handle *h)
  3013. {
  3014.         html_content *c = (html_content *) hlcache_handle_get_content(h);
  3015.  
  3016.         assert(c != NULL);
  3017.  
  3018.         return c->encoding_source;
  3019. }
  3020.  
  3021. /**
  3022.  * Retrieve framesets used in an HTML document
  3023.  *
  3024.  * \param h  Content to inspect
  3025.  * \return Pointer to framesets, or NULL if none
  3026.  */
  3027. struct content_html_frames *html_get_frameset(hlcache_handle *h)
  3028. {
  3029.         html_content *c = (html_content *) hlcache_handle_get_content(h);
  3030.  
  3031.         assert(c != NULL);
  3032.  
  3033.         return c->frameset;
  3034. }
  3035.  
  3036. /**
  3037.  * Retrieve iframes used in an HTML document
  3038.  *
  3039.  * \param h  Content to inspect
  3040.  * \return Pointer to iframes, or NULL if none
  3041.  */
  3042. struct content_html_iframe *html_get_iframe(hlcache_handle *h)
  3043. {
  3044.         html_content *c = (html_content *) hlcache_handle_get_content(h);
  3045.  
  3046.         assert(c != NULL);
  3047.  
  3048.         return c->iframe;
  3049. }
  3050.  
  3051. /**
  3052.  * Retrieve an HTML content's base URL
  3053.  *
  3054.  * \param h  Content to retrieve base target from
  3055.  * \return Pointer to URL
  3056.  */
  3057. nsurl *html_get_base_url(hlcache_handle *h)
  3058. {
  3059.         html_content *c = (html_content *) hlcache_handle_get_content(h);
  3060.  
  3061.         assert(c != NULL);
  3062.  
  3063.         return c->base_url;
  3064. }
  3065.  
  3066. /**
  3067.  * Retrieve an HTML content's base target
  3068.  *
  3069.  * \param h  Content to retrieve base target from
  3070.  * \return Pointer to target, or NULL if none
  3071.  */
  3072. const char *html_get_base_target(hlcache_handle *h)
  3073. {
  3074.         html_content *c = (html_content *) hlcache_handle_get_content(h);
  3075.  
  3076.         assert(c != NULL);
  3077.  
  3078.         return c->base_target;
  3079. }
  3080.  
  3081. /**
  3082.  * Retrieve stylesheets used by HTML document
  3083.  *
  3084.  * \param h  Content to retrieve stylesheets from
  3085.  * \param n  Pointer to location to receive number of sheets
  3086.  * \return Pointer to array of stylesheets
  3087.  */
  3088. struct html_stylesheet *html_get_stylesheets(hlcache_handle *h, unsigned int *n)
  3089. {
  3090.         html_content *c = (html_content *) hlcache_handle_get_content(h);
  3091.  
  3092.         assert(c != NULL);
  3093.         assert(n != NULL);
  3094.  
  3095.         *n = c->stylesheet_count;
  3096.  
  3097.         return c->stylesheets;
  3098. }
  3099.  
  3100. /**
  3101.  * Retrieve objects used by HTML document
  3102.  *
  3103.  * \param h  Content to retrieve objects from
  3104.  * \param n  Pointer to location to receive number of objects
  3105.  * \return Pointer to list of objects
  3106.  */
  3107. struct content_html_object *html_get_objects(hlcache_handle *h, unsigned int *n)
  3108. {
  3109.         html_content *c = (html_content *) hlcache_handle_get_content(h);
  3110.  
  3111.         assert(c != NULL);
  3112.         assert(n != NULL);
  3113.  
  3114.         *n = c->num_objects;
  3115.  
  3116.         return c->object_list;
  3117. }
  3118.  
  3119. /**
  3120.  * Retrieve layout coordinates of box with given id
  3121.  *
  3122.  * \param h        HTML document to search
  3123.  * \param frag_id  String containing an element id
  3124.  * \param x        Updated to global x coord iff id found
  3125.  * \param y        Updated to global y coord iff id found
  3126.  * \return  true iff id found
  3127.  */
  3128. bool html_get_id_offset(hlcache_handle *h, lwc_string *frag_id, int *x, int *y)
  3129. {
  3130.         struct box *pos;
  3131.         struct box *layout;
  3132.  
  3133.         if (content_get_type(h) != CONTENT_HTML)
  3134.                 return false;
  3135.  
  3136.         layout = html_get_box_tree(h);
  3137.  
  3138.         if ((pos = box_find_by_id(layout, frag_id)) != 0) {
  3139.                 box_coords(pos, x, y);
  3140.                 return true;
  3141.         }
  3142.         return false;
  3143. }
  3144.  
  3145. /**
  3146.  * Compute the type of a content
  3147.  *
  3148.  * \return CONTENT_HTML
  3149.  */
  3150. static content_type html_content_type(void)
  3151. {
  3152.         return CONTENT_HTML;
  3153. }
  3154.  
  3155.  
  3156. static void html_fini(void)
  3157. {
  3158.         box_construct_fini();
  3159.  
  3160.         if (html_user_stylesheet_url != NULL) {
  3161.                 nsurl_unref(html_user_stylesheet_url);
  3162.                 html_user_stylesheet_url = NULL;
  3163.         }
  3164.  
  3165.         if (html_quirks_stylesheet_url != NULL) {
  3166.                 nsurl_unref(html_quirks_stylesheet_url);
  3167.                 html_quirks_stylesheet_url = NULL;
  3168.         }
  3169.  
  3170.         if (html_adblock_stylesheet_url != NULL) {
  3171.                 nsurl_unref(html_adblock_stylesheet_url);
  3172.                 html_adblock_stylesheet_url = NULL;
  3173.         }
  3174.  
  3175.         if (html_default_stylesheet_url != NULL) {
  3176.                 nsurl_unref(html_default_stylesheet_url);
  3177.                 html_default_stylesheet_url = NULL;
  3178.         }
  3179.  
  3180.         if (html_charset != NULL) {
  3181.                 lwc_string_unref(html_charset);
  3182.                 html_charset = NULL;
  3183.         }
  3184. }
  3185.  
  3186. static const content_handler html_content_handler = {
  3187.         .fini = html_fini,
  3188.         .create = html_create,
  3189.         .process_data = html_process_data,
  3190.         .data_complete = html_convert,
  3191.         .reformat = html_reformat,
  3192.         .destroy = html_destroy,
  3193.         .stop = html_stop,
  3194.         .mouse_track = html_mouse_track,
  3195.         .mouse_action = html_mouse_action,
  3196.         .redraw = html_redraw,
  3197.         .open = html_open,
  3198.         .close = html_close,
  3199.         .get_selection = html_get_selection,
  3200.         .get_contextual_content = html_get_contextual_content,
  3201.         .scroll_at_point = html_scroll_at_point,
  3202.         .drop_file_at_point = html_drop_file_at_point,
  3203.         .debug_dump = html_debug_dump,
  3204.         .clone = html_clone,
  3205.         .type = html_content_type,
  3206.         .no_share = true,
  3207. };
  3208.  
  3209. nserror html_init(void)
  3210. {
  3211.         uint32_t i;
  3212.         lwc_error lerror;
  3213.         nserror error;
  3214.  
  3215.         lerror = lwc_intern_string("charset", SLEN("charset"), &html_charset);
  3216.         if (lerror != lwc_error_ok) {
  3217.                 error = NSERROR_NOMEM;
  3218.                 goto error;
  3219.         }
  3220.  
  3221.         error = nsurl_create("resource:default.css",
  3222.                         &html_default_stylesheet_url);
  3223.         if (error != NSERROR_OK)
  3224.                 goto error;
  3225.  
  3226.         error = nsurl_create("resource:adblock.css",
  3227.                         &html_adblock_stylesheet_url);
  3228.         if (error != NSERROR_OK)
  3229.                 goto error;
  3230.  
  3231.         error = nsurl_create("resource:quirks.css",
  3232.                         &html_quirks_stylesheet_url);
  3233.         if (error != NSERROR_OK)
  3234.                 goto error;
  3235.  
  3236.         error = nsurl_create("resource:user.css",
  3237.                         &html_user_stylesheet_url);
  3238.         if (error != NSERROR_OK)
  3239.                 goto error;
  3240.  
  3241.         error = box_construct_init();
  3242.         if (error != NSERROR_OK)
  3243.                 goto error;
  3244.  
  3245.         for (i = 0; i < NOF_ELEMENTS(html_types); i++) {
  3246.                 error = content_factory_register_handler(html_types[i],
  3247.                                 &html_content_handler);
  3248.                 if (error != NSERROR_OK)
  3249.                         goto error;
  3250.         }
  3251.  
  3252.         return NSERROR_OK;
  3253.  
  3254. error:
  3255.         html_fini();
  3256.  
  3257.         return error;
  3258. }
  3259.  
  3260. /**
  3261.  * Get the browser window containing an HTML content
  3262.  *
  3263.  * \param  c    HTML content
  3264.  * \return the browser window
  3265.  */
  3266. struct browser_window *html_get_browser_window(struct content *c)
  3267. {
  3268.         html_content *html = (html_content *) c;
  3269.  
  3270.         assert(c != NULL);
  3271.         assert(c->handler == &html_content_handler);
  3272.  
  3273.         return html->bw;
  3274. }
  3275.