Subversion Repositories Kolibri OS

Rev

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