Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
  3.  * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
  4.  * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk>
  5.  * Copyright 2006 Richard Wilson <info@tinct.net>
  6.  * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
  7.  *
  8.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  9.  *
  10.  * NetSurf is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; version 2 of the License.
  13.  *
  14.  * NetSurf is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  21.  */
  22.  
  23. /** \file
  24.  * Conversion of XML tree to box tree (implementation).
  25.  */
  26.  
  27. #include <assert.h>
  28. #include <ctype.h>
  29. #include <stdio.h>
  30. #include <stdbool.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <strings.h>
  34. #include "utils/config.h"
  35. #include "content/content_protected.h"
  36. #include "css/css.h"
  37. #include "css/utils.h"
  38. #include "css/select.h"
  39. #include "desktop/options.h"
  40. #include "render/box.h"
  41. #include "render/form.h"
  42. #include "render/html_internal.h"
  43. #include "utils/corestrings.h"
  44. #include "utils/locale.h"
  45. #include "utils/log.h"
  46. #include "utils/messages.h"
  47. #include "utils/schedule.h"
  48. #include "utils/talloc.h"
  49. #include "utils/url.h"
  50. #include "utils/utils.h"
  51.  
  52. /**
  53.  * Context for box tree construction
  54.  */
  55. struct box_construct_ctx {
  56.         html_content *content;          /**< Content we're constructing for */
  57.  
  58.         dom_node *n;                    /**< Current node to process */
  59.  
  60.         struct box *root_box;           /**< Root box in the tree */
  61.  
  62.         box_construct_complete_cb cb;   /**< Callback to invoke on completion */
  63.  
  64.         int *bctx;                      /**< talloc context */
  65. };
  66.  
  67. /**
  68.  * Transient properties for construction of current node
  69.  */
  70. struct box_construct_props {
  71.         /** Style from which to inherit, or NULL if none */
  72.         const css_computed_style *parent_style;
  73.         /** Current link target, or NULL if none */
  74.         nsurl *href;
  75.         /** Current frame target, or NULL if none */
  76.         const char *target;
  77.         /** Current title attribute, or NULL if none */
  78.         const char *title;
  79.         /** Identity of the current block-level container */
  80.         struct box *containing_block;
  81.         /** Current container for inlines, or NULL if none
  82.          * \note If non-NULL, will be the last child of containing_block */
  83.         struct box *inline_container;
  84.         /** Whether the current node is the root of the DOM tree */
  85.         bool node_is_root;
  86. };
  87.  
  88. static const content_type image_types = CONTENT_IMAGE;
  89.  
  90. /* the strings are not important, since we just compare the pointers */
  91. const char *TARGET_SELF = "_self";
  92. const char *TARGET_PARENT = "_parent";
  93. const char *TARGET_TOP = "_top";
  94. const char *TARGET_BLANK = "_blank";
  95.  
  96. static void convert_xml_to_box(struct box_construct_ctx *ctx);
  97. static bool box_construct_element(struct box_construct_ctx *ctx,
  98.                 bool *convert_children);
  99. static void box_construct_element_after(dom_node *n, html_content *content);
  100. static bool box_construct_text(struct box_construct_ctx *ctx);
  101. static css_select_results * box_get_style(html_content *c,
  102.                 const css_computed_style *parent_style, dom_node *n);
  103. static void box_text_transform(char *s, unsigned int len,
  104.                 enum css_text_transform_e tt);
  105. #define BOX_SPECIAL_PARAMS dom_node *n, html_content *content, \
  106.                 struct box *box, bool *convert_children
  107. static bool box_a(BOX_SPECIAL_PARAMS);
  108. static bool box_body(BOX_SPECIAL_PARAMS);
  109. static bool box_br(BOX_SPECIAL_PARAMS);
  110. static bool box_image(BOX_SPECIAL_PARAMS);
  111. static bool box_textarea(BOX_SPECIAL_PARAMS);
  112. static bool box_select(BOX_SPECIAL_PARAMS);
  113. static bool box_input(BOX_SPECIAL_PARAMS);
  114. static bool box_input_text(BOX_SPECIAL_PARAMS, bool password);
  115. static bool box_button(BOX_SPECIAL_PARAMS);
  116. static bool box_frameset(BOX_SPECIAL_PARAMS);
  117. static bool box_create_frameset(struct content_html_frames *f, dom_node *n,
  118.                 html_content *content);
  119. static bool box_select_add_option(struct form_control *control, dom_node *n);
  120. static bool box_noscript(BOX_SPECIAL_PARAMS);
  121. static bool box_object(BOX_SPECIAL_PARAMS);
  122. static bool box_embed(BOX_SPECIAL_PARAMS);
  123. static bool box_pre(BOX_SPECIAL_PARAMS);
  124. static bool box_iframe(BOX_SPECIAL_PARAMS);
  125. static bool box_get_attribute(dom_node *n, const char *attribute,
  126.                 void *context, char **value);
  127. static struct frame_dimension *box_parse_multi_lengths(const char *s,
  128.                 unsigned int *count);
  129.  
  130. /* element_table must be sorted by name */
  131. struct element_entry {
  132.         char name[10];   /* element type */
  133.         bool (*convert)(BOX_SPECIAL_PARAMS);
  134. };
  135. static const struct element_entry element_table[] = {
  136.         {"a", box_a},
  137.         {"body", box_body},
  138.         {"br", box_br},
  139.         {"button", box_button},
  140.         {"embed", box_embed},
  141.         {"frameset", box_frameset},
  142.         {"iframe", box_iframe},
  143.         {"image", box_image},
  144.         {"img", box_image},
  145.         {"input", box_input},
  146.         {"noscript", box_noscript},
  147.         {"object", box_object},
  148.         {"pre", box_pre},
  149.         {"select", box_select},
  150.         {"textarea", box_textarea}
  151. };
  152. #define ELEMENT_TABLE_COUNT (sizeof(element_table) / sizeof(element_table[0]))
  153.  
  154. /**
  155.  * Construct a box tree from an xml tree and stylesheets.
  156.  *
  157.  * \param n   xml tree
  158.  * \param c   content of type CONTENT_HTML to construct box tree in
  159.  * \param cb  callback to report conversion completion
  160.  * \return    netsurf error code indicating status of call
  161.  */
  162.  
  163. nserror dom_to_box(dom_node *n, html_content *c, box_construct_complete_cb cb)
  164. {
  165.         struct box_construct_ctx *ctx;
  166.  
  167.         if (c->bctx == NULL) {
  168.                 /* create a context allocation for this box tree */
  169.                 c->bctx = talloc_zero(0, int);
  170.                 if (c->bctx == NULL) {
  171.                         return NSERROR_NOMEM;
  172.                 }
  173.         }
  174.  
  175.         ctx = malloc(sizeof(*ctx));
  176.         if (ctx == NULL) {
  177.                 return NSERROR_NOMEM;
  178.         }
  179.  
  180.         ctx->content = c;
  181.         ctx->n = dom_node_ref(n);
  182.         ctx->root_box = NULL;
  183.         ctx->cb = cb;
  184.         ctx->bctx = c->bctx;
  185.  
  186.         schedule(0, (schedule_callback_fn) convert_xml_to_box, ctx);
  187.  
  188.         return NSERROR_OK;
  189. }
  190.  
  191. /* mapping from CSS display to box type
  192.  * this table must be in sync with libcss' css_display enum */
  193. static const box_type box_map[] = {
  194.         0, /*CSS_DISPLAY_INHERIT,*/
  195.         BOX_INLINE, /*CSS_DISPLAY_INLINE,*/
  196.         BOX_BLOCK, /*CSS_DISPLAY_BLOCK,*/
  197.         BOX_BLOCK, /*CSS_DISPLAY_LIST_ITEM,*/
  198.         BOX_INLINE, /*CSS_DISPLAY_RUN_IN,*/
  199.         BOX_INLINE_BLOCK, /*CSS_DISPLAY_INLINE_BLOCK,*/
  200.         BOX_TABLE, /*CSS_DISPLAY_TABLE,*/
  201.         BOX_TABLE, /*CSS_DISPLAY_INLINE_TABLE,*/
  202.         BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_ROW_GROUP,*/
  203.         BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_HEADER_GROUP,*/
  204.         BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_FOOTER_GROUP,*/
  205.         BOX_TABLE_ROW, /*CSS_DISPLAY_TABLE_ROW,*/
  206.         BOX_NONE, /*CSS_DISPLAY_TABLE_COLUMN_GROUP,*/
  207.         BOX_NONE, /*CSS_DISPLAY_TABLE_COLUMN,*/
  208.         BOX_TABLE_CELL, /*CSS_DISPLAY_TABLE_CELL,*/
  209.         BOX_INLINE, /*CSS_DISPLAY_TABLE_CAPTION,*/
  210.         BOX_NONE /*CSS_DISPLAY_NONE*/
  211. };
  212.  
  213. /** Key for box userdata on DOM elements (== '__ns_box') */
  214. static dom_string *kstr_box_key;
  215. static dom_string *kstr_title;
  216. static dom_string *kstr_id;
  217. static dom_string *kstr_colspan;
  218. static dom_string *kstr_rowspan;
  219. static dom_string *kstr_style;
  220. static dom_string *kstr_href;
  221. static dom_string *kstr_name;
  222. static dom_string *kstr_target;
  223. static dom_string *kstr_alt;
  224. static dom_string *kstr_src;
  225. static dom_string *kstr_codebase;
  226. static dom_string *kstr_classid;
  227. static dom_string *kstr_data;
  228. static dom_string *kstr_rows;
  229. static dom_string *kstr_cols;
  230. static dom_string *kstr_border;
  231. static dom_string *kstr_frameborder;
  232. static dom_string *kstr_bordercolor;
  233. static dom_string *kstr_noresize;
  234. static dom_string *kstr_scrolling;
  235. static dom_string *kstr_marginwidth;
  236. static dom_string *kstr_marginheight;
  237. static dom_string *kstr_type;
  238. static dom_string *kstr_value;
  239. static dom_string *kstr_selected;
  240.  
  241. nserror box_construct_init(void)
  242. {
  243.         dom_exception err;
  244.  
  245.         err = dom_string_create_interned((const uint8_t *) "__ns_box",
  246.                                 SLEN("__ns_box"), &kstr_box_key);
  247.         if (err != DOM_NO_ERR || kstr_box_key == NULL)
  248.                 goto error;
  249.  
  250. #define BOX_CONSTRUCT_STRING_INTERN(NAME)                               \
  251.         err = dom_string_create_interned((const uint8_t *)#NAME,        \
  252.                                          sizeof(#NAME) - 1,             \
  253.                                          &kstr_##NAME );                \
  254.         if ((err != DOM_NO_ERR) || (kstr_##NAME == NULL))               \
  255.                 goto error                                             
  256.  
  257.         BOX_CONSTRUCT_STRING_INTERN(title);
  258.         BOX_CONSTRUCT_STRING_INTERN(id);
  259.         BOX_CONSTRUCT_STRING_INTERN(colspan);
  260.         BOX_CONSTRUCT_STRING_INTERN(rowspan);
  261.         BOX_CONSTRUCT_STRING_INTERN(style);
  262.         BOX_CONSTRUCT_STRING_INTERN(href);
  263.         BOX_CONSTRUCT_STRING_INTERN(name);
  264.         BOX_CONSTRUCT_STRING_INTERN(target);
  265.         BOX_CONSTRUCT_STRING_INTERN(alt);
  266.         BOX_CONSTRUCT_STRING_INTERN(src);
  267.         BOX_CONSTRUCT_STRING_INTERN(codebase);
  268.         BOX_CONSTRUCT_STRING_INTERN(classid);
  269.         BOX_CONSTRUCT_STRING_INTERN(data);
  270.         BOX_CONSTRUCT_STRING_INTERN(rows);
  271.         BOX_CONSTRUCT_STRING_INTERN(cols);
  272.         BOX_CONSTRUCT_STRING_INTERN(border);
  273.         BOX_CONSTRUCT_STRING_INTERN(frameborder);
  274.         BOX_CONSTRUCT_STRING_INTERN(bordercolor);
  275.         BOX_CONSTRUCT_STRING_INTERN(noresize);
  276.         BOX_CONSTRUCT_STRING_INTERN(scrolling);
  277.         BOX_CONSTRUCT_STRING_INTERN(marginwidth);
  278.         BOX_CONSTRUCT_STRING_INTERN(marginheight);
  279.         BOX_CONSTRUCT_STRING_INTERN(type);
  280.         BOX_CONSTRUCT_STRING_INTERN(value);
  281.         BOX_CONSTRUCT_STRING_INTERN(selected);
  282.  
  283. #undef BOX_CONSTRUCT_STRING_INTERN
  284.  
  285.         return NSERROR_OK;
  286.  
  287. error:
  288.         return NSERROR_NOMEM;
  289. }
  290.  
  291. void box_construct_fini(void)
  292. {
  293.         if (kstr_box_key != NULL) {
  294.                 dom_string_unref(kstr_box_key);
  295.                 kstr_box_key = NULL;
  296.         }
  297.  
  298. #define BOX_CONSTRUCT_STRING_UNREF(NAME)                                \
  299.         do {                                                            \
  300.                 if (kstr_##NAME != NULL) {                              \
  301.                         dom_string_unref(kstr_##NAME);                  \
  302.                         kstr_##NAME = NULL;                             \
  303.                 }                                                       \
  304.         } while (0)                                                     \
  305.  
  306.         BOX_CONSTRUCT_STRING_UNREF(title);
  307.         BOX_CONSTRUCT_STRING_UNREF(id);
  308.         BOX_CONSTRUCT_STRING_UNREF(colspan);
  309.         BOX_CONSTRUCT_STRING_UNREF(rowspan);
  310.         BOX_CONSTRUCT_STRING_UNREF(style);
  311.         BOX_CONSTRUCT_STRING_UNREF(href);
  312.         BOX_CONSTRUCT_STRING_UNREF(name);
  313.         BOX_CONSTRUCT_STRING_UNREF(target);
  314.         BOX_CONSTRUCT_STRING_UNREF(alt);
  315.         BOX_CONSTRUCT_STRING_UNREF(src);
  316.         BOX_CONSTRUCT_STRING_UNREF(codebase);
  317.         BOX_CONSTRUCT_STRING_UNREF(classid);
  318.         BOX_CONSTRUCT_STRING_UNREF(data);
  319.         BOX_CONSTRUCT_STRING_UNREF(rows);
  320.         BOX_CONSTRUCT_STRING_UNREF(cols);
  321.         BOX_CONSTRUCT_STRING_UNREF(border);
  322.         BOX_CONSTRUCT_STRING_UNREF(frameborder);
  323.         BOX_CONSTRUCT_STRING_UNREF(bordercolor);
  324.         BOX_CONSTRUCT_STRING_UNREF(noresize);
  325.         BOX_CONSTRUCT_STRING_UNREF(scrolling);
  326.         BOX_CONSTRUCT_STRING_UNREF(marginwidth);
  327.         BOX_CONSTRUCT_STRING_UNREF(marginheight);
  328.         BOX_CONSTRUCT_STRING_UNREF(type);
  329.         BOX_CONSTRUCT_STRING_UNREF(value);
  330.         BOX_CONSTRUCT_STRING_UNREF(selected);
  331.  
  332. #undef BOX_CONSTRUCT_DOM_STRING_UNREF
  333. }
  334.  
  335. static inline struct box *box_for_node(dom_node *n)
  336. {
  337.         struct box *box = NULL;
  338.         dom_exception err;
  339.  
  340.         err = dom_node_get_user_data(n, kstr_box_key, (void *) &box);
  341.         if (err != DOM_NO_ERR)
  342.                 return NULL;
  343.  
  344.         return box;
  345. }
  346.  
  347. static inline bool box_is_root(dom_node *n)
  348. {
  349.         dom_node *parent;
  350.         dom_node_type type;
  351.         dom_exception err;
  352.  
  353.         err = dom_node_get_parent_node(n, &parent);
  354.         if (err != DOM_NO_ERR)
  355.                 return false;
  356.  
  357.         if (parent != NULL) {
  358.                 err = dom_node_get_node_type(parent, &type);
  359.  
  360.                 dom_node_unref(parent);
  361.  
  362.                 if (err != DOM_NO_ERR)
  363.                         return false;
  364.  
  365.                 if (type != DOM_DOCUMENT_NODE)
  366.                         return false;
  367.         }
  368.  
  369.         return true;
  370. }
  371.  
  372. /**
  373.  * Find the next node in the DOM tree, completing
  374.  * element construction where appropriate.
  375.  *
  376.  * \param n                 Current node
  377.  * \param content           Containing content
  378.  * \param convert_children  Whether to consider children of \a n
  379.  * \return Next node to process, or NULL if complete
  380.  *
  381.  * \note \a n will be unreferenced
  382.  */
  383. static dom_node *next_node(dom_node *n, html_content *content,
  384.                 bool convert_children)
  385. {
  386.         dom_node *next = NULL;
  387.         bool has_children;
  388.         dom_exception err;
  389.  
  390.         err = dom_node_has_child_nodes(n, &has_children);
  391.         if (err != DOM_NO_ERR) {
  392.                 dom_node_unref(n);
  393.                 return NULL;
  394.         }
  395.  
  396.         if (convert_children && has_children) {
  397.                 err = dom_node_get_first_child(n, &next);
  398.                 if (err != DOM_NO_ERR) {
  399.                         dom_node_unref(n);
  400.                         return NULL;
  401.                 }
  402.                 dom_node_unref(n);
  403.         } else {
  404.                 err = dom_node_get_next_sibling(n, &next);
  405.                 if (err != DOM_NO_ERR) {
  406.                         dom_node_unref(n);
  407.                         return NULL;
  408.                 }
  409.  
  410.                 if (next != NULL) {
  411.                         if (box_for_node(n) != NULL)
  412.                                 box_construct_element_after(n, content);
  413.                         dom_node_unref(n);
  414.                 } else {
  415.                         if (box_for_node(n) != NULL)
  416.                                 box_construct_element_after(n, content);
  417.  
  418.                         while (box_is_root(n) == false) {
  419.                                 dom_node *parent = NULL;
  420.                                 dom_node *parent_next = NULL;
  421.  
  422.                                 err = dom_node_get_parent_node(n, &parent);
  423.                                 if (err != DOM_NO_ERR) {
  424.                                         dom_node_unref(n);
  425.                                         return NULL;
  426.                                 }
  427.  
  428.                                 assert(parent != NULL);
  429.  
  430.                                 err = dom_node_get_next_sibling(parent,
  431.                                                 &parent_next);
  432.                                 if (err != DOM_NO_ERR) {
  433.                                         dom_node_unref(parent);
  434.                                         dom_node_unref(n);
  435.                                         return NULL;
  436.                                 }
  437.  
  438.                                 if (parent_next != NULL) {
  439.                                         dom_node_unref(parent_next);
  440.                                         dom_node_unref(parent);
  441.                                         break;
  442.                                 }
  443.  
  444.                                 dom_node_unref(n);
  445.                                 n = parent;
  446.                                 parent = NULL;
  447.  
  448.                                 if (box_for_node(n) != NULL) {
  449.                                         box_construct_element_after(
  450.                                                         n, content);
  451.                                 }
  452.                         }
  453.  
  454.                         if (box_is_root(n) == false) {
  455.                                 dom_node *parent = NULL;
  456.  
  457.                                 err = dom_node_get_parent_node(n, &parent);
  458.                                 if (err != DOM_NO_ERR) {
  459.                                         dom_node_unref(n);
  460.                                         return NULL;
  461.                                 }
  462.  
  463.                                 assert(parent != NULL);
  464.  
  465.                                 err = dom_node_get_next_sibling(parent, &next);
  466.                                 if (err != DOM_NO_ERR) {
  467.                                         dom_node_unref(parent);
  468.                                         dom_node_unref(n);
  469.                                         return NULL;
  470.                                 }
  471.  
  472.                                 if (box_for_node(parent) != NULL) {
  473.                                         box_construct_element_after(parent,
  474.                                                         content);
  475.                                 }
  476.  
  477.                                 dom_node_unref(parent);
  478.                         }
  479.  
  480.                         dom_node_unref(n);
  481.                 }
  482.         }
  483.  
  484.         return next;
  485. }
  486.  
  487. /**
  488.  * Convert an ELEMENT node to a box tree fragment,
  489.  * then schedule conversion of the next ELEMENT node
  490.  */
  491. void convert_xml_to_box(struct box_construct_ctx *ctx)
  492. {
  493.         dom_node *next;
  494.         bool convert_children;
  495.         uint32_t num_processed = 0;
  496.         const uint32_t max_processed_before_yield = 10;
  497.  
  498.         do {
  499.                 convert_children = true;
  500.  
  501.                 assert(ctx->n != NULL);
  502.  
  503.                 if (box_construct_element(ctx, &convert_children) == false) {
  504.                         ctx->cb(ctx->content, false);
  505.                         dom_node_unref(ctx->n);
  506.                         free(ctx);
  507.                         return;
  508.                 }
  509.  
  510.                 /* Find next element to process, converting text nodes as we go */
  511.                 next = next_node(ctx->n, ctx->content, convert_children);
  512.                 while (next != NULL) {
  513.                         dom_node_type type;
  514.                         dom_exception err;
  515.  
  516.                         err = dom_node_get_node_type(next, &type);
  517.                         if (err != DOM_NO_ERR) {
  518.                                 ctx->cb(ctx->content, false);
  519.                                 dom_node_unref(next);
  520.                                 free(ctx);
  521.                                 return;
  522.                         }
  523.  
  524.                         if (type == DOM_ELEMENT_NODE)
  525.                                 break;
  526.  
  527.                         if (type == DOM_TEXT_NODE) {
  528.                                 ctx->n = next;
  529.                                 if (box_construct_text(ctx) == false) {
  530.                                         ctx->cb(ctx->content, false);
  531.                                         dom_node_unref(ctx->n);
  532.                                         free(ctx);
  533.                                         return;
  534.                                 }
  535.                         }
  536.  
  537.                         next = next_node(next, ctx->content, true);
  538.                 }
  539.  
  540.                 ctx->n = next;
  541.  
  542.                 if (next == NULL) {
  543.                         /* Conversion complete */
  544.                         struct box root;
  545.  
  546.                         memset(&root, 0, sizeof(root));
  547.  
  548.                         root.type = BOX_BLOCK;
  549.                         root.children = root.last = ctx->root_box;
  550.                         root.children->parent = &root;
  551.  
  552.                         /** \todo Remove box_normalise_block */
  553.                         if (box_normalise_block(&root, ctx->content) == false) {
  554.                                 ctx->cb(ctx->content, false);
  555.                         } else {
  556.                                 ctx->content->layout = root.children;
  557.                                 ctx->content->layout->parent = NULL;
  558.  
  559.                                 ctx->cb(ctx->content, true);
  560.                         }
  561.  
  562.                         assert(ctx->n == NULL);
  563.  
  564.                         free(ctx);
  565.                         return;
  566.                 }
  567.         } while (++num_processed < max_processed_before_yield);
  568.  
  569.         /* More work to do: schedule a continuation */
  570.         schedule(0, (schedule_callback_fn) convert_xml_to_box, ctx);
  571. }
  572.  
  573. /**
  574.  * Construct a list marker box
  575.  *
  576.  * \param box      Box to attach marker to
  577.  * \param title    Current title attribute
  578.  * \param content  Containing content
  579.  * \param parent   Current block-level container
  580.  * \return True on success, false on memory exhaustion
  581.  */
  582. static bool box_construct_marker(struct box *box, const char *title,
  583.                 struct box_construct_ctx *ctx, struct box *parent)
  584. {
  585.         lwc_string *image_uri;
  586.         struct box *marker;
  587.  
  588.         marker = box_create(NULL, box->style, false, NULL, NULL, title,
  589.                         NULL, ctx->bctx);
  590.         if (marker == false)
  591.                 return false;
  592.  
  593.         marker->type = BOX_BLOCK;
  594.  
  595.         /** \todo marker content (list-style-type) */
  596.         switch (css_computed_list_style_type(box->style)) {
  597.         case CSS_LIST_STYLE_TYPE_DISC:
  598.                 /* 2022 BULLET */
  599.                 marker->text = (char *) "\342\200\242";
  600.                 marker->length = 3;
  601.                 break;
  602.         case CSS_LIST_STYLE_TYPE_CIRCLE:
  603.                 /* 25CB WHITE CIRCLE */
  604.                 marker->text = (char *) "\342\227\213";
  605.                 marker->length = 3;
  606.                 break;
  607.         case CSS_LIST_STYLE_TYPE_SQUARE:
  608.                 /* 25AA BLACK SMALL SQUARE */
  609.                 marker->text = (char *) "\342\226\252";
  610.                 marker->length = 3;
  611.                 break;
  612.         case CSS_LIST_STYLE_TYPE_DECIMAL:
  613.         case CSS_LIST_STYLE_TYPE_LOWER_ALPHA:
  614.         case CSS_LIST_STYLE_TYPE_LOWER_ROMAN:
  615.         case CSS_LIST_STYLE_TYPE_UPPER_ALPHA:
  616.         case CSS_LIST_STYLE_TYPE_UPPER_ROMAN:
  617.         default:
  618.                 if (parent->last) {
  619.                         struct box *last = parent->last;
  620.  
  621.                         /* Drill down into last child of parent
  622.                          * to find the list marker (if any)
  623.                          *
  624.                          * Floated list boxes end up as:
  625.                          *
  626.                          * parent
  627.                          *   BOX_INLINE_CONTAINER
  628.                          *     BOX_FLOAT_{LEFT,RIGHT}
  629.                          *       BOX_BLOCK <-- list box
  630.                          *        ...
  631.                          */
  632.                         while (last != NULL) {
  633.                                 if (last->list_marker != NULL)
  634.                                         break;
  635.  
  636.                                 last = last->last;
  637.                         }
  638.  
  639.                         if (last && last->list_marker) {
  640.                                 marker->rows = last->list_marker->rows + 1;
  641.                         }
  642.                 }
  643.  
  644.                 marker->text = talloc_array(ctx->bctx, char, 20);
  645.                 if (marker->text == NULL)
  646.                         return false;
  647.  
  648.                 snprintf(marker->text, 20, "%u.", marker->rows);
  649.                 marker->length = strlen(marker->text);
  650.                 break;
  651.         case CSS_LIST_STYLE_TYPE_NONE:
  652.                 marker->text = 0;
  653.                 marker->length = 0;
  654.                 break;
  655.         }
  656.  
  657.         if (css_computed_list_style_image(box->style, &image_uri) == CSS_LIST_STYLE_IMAGE_URI &&
  658.             (image_uri != NULL) &&
  659.             (nsoption_bool(foreground_images) == true)) {
  660.                 nsurl *url;
  661.                 nserror error;
  662.  
  663.                 /* TODO: we get a url out of libcss as a lwc string, but
  664.                  *       earlier we already had it as a nsurl after we
  665.                  *       nsurl_joined it.  Can this be improved?
  666.                  *       For now, just making another nsurl. */
  667.                 error = nsurl_create(lwc_string_data(image_uri), &url);
  668.                 if (error != NSERROR_OK)
  669.                         return false;
  670.  
  671.                 if (html_fetch_object(ctx->content, url, marker, image_types,
  672.                                 ctx->content->base.available_width, 1000, false) ==
  673.                                 false) {
  674.                         nsurl_unref(url);
  675.                         return false;
  676.                 }
  677.                 nsurl_unref(url);
  678.         }
  679.  
  680.         box->list_marker = marker;
  681.         marker->parent = box;
  682.  
  683.         return true;
  684. }
  685.  
  686. /**
  687.  * Construct the box required for a generated element.
  688.  *
  689.  * \param n        XML node of type XML_ELEMENT_NODE
  690.  * \param content  Content of type CONTENT_HTML that is being processed
  691.  * \param box      Box which may have generated content
  692.  * \param style    Complete computed style for pseudo element, or NULL
  693.  *
  694.  * TODO:
  695.  * This is currently incomplete. It just does enough to support the clearfix
  696.  * hack. ( http://www.positioniseverything.net/easyclearing.html )
  697.  */
  698. static void box_construct_generate(dom_node *n, html_content *content,
  699.                 struct box *box, const css_computed_style *style)
  700. {
  701.         struct box *gen = NULL;
  702.         const css_computed_content_item *c_item;
  703.  
  704.         /* Nothing to generate if the parent box is not a block */
  705.         if (box->type != BOX_BLOCK)
  706.                 return;
  707.  
  708.         /* To determine if an element has a pseudo element, we select
  709.          * for it and test to see if the returned style's content
  710.          * property is set to normal. */
  711.         if (style == NULL ||
  712.                         css_computed_content(style, &c_item) ==
  713.                         CSS_CONTENT_NORMAL) {
  714.                 /* No pseudo element */
  715.                 return;
  716.         }
  717.  
  718.         /* create box for this element */
  719.         if (css_computed_display(style, box_is_root(n)) == CSS_DISPLAY_BLOCK) {
  720.                 /* currently only support block level elements */
  721.  
  722.                 /** \todo Not wise to drop const from the computed style */
  723.                 gen = box_create(NULL, (css_computed_style *) style,
  724.                                 false, NULL, NULL, NULL, NULL, content->bctx);
  725.                 if (gen == NULL) {
  726.                         return;
  727.                 }
  728.  
  729.                 /* set box type from computed display */
  730.                 gen->type = box_map[css_computed_display(
  731.                                 style, box_is_root(n))];
  732.  
  733.                 box_add_child(box, gen);
  734.         }
  735. }
  736.  
  737. /**
  738.  * Extract transient construction properties
  739.  *
  740.  * \param n      Current DOM node to convert
  741.  * \param props  Property object to populate
  742.  */
  743. static void box_extract_properties(dom_node *n,
  744.                 struct box_construct_props *props)
  745. {
  746.         memset(props, 0, sizeof(*props));
  747.  
  748.         props->node_is_root = box_is_root(n);
  749.  
  750.         /* Extract properties from containing DOM node */
  751.         if (props->node_is_root == false) {
  752.                 dom_node *current_node = n;
  753.                 dom_node *parent_node = NULL;
  754.                 struct box *parent_box;
  755.                 dom_exception err;
  756.  
  757.                 /* Find ancestor node containing parent box */
  758.                 while (true) {
  759.                         err = dom_node_get_parent_node(current_node,
  760.                                         &parent_node);
  761.                         if (err != DOM_NO_ERR || parent_node == NULL)
  762.                                 break;
  763.  
  764.                         parent_box = box_for_node(parent_node);
  765.  
  766.                         if (parent_box != NULL) {
  767.                                 props->parent_style = parent_box->style;
  768.                                 props->href = parent_box->href;
  769.                                 props->target = parent_box->target;
  770.                                 props->title = parent_box->title;
  771.  
  772.                                 dom_node_unref(parent_node);
  773.                                 break;
  774.                         } else {
  775.                                 if (current_node != n)
  776.                                         dom_node_unref(current_node);
  777.                                 current_node = parent_node;
  778.                                 parent_node = NULL;
  779.                         }
  780.                 }
  781.                        
  782.                 /* Find containing block (may be parent) */
  783.                 while (true) {
  784.                         struct box *b;
  785.  
  786.                         err = dom_node_get_parent_node(current_node,
  787.                                         &parent_node);
  788.                         if (err != DOM_NO_ERR || parent_node == NULL) {
  789.                                 if (current_node != n)
  790.                                         dom_node_unref(current_node);
  791.                                 break;
  792.                         }
  793.  
  794.                         if (current_node != n)
  795.                                 dom_node_unref(current_node);
  796.  
  797.                         b = box_for_node(parent_node);
  798.  
  799.                         /* Children of nodes that created an inline box
  800.                          * will generate boxes which are attached as
  801.                          * _siblings_ of the box generated for their
  802.                          * parent node. Note, however, that we'll still
  803.                          * use the parent node's styling as the parent
  804.                          * style, above. */
  805.                         if (b != NULL && b->type != BOX_INLINE &&
  806.                                         b->type != BOX_BR) {
  807.                                 props->containing_block = b;
  808.  
  809.                                 dom_node_unref(parent_node);
  810.                                 break;
  811.                         } else {
  812.                                 current_node = parent_node;
  813.                                 parent_node = NULL;
  814.                         }
  815.                 }
  816.         }
  817.  
  818.         /* Compute current inline container, if any */
  819.         if (props->containing_block != NULL &&
  820.                         props->containing_block->last != NULL &&
  821.                         props->containing_block->last->type ==
  822.                                 BOX_INLINE_CONTAINER)
  823.                 props->inline_container = props->containing_block->last;
  824. }
  825.  
  826. /**
  827.  * Construct the box tree for an XML element.
  828.  *
  829.  * \param ctx               Tree construction context
  830.  * \param convert_children  Whether to convert children
  831.  * \return  true on success, false on memory exhaustion
  832.  */
  833.  
  834. bool box_construct_element(struct box_construct_ctx *ctx,
  835.                 bool *convert_children)
  836. {
  837.         dom_string *title0, *s;
  838.         lwc_string *id = NULL;
  839.         struct box *box = NULL, *old_box;
  840.         css_select_results *styles = NULL;
  841.         struct element_entry *element;
  842.         lwc_string *bgimage_uri;
  843.         dom_exception err;
  844.         struct box_construct_props props;
  845.  
  846.         assert(ctx->n != NULL);
  847.  
  848.         box_extract_properties(ctx->n, &props);
  849.  
  850.         if (props.containing_block != NULL) {
  851.                 /* In case the containing block is a pre block, we clear
  852.                  * the PRE_STRIP flag since it is not used if we follow
  853.                  * the pre with a tag */
  854.                 props.containing_block->flags &= ~PRE_STRIP;
  855.         }
  856.  
  857.         styles = box_get_style(ctx->content, props.parent_style, ctx->n);
  858.         if (styles == NULL)
  859.                 return false;
  860.  
  861.         /* Extract title attribute, if present */
  862.         err = dom_element_get_attribute(ctx->n, kstr_title, &title0);
  863.         if (err != DOM_NO_ERR)
  864.                 return false;
  865.  
  866.         if (title0 != NULL) {
  867.                 char *t = squash_whitespace(dom_string_data(title0));
  868.  
  869.                 dom_string_unref(title0);
  870.  
  871.                 if (t == NULL)
  872.                         return false;
  873.  
  874.                 props.title = talloc_strdup(ctx->bctx, t);
  875.  
  876.                 free(t);
  877.  
  878.                 if (props.title == NULL)
  879.                         return false;
  880.         }
  881.  
  882.         /* Extract id attribute, if present */
  883.         err = dom_element_get_attribute(ctx->n, kstr_id, &s);
  884.         if (err != DOM_NO_ERR)
  885.                 return false;
  886.  
  887.         if (s != NULL) {
  888.                 err = dom_string_intern(s, &id);
  889.                 if (err != DOM_NO_ERR)
  890.                         id = NULL;
  891.  
  892.                 dom_string_unref(s);
  893.         }
  894.  
  895.         box = box_create(styles, styles->styles[CSS_PSEUDO_ELEMENT_NONE], false,
  896.                         props.href, props.target, props.title, id,
  897.                         ctx->bctx);
  898.         if (box == NULL)
  899.                 return false;
  900.  
  901.         /* If this is the root box, add it to the context */
  902.         if (props.node_is_root)
  903.                 ctx->root_box = box;
  904.  
  905.         /* Deal with colspan/rowspan */
  906.         err = dom_element_get_attribute(ctx->n, kstr_colspan, &s);
  907.         if (err != DOM_NO_ERR)
  908.                 return false;
  909.  
  910.         if (s != NULL) {
  911.                 const char *val = dom_string_data(s);
  912.  
  913.                 if ('0' <= val[0] && val[0] <= '9')
  914.                         box->columns = strtol(val, NULL, 10);
  915.  
  916.                 dom_string_unref(s);
  917.         }
  918.  
  919.         err = dom_element_get_attribute(ctx->n, kstr_rowspan, &s);
  920.         if (err != DOM_NO_ERR)
  921.                 return false;
  922.  
  923.         if (s != NULL) {
  924.                 const char *val = dom_string_data(s);
  925.  
  926.                 if ('0' <= val[0] && val[0] <= '9')
  927.                         box->rows = strtol(val, NULL, 10);
  928.  
  929.                 dom_string_unref(s);
  930.         }
  931.  
  932.         /* Set box type from computed display */
  933.         if ((css_computed_position(box->style) == CSS_POSITION_ABSOLUTE ||
  934.                         css_computed_position(box->style) ==
  935.                                         CSS_POSITION_FIXED) &&
  936.                         (css_computed_display_static(box->style) ==
  937.                                         CSS_DISPLAY_INLINE ||
  938.                          css_computed_display_static(box->style) ==
  939.                                         CSS_DISPLAY_INLINE_BLOCK ||
  940.                          css_computed_display_static(box->style) ==
  941.                                         CSS_DISPLAY_INLINE_TABLE)) {
  942.                 /* Special case for absolute positioning: make absolute inlines
  943.                  * into inline block so that the boxes are constructed in an
  944.                  * inline container as if they were not absolutely positioned.
  945.                  * Layout expects and handles this. */
  946.                 box->type = box_map[CSS_DISPLAY_INLINE_BLOCK];
  947.         } else {
  948.                 /* Normal mapping */
  949.                 box->type = box_map[css_computed_display(box->style,
  950.                                 props.node_is_root)];
  951.         }
  952.  
  953.         /* Handle the :before pseudo element */
  954.         box_construct_generate(ctx->n, ctx->content, box,
  955.                         box->styles->styles[CSS_PSEUDO_ELEMENT_BEFORE]);
  956.  
  957.         err = dom_node_get_node_name(ctx->n, &s);
  958.         if (err != DOM_NO_ERR || s == NULL)
  959.                 return false;
  960.  
  961.         /* Special elements */
  962.         element = bsearch(dom_string_data(s), element_table,
  963.                         ELEMENT_TABLE_COUNT, sizeof(element_table[0]),
  964.                         (int (*)(const void *, const void *)) strcasecmp);
  965.  
  966.         dom_string_unref(s);
  967.  
  968.         if (element != NULL) {
  969.                 /* A special convert function exists for this element */
  970.                 if (element->convert(ctx->n, ctx->content, box,
  971.                                 convert_children) == false)
  972.                         return false;
  973.         }
  974.  
  975.         if (box->type == BOX_NONE || css_computed_display(box->style,
  976.                         props.node_is_root) == CSS_DISPLAY_NONE) {
  977.                 css_select_results_destroy(styles);
  978.                 box->styles = NULL;
  979.                 box->style = NULL;
  980.  
  981.                 /* Invalidate associated gadget, if any */
  982.                 if (box->gadget != NULL) {
  983.                         box->gadget->box = NULL;
  984.                         box->gadget = NULL;
  985.                 }
  986.  
  987.                 /* Can't do this, because the lifetimes of boxes and gadgets
  988.                  * are inextricably linked. Fortunately, talloc will save us
  989.                  * (for now) */
  990.                 /* box_free_box(box); */
  991.  
  992.                 *convert_children = false;
  993.  
  994.                 return true;
  995.         }
  996.  
  997.         /* Attach DOM node to box */
  998.         err = dom_node_set_user_data(ctx->n, kstr_box_key, box, NULL,
  999.                         (void *) &old_box);
  1000.         if (err != DOM_NO_ERR)
  1001.                 return false;
  1002.  
  1003.         /* Attach box to DOM node */
  1004.         box->node = dom_node_ref(ctx->n);
  1005.  
  1006.         if (props.inline_container == NULL &&
  1007.                         (box->type == BOX_INLINE ||
  1008.                          box->type == BOX_BR ||
  1009.                          box->type == BOX_INLINE_BLOCK ||
  1010.                          css_computed_float(box->style) == CSS_FLOAT_LEFT ||
  1011.                          css_computed_float(box->style) == CSS_FLOAT_RIGHT)) {
  1012.                 /* Found an inline child of a block without a current container
  1013.                  * (i.e. this box is the first child of its parent, or was
  1014.                  * preceded by block-level siblings) */
  1015.                 assert(props.containing_block != NULL &&
  1016.                                 "Root box must not be inline or floated");
  1017.  
  1018.                 props.inline_container = box_create(NULL, NULL, false, NULL,
  1019.                                 NULL, NULL, NULL, ctx->bctx);
  1020.                 if (props.inline_container == NULL)
  1021.                         return false;
  1022.  
  1023.                 props.inline_container->type = BOX_INLINE_CONTAINER;
  1024.  
  1025.                 box_add_child(props.containing_block, props.inline_container);
  1026.         }
  1027.  
  1028.         /* Kick off fetch for any background image */
  1029.         if (css_computed_background_image(box->style, &bgimage_uri) ==
  1030.                         CSS_BACKGROUND_IMAGE_IMAGE && bgimage_uri != NULL &&
  1031.             nsoption_bool(background_images) == true) {
  1032.                 nsurl *url;
  1033.                 nserror error;
  1034.  
  1035.                 /* TODO: we get a url out of libcss as a lwc string, but
  1036.                  *       earlier we already had it as a nsurl after we
  1037.                  *       nsurl_joined it.  Can this be improved?
  1038.                  *       For now, just making another nsurl. */
  1039.                 error = nsurl_create(lwc_string_data(bgimage_uri), &url);
  1040.                 if (error != NSERROR_OK)
  1041.                         return false;
  1042.  
  1043.                 if (html_fetch_object(ctx->content, url, box, image_types,
  1044.                                 ctx->content->base.available_width, 1000,
  1045.                                 true) == false) {
  1046.                         nsurl_unref(url);
  1047.                         return false;
  1048.                 }
  1049.                 nsurl_unref(url);
  1050.         }
  1051.  
  1052.         if (*convert_children)
  1053.                 box->flags |= CONVERT_CHILDREN;
  1054.  
  1055.         if (box->type == BOX_INLINE || box->type == BOX_BR ||
  1056.                         box->type == BOX_INLINE_BLOCK) {
  1057.                 /* Inline container must exist, as we'll have
  1058.                  * created it above if it didn't */
  1059.                 assert(props.inline_container != NULL);
  1060.  
  1061.                 box_add_child(props.inline_container, box);
  1062.         } else {
  1063.                 if (css_computed_display(box->style, props.node_is_root) ==
  1064.                                 CSS_DISPLAY_LIST_ITEM) {
  1065.                         /* List item: compute marker */
  1066.                         if (box_construct_marker(box, props.title, ctx,
  1067.                                         props.containing_block) == false)
  1068.                                 return false;
  1069.                 }
  1070.  
  1071.                 if (css_computed_float(box->style) == CSS_FLOAT_LEFT ||
  1072.                                 css_computed_float(box->style) ==
  1073.                                 CSS_FLOAT_RIGHT) {
  1074.                         /* Float: insert a float between the parent and box. */
  1075.                         struct box *flt = box_create(NULL, NULL, false,
  1076.                                         props.href, props.target, props.title,
  1077.                                         NULL, ctx->bctx);
  1078.                         if (flt == NULL)
  1079.                                 return false;
  1080.  
  1081.                         if (css_computed_float(box->style) == CSS_FLOAT_LEFT)
  1082.                                 flt->type = BOX_FLOAT_LEFT;
  1083.                         else
  1084.                                 flt->type = BOX_FLOAT_RIGHT;
  1085.  
  1086.                         box_add_child(props.inline_container, flt);
  1087.                         box_add_child(flt, box);
  1088.                 } else {
  1089.                         /* Non-floated block-level box: add to containing block
  1090.                          * if there is one. If we're the root box, then there
  1091.                          * won't be. */
  1092.                         if (props.containing_block != NULL)
  1093.                                 box_add_child(props.containing_block, box);
  1094.                 }
  1095.         }
  1096.  
  1097.         return true;
  1098. }
  1099.  
  1100. /**
  1101.  * Complete construction of the box tree for an element.
  1102.  *
  1103.  * \param n        DOM node to construct for
  1104.  * \param content  Containing document
  1105.  *
  1106.  * This will be called after all children of an element have been processed
  1107.  */
  1108. void box_construct_element_after(dom_node *n, html_content *content)
  1109. {
  1110.         struct box_construct_props props;
  1111.         struct box *box = box_for_node(n);
  1112.  
  1113.         assert(box != NULL);
  1114.  
  1115.         box_extract_properties(n, &props);
  1116.  
  1117.         if (box->type == BOX_INLINE || box->type == BOX_BR) {
  1118.                 /* Insert INLINE_END into containing block */
  1119.                 struct box *inline_end;
  1120.                 bool has_children;
  1121.                 dom_exception err;
  1122.  
  1123.                 err = dom_node_has_child_nodes(n, &has_children);
  1124.                 if (err != DOM_NO_ERR)
  1125.                         return;
  1126.  
  1127.                 if (has_children == false ||
  1128.                                 (box->flags & CONVERT_CHILDREN) == 0) {
  1129.                         /* No children, or didn't want children converted */
  1130.                         return;
  1131.                 }
  1132.  
  1133.                 if (props.inline_container == NULL) {
  1134.                         /* Create inline container if we don't have one */
  1135.                         props.inline_container = box_create(NULL, NULL, false,
  1136.                                         NULL, NULL, NULL, NULL, content->bctx);
  1137.                         if (props.inline_container == NULL)
  1138.                                 return;
  1139.  
  1140.                         props.inline_container->type = BOX_INLINE_CONTAINER;
  1141.  
  1142.                         box_add_child(props.containing_block,
  1143.                                         props.inline_container);
  1144.                 }
  1145.  
  1146.                 inline_end = box_create(NULL, box->style, false,
  1147.                                 box->href, box->target, box->title,
  1148.                                 box->id == NULL ? NULL :
  1149.                                 lwc_string_ref(box->id), content->bctx);
  1150.                 if (inline_end != NULL) {
  1151.                         inline_end->type = BOX_INLINE_END;
  1152.  
  1153.                         assert(props.inline_container != NULL);
  1154.  
  1155.                         box_add_child(props.inline_container, inline_end);
  1156.  
  1157.                         box->inline_end = inline_end;
  1158.                         inline_end->inline_end = box;
  1159.                 }
  1160.         } else {
  1161.                 /* Handle the :after pseudo element */
  1162.                 box_construct_generate(n, content, box,
  1163.                                 box->styles->styles[CSS_PSEUDO_ELEMENT_AFTER]);
  1164.         }
  1165. }
  1166.  
  1167. /**
  1168.  * Construct the box tree for an XML text node.
  1169.  *
  1170.  * \param  ctx  Tree construction context
  1171.  * \return  true on success, false on memory exhaustion
  1172.  */
  1173.  
  1174. bool box_construct_text(struct box_construct_ctx *ctx)
  1175. {
  1176.         struct box_construct_props props;
  1177.         struct box *box = NULL;
  1178.         dom_string *content;
  1179.         dom_exception err;
  1180.  
  1181.         assert(ctx->n != NULL);
  1182.  
  1183.         box_extract_properties(ctx->n, &props);
  1184.  
  1185.         assert(props.containing_block != NULL);
  1186.  
  1187.         err = dom_characterdata_get_data(ctx->n, &content);
  1188.         if (err != DOM_NO_ERR || content == NULL)
  1189.                 return false;
  1190.  
  1191.         if (css_computed_white_space(props.parent_style) ==
  1192.                         CSS_WHITE_SPACE_NORMAL ||
  1193.                         css_computed_white_space(props.parent_style) ==
  1194.                         CSS_WHITE_SPACE_NOWRAP) {
  1195.                 char *text;
  1196.  
  1197.                 text = squash_whitespace(dom_string_data(content));
  1198.  
  1199.                 dom_string_unref(content);
  1200.  
  1201.                 if (text == NULL)
  1202.                         return false;
  1203.  
  1204.                 /* if the text is just a space, combine it with the preceding
  1205.                  * text node, if any */
  1206.                 if (text[0] == ' ' && text[1] == 0) {
  1207.                         if (props.inline_container != NULL) {
  1208.                                 assert(props.inline_container->last != NULL);
  1209.  
  1210.                                 props.inline_container->last->space =
  1211.                                                 UNKNOWN_WIDTH;
  1212.                         }
  1213.  
  1214.                         free(text);
  1215.  
  1216.                         return true;
  1217.                 }
  1218.  
  1219.                 if (props.inline_container == NULL) {
  1220.                         /* Child of a block without a current container
  1221.                          * (i.e. this box is the first child of its parent, or
  1222.                          * was preceded by block-level siblings) */
  1223.                         props.inline_container = box_create(NULL, NULL, false,
  1224.                                         NULL, NULL, NULL, NULL, ctx->bctx);
  1225.                         if (props.inline_container == NULL) {
  1226.                                 free(text);
  1227.                                 return false;
  1228.                         }
  1229.  
  1230.                         props.inline_container->type = BOX_INLINE_CONTAINER;
  1231.  
  1232.                         box_add_child(props.containing_block,
  1233.                                         props.inline_container);
  1234.                 }
  1235.  
  1236.                 /** \todo Dropping const here is not clever */
  1237.                 box = box_create(NULL,
  1238.                                 (css_computed_style *) props.parent_style,
  1239.                                 false, props.href, props.target, props.title,
  1240.                                 NULL, ctx->bctx);
  1241.                 if (box == NULL) {
  1242.                         free(text);
  1243.                         return false;
  1244.                 }
  1245.  
  1246.                 box->type = BOX_TEXT;
  1247.  
  1248.                 box->text = talloc_strdup(ctx->bctx, text);
  1249.                 free(text);
  1250.                 if (box->text == NULL)
  1251.                         return false;
  1252.  
  1253.                 box->length = strlen(box->text);
  1254.  
  1255.                 /* strip ending space char off */
  1256.                 if (box->length > 1 && box->text[box->length - 1] == ' ') {
  1257.                         box->space = UNKNOWN_WIDTH;
  1258.                         box->length--;
  1259.                 }
  1260.  
  1261.                 if (css_computed_text_transform(props.parent_style) !=
  1262.                                 CSS_TEXT_TRANSFORM_NONE)
  1263.                         box_text_transform(box->text, box->length,
  1264.                                 css_computed_text_transform(
  1265.                                         props.parent_style));
  1266.  
  1267.                 box_add_child(props.inline_container, box);
  1268.  
  1269.                 if (box->text[0] == ' ') {
  1270.                         box->length--;
  1271.  
  1272.                         memmove(box->text, &box->text[1], box->length);
  1273.  
  1274.                         if (box->prev != NULL)
  1275.                                 box->prev->space = UNKNOWN_WIDTH;
  1276.                 }
  1277.         } else {
  1278.                 /* white-space: pre */
  1279.                 char *text;
  1280.                 size_t text_len = dom_string_byte_length(content);
  1281.                 size_t i;
  1282.                 char *current;
  1283.                 enum css_white_space_e white_space =
  1284.                                 css_computed_white_space(props.parent_style);
  1285.  
  1286.                 /* note: pre-wrap/pre-line are unimplemented */
  1287.                 assert(white_space == CSS_WHITE_SPACE_PRE ||
  1288.                                 white_space == CSS_WHITE_SPACE_PRE_LINE ||
  1289.                                 white_space == CSS_WHITE_SPACE_PRE_WRAP);
  1290.  
  1291.                 text = malloc(text_len + 1);
  1292.                 dom_string_unref(content);
  1293.  
  1294.                 if (text == NULL)
  1295.                         return false;
  1296.  
  1297.                 memcpy(text, dom_string_data(content), text_len);
  1298.                 text[text_len] = '\0';
  1299.  
  1300.                 /* TODO: Handle tabs properly */
  1301.                 for (i = 0; i < text_len; i++)
  1302.                         if (text[i] == '\t')
  1303.                                 text[i] = ' ';
  1304.  
  1305.                 if (css_computed_text_transform(props.parent_style) !=
  1306.                                 CSS_TEXT_TRANSFORM_NONE)
  1307.                         box_text_transform(text, strlen(text),
  1308.                                 css_computed_text_transform(
  1309.                                                 props.parent_style));
  1310.  
  1311.                 current = text;
  1312.  
  1313.                 /* swallow a single leading new line */
  1314.                 if (props.containing_block->flags & PRE_STRIP) {
  1315.                         switch (*current) {
  1316.                         case '\n':
  1317.                                 current++;
  1318.                                 break;
  1319.                         case '\r':
  1320.                                 current++;
  1321.                                 if (*current == '\n')
  1322.                                         current++;
  1323.                                 break;
  1324.                         }
  1325.                         props.containing_block->flags &= ~PRE_STRIP;
  1326.                 }
  1327.  
  1328.                 do {
  1329.                         size_t len = strcspn(current, "\r\n");
  1330.  
  1331.                         char old = current[len];
  1332.  
  1333.                         current[len] = 0;
  1334.  
  1335.                         if (props.inline_container == NULL) {
  1336.                                 /* Child of a block without a current container
  1337.                                  * (i.e. this box is the first child of its
  1338.                                  * parent, or was preceded by block-level
  1339.                                  * siblings) */
  1340.                                 props.inline_container = box_create(NULL, NULL,
  1341.                                                 false, NULL, NULL, NULL, NULL,
  1342.                                                 ctx->bctx);
  1343.                                 if (props.inline_container == NULL) {
  1344.                                         free(text);
  1345.                                         return false;
  1346.                                 }
  1347.  
  1348.                                 props.inline_container->type =
  1349.                                                 BOX_INLINE_CONTAINER;
  1350.  
  1351.                                 box_add_child(props.containing_block,
  1352.                                                 props.inline_container);
  1353.                         }
  1354.  
  1355.                         /** \todo Dropping const isn't clever */
  1356.                         box = box_create(NULL,
  1357.                                 (css_computed_style *) props.parent_style,
  1358.                                 false, props.href, props.target, props.title,
  1359.                                 NULL, ctx->bctx);
  1360.                         if (box == NULL) {
  1361.                                 free(text);
  1362.                                 return false;
  1363.                         }
  1364.  
  1365.                         box->type = BOX_TEXT;
  1366.  
  1367.                         box->text = talloc_strdup(ctx->bctx, current);
  1368.                         if (box->text == NULL) {
  1369.                                 free(text);
  1370.                                 return false;
  1371.                         }
  1372.  
  1373.                         box->length = strlen(box->text);
  1374.  
  1375.                         box_add_child(props.inline_container, box);
  1376.  
  1377.                         current[len] = old;
  1378.  
  1379.                         current += len;
  1380.  
  1381.                         if (current[0] != '\0') {
  1382.                                 /* Linebreak: create new inline container */
  1383.                                 props.inline_container = box_create(NULL, NULL,
  1384.                                                 false, NULL, NULL, NULL, NULL,
  1385.                                                 ctx->bctx);
  1386.                                 if (props.inline_container == NULL) {
  1387.                                         free(text);
  1388.                                         return false;
  1389.                                 }
  1390.  
  1391.                                 props.inline_container->type =
  1392.                                                 BOX_INLINE_CONTAINER;
  1393.  
  1394.                                 box_add_child(props.containing_block,
  1395.                                                 props.inline_container);
  1396.  
  1397.                                 if (current[0] == '\r' && current[1] == '\n')
  1398.                                         current += 2;
  1399.                                 else
  1400.                                         current++;
  1401.                         }
  1402.                 } while (*current);
  1403.  
  1404.                 free(text);
  1405.         }
  1406.  
  1407.         return true;
  1408. }
  1409.  
  1410. /**
  1411.  * Get the style for an element.
  1412.  *
  1413.  * \param  c               content of type CONTENT_HTML that is being processed
  1414.  * \param  parent_style    style at this point in xml tree, or NULL for root
  1415.  * \param  n               node in xml tree
  1416.  * \return  the new style, or NULL on memory exhaustion
  1417.  */
  1418. css_select_results *box_get_style(html_content *c,
  1419.                 const css_computed_style *parent_style, dom_node *n)
  1420. {
  1421.         dom_string *s;
  1422.         dom_exception err;
  1423.         int pseudo_element;
  1424.         css_error error;
  1425.         css_stylesheet *inline_style = NULL;
  1426.         css_select_results *styles;
  1427.         nscss_select_ctx ctx;
  1428.  
  1429.         /* Firstly, construct inline stylesheet, if any */
  1430.         err = dom_element_get_attribute(n, kstr_style, &s);
  1431.         if (err != DOM_NO_ERR)
  1432.                 return NULL;
  1433.  
  1434.         if (s != NULL) {
  1435.                 inline_style = nscss_create_inline_style(
  1436.                                 (const uint8_t *) dom_string_data(s),
  1437.                                 dom_string_byte_length(s),
  1438.                                 c->encoding,
  1439.                                 nsurl_access(content_get_url(&c->base)),
  1440.                                 c->quirks != DOM_DOCUMENT_QUIRKS_MODE_NONE,
  1441.                                 box_style_alloc, NULL);
  1442.  
  1443.                 dom_string_unref(s);
  1444.  
  1445.                 if (inline_style == NULL)
  1446.                         return NULL;
  1447.         }
  1448.  
  1449.         /* Populate selection context */
  1450.         ctx.ctx = c->select_ctx;
  1451.         ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
  1452.         ctx.base_url = c->base_url;
  1453.         ctx.universal = c->universal;
  1454.  
  1455.         /* Select partial style for element */
  1456.         styles = nscss_get_style(&ctx, n, CSS_MEDIA_SCREEN, inline_style,
  1457.                         box_style_alloc, NULL);
  1458.  
  1459.         /* No longer need inline style */
  1460.         if (inline_style != NULL)
  1461.                 css_stylesheet_destroy(inline_style);
  1462.  
  1463.         /* Failed selecting partial style -- bail out */
  1464.         if (styles == NULL)
  1465.                 return NULL;
  1466.  
  1467.         /* If there's a parent style, compose with partial to obtain
  1468.          * complete computed style for element */
  1469.         if (parent_style != NULL) {
  1470.                 /* Complete the computed style, by composing with the parent
  1471.                  * element's style */
  1472.                 error = css_computed_style_compose(parent_style,
  1473.                                 styles->styles[CSS_PSEUDO_ELEMENT_NONE],
  1474.                                 nscss_compute_font_size, NULL,
  1475.                                 styles->styles[CSS_PSEUDO_ELEMENT_NONE]);
  1476.                 if (error != CSS_OK) {
  1477.                         css_select_results_destroy(styles);
  1478.                         return NULL;
  1479.                 }
  1480.         }
  1481.  
  1482.         for (pseudo_element = CSS_PSEUDO_ELEMENT_NONE + 1;
  1483.                         pseudo_element < CSS_PSEUDO_ELEMENT_COUNT;
  1484.                         pseudo_element++) {
  1485.  
  1486.                 if (pseudo_element == CSS_PSEUDO_ELEMENT_FIRST_LETTER ||
  1487.                                 pseudo_element == CSS_PSEUDO_ELEMENT_FIRST_LINE)
  1488.                         /* TODO: Handle first-line and first-letter pseudo
  1489.                          *       element computed style completion */
  1490.                         continue;
  1491.  
  1492.                 if (styles->styles[pseudo_element] == NULL)
  1493.                         /* There were no rules concerning this pseudo element */
  1494.                         continue;
  1495.  
  1496.                 /* Complete the pseudo element's computed style, by composing
  1497.                  * with the base element's style */
  1498.                 error = css_computed_style_compose(
  1499.                                 styles->styles[CSS_PSEUDO_ELEMENT_NONE],
  1500.                                 styles->styles[pseudo_element],
  1501.                                 nscss_compute_font_size, NULL,
  1502.                                 styles->styles[pseudo_element]);
  1503.                 if (error != CSS_OK) {
  1504.                         /* TODO: perhaps this shouldn't be quite so
  1505.                          * catastrophic? */
  1506.                         css_select_results_destroy(styles);
  1507.                         return NULL;
  1508.                 }
  1509.         }
  1510.  
  1511.         return styles;
  1512. }
  1513.  
  1514.  
  1515. /**
  1516.  * Apply the CSS text-transform property to given text for its ASCII chars.
  1517.  *
  1518.  * \param  s    string to transform
  1519.  * \param  len  length of s
  1520.  * \param  tt   transform type
  1521.  */
  1522.  
  1523. void box_text_transform(char *s, unsigned int len, enum css_text_transform_e tt)
  1524. {
  1525.         unsigned int i;
  1526.         if (len == 0)
  1527.                 return;
  1528.         switch (tt) {
  1529.                 case CSS_TEXT_TRANSFORM_UPPERCASE:
  1530.                         for (i = 0; i < len; ++i)
  1531.                                 if ((unsigned char) s[i] < 0x80)
  1532.                                         s[i] = ls_toupper(s[i]);
  1533.                         break;
  1534.                 case CSS_TEXT_TRANSFORM_LOWERCASE:
  1535.                         for (i = 0; i < len; ++i)
  1536.                                 if ((unsigned char) s[i] < 0x80)
  1537.                                         s[i] = ls_tolower(s[i]);
  1538.                         break;
  1539.                 case CSS_TEXT_TRANSFORM_CAPITALIZE:
  1540.                         if ((unsigned char) s[0] < 0x80)
  1541.                                 s[0] = ls_toupper(s[0]);
  1542.                         for (i = 1; i < len; ++i)
  1543.                                 if ((unsigned char) s[i] < 0x80 &&
  1544.                                                 ls_isspace(s[i - 1]))
  1545.                                         s[i] = ls_toupper(s[i]);
  1546.                         break;
  1547.                 default:
  1548.                         break;
  1549.         }
  1550. }
  1551.  
  1552.  
  1553. /**
  1554.  * \name  Special case element handlers
  1555.  *
  1556.  * These functions are called by box_construct_element() when an element is
  1557.  * being converted, according to the entries in element_table.
  1558.  *
  1559.  * The parameters are the xmlNode, the content for the document, and a partly
  1560.  * filled in box structure for the element.
  1561.  *
  1562.  * Return true on success, false on memory exhaustion. Set *convert_children
  1563.  * to false if children of this element in the XML tree should be skipped (for
  1564.  * example, if they have been processed in some special way already).
  1565.  *
  1566.  * Elements ordered as in the HTML 4.01 specification. Section numbers in
  1567.  * brackets [] refer to the spec.
  1568.  *
  1569.  * \{
  1570.  */
  1571.  
  1572. /**
  1573.  * Document body [7.5.1].
  1574.  */
  1575.  
  1576. bool box_body(BOX_SPECIAL_PARAMS)
  1577. {
  1578.         css_color color;
  1579.  
  1580.         css_computed_background_color(box->style, &color);
  1581.         if (nscss_color_is_transparent(color))
  1582.                 content->background_colour = NS_TRANSPARENT;
  1583.         else
  1584.                 content->background_colour = nscss_color_to_ns(color);
  1585.  
  1586.         return true;
  1587. }
  1588.  
  1589.  
  1590. /**
  1591.  * Forced line break [9.3.2].
  1592.  */
  1593.  
  1594. bool box_br(BOX_SPECIAL_PARAMS)
  1595. {
  1596.         box->type = BOX_BR;
  1597.         return true;
  1598. }
  1599.  
  1600. /**
  1601.  * Preformatted text [9.3.4].
  1602.  */
  1603.  
  1604. bool box_pre(BOX_SPECIAL_PARAMS)
  1605. {
  1606.         box->flags |= PRE_STRIP;
  1607.         return true;
  1608. }
  1609.  
  1610. /**
  1611.  * Anchor [12.2].
  1612.  */
  1613.  
  1614. bool box_a(BOX_SPECIAL_PARAMS)
  1615. {
  1616.         bool ok;
  1617.         nsurl *url;
  1618.         dom_string *s;
  1619.         dom_exception err;
  1620.  
  1621.         err = dom_element_get_attribute(n, kstr_href, &s);
  1622.         if (err == DOM_NO_ERR && s != NULL) {
  1623.                 ok = box_extract_link(dom_string_data(s),
  1624.                                 content->base_url, &url);
  1625.                 dom_string_unref(s);
  1626.                 if (!ok)
  1627.                         return false;
  1628.                 if (url) {
  1629.                         if (box->href != NULL)
  1630.                                 nsurl_unref(box->href);
  1631.                         box->href = url;
  1632.                 }
  1633.         }
  1634.  
  1635.         /* name and id share the same namespace */
  1636.         err = dom_element_get_attribute(n, kstr_name, &s);
  1637.         if (err == DOM_NO_ERR && s != NULL) {
  1638.                 lwc_string *lwc_name;
  1639.  
  1640.                 err = dom_string_intern(s, &lwc_name);
  1641.  
  1642.                 dom_string_unref(s);
  1643.  
  1644.                 if (err == DOM_NO_ERR) {
  1645.                         /* name replaces existing id
  1646.                          * TODO: really? */
  1647.                         if (box->id != NULL)
  1648.                                 lwc_string_unref(box->id);
  1649.  
  1650.                         box->id = lwc_name;
  1651.                 }
  1652.         }
  1653.  
  1654.         /* target frame [16.3] */
  1655.         err = dom_element_get_attribute(n, kstr_target, &s);
  1656.         if (err == DOM_NO_ERR && s != NULL) {
  1657.                 if (dom_string_caseless_lwc_isequal(s,
  1658.                                 corestring_lwc__blank))
  1659.                         box->target = TARGET_BLANK;
  1660.                 else if (dom_string_caseless_lwc_isequal(s,
  1661.                                 corestring_lwc__top))
  1662.                         box->target = TARGET_TOP;
  1663.                 else if (dom_string_caseless_lwc_isequal(s,
  1664.                                 corestring_lwc__parent))
  1665.                         box->target = TARGET_PARENT;
  1666.                 else if (dom_string_caseless_lwc_isequal(s,
  1667.                                 corestring_lwc__self))
  1668.                         /* the default may have been overridden by a
  1669.                          * <base target=...>, so this is different to 0 */
  1670.                         box->target = TARGET_SELF;
  1671.                 else {
  1672.                         /* 6.16 says that frame names must begin with [a-zA-Z]
  1673.                          * This doesn't match reality, so just take anything */
  1674.                         box->target = talloc_strdup(content->bctx,
  1675.                                         dom_string_data(s));
  1676.                         if (!box->target) {
  1677.                                 dom_string_unref(s);
  1678.                                 return false;
  1679.                         }
  1680.                 }
  1681.                 dom_string_unref(s);
  1682.         }
  1683.  
  1684.         return true;
  1685. }
  1686.  
  1687.  
  1688. /**
  1689.  * Embedded image [13.2].
  1690.  */
  1691.  
  1692. bool box_image(BOX_SPECIAL_PARAMS)
  1693. {
  1694.         bool ok;
  1695.         dom_string *s;
  1696.         dom_exception err;
  1697.         nsurl *url;
  1698.         enum css_width_e wtype;
  1699.         enum css_height_e htype;
  1700.         css_fixed value = 0;
  1701.         css_unit wunit = CSS_UNIT_PX;
  1702.         css_unit hunit = CSS_UNIT_PX;
  1703.  
  1704.         if (box->style && css_computed_display(box->style,
  1705.                         box_is_root(n)) == CSS_DISPLAY_NONE)
  1706.                 return true;
  1707.  
  1708.         /* handle alt text */
  1709.         err = dom_element_get_attribute(n, kstr_alt, &s);
  1710.         if (err == DOM_NO_ERR && s != NULL) {
  1711.                 char *alt = squash_whitespace(dom_string_data(s));
  1712.                 dom_string_unref(s);
  1713.                 if (alt == NULL)
  1714.                         return false;
  1715.                 box->text = talloc_strdup(content->bctx, alt);
  1716.                 free(alt);
  1717.                 if (box->text == NULL)
  1718.                         return false;
  1719.                 box->length = strlen(box->text);
  1720.         }
  1721.  
  1722.         if (nsoption_bool(foreground_images) == false) {
  1723.                 return true;
  1724.         }
  1725.  
  1726.         /* imagemap associated with this image */
  1727.         if (!box_get_attribute(n, "usemap", content->bctx, &box->usemap))
  1728.                 return false;
  1729.         if (box->usemap && box->usemap[0] == '#')
  1730.                 box->usemap++;
  1731.  
  1732.         /* get image URL */
  1733.         err = dom_element_get_attribute(n, kstr_src, &s);
  1734.         if (err != DOM_NO_ERR || s == NULL)
  1735.                 return true;
  1736.  
  1737.         if (box_extract_link(dom_string_data(s), content->base_url,
  1738.                         &url) == false) {
  1739.                 dom_string_unref(s);
  1740.                 return false;
  1741.         }
  1742.  
  1743.         dom_string_unref(s);
  1744.  
  1745.         if (url == NULL)
  1746.                 return true;
  1747.  
  1748.         /* start fetch */
  1749.         ok = html_fetch_object(content, url, box, image_types,
  1750.                         content->base.available_width, 1000, false);
  1751.         nsurl_unref(url);
  1752.  
  1753.         wtype = css_computed_width(box->style, &value, &wunit);
  1754.         htype = css_computed_height(box->style, &value, &hunit);
  1755.  
  1756.         if (wtype == CSS_WIDTH_SET && wunit != CSS_UNIT_PCT &&
  1757.                         htype == CSS_HEIGHT_SET && hunit != CSS_UNIT_PCT) {
  1758.                 /* We know the dimensions the image will be shown at before it's
  1759.                  * fetched. */
  1760.                 box->flags |= REPLACE_DIM;
  1761.         }
  1762.  
  1763.         return ok;
  1764. }
  1765.  
  1766.  
  1767. /**
  1768.  * Noscript element
  1769.  */
  1770.  
  1771. bool box_noscript(BOX_SPECIAL_PARAMS)
  1772. {
  1773.         /* If scripting is enabled, do not display the contents of noscript */
  1774.         if (nsoption_bool(enable_javascript))
  1775.                 *convert_children = false;
  1776.  
  1777.         return true;
  1778. }
  1779.  
  1780.  
  1781. /**
  1782.  * Destructor for object_params, for <object> elements
  1783.  *
  1784.  * \param b     The object params being destroyed.
  1785.  * \return 0 to allow talloc to continue destroying the tree.
  1786.  */
  1787. static int box_object_talloc_destructor(struct object_params *o)
  1788. {
  1789.         if (o->codebase != NULL)
  1790.                 nsurl_unref(o->codebase);
  1791.         if (o->classid != NULL)
  1792.                 nsurl_unref(o->classid);
  1793.         if (o->data != NULL)
  1794.                 nsurl_unref(o->data);
  1795.        
  1796.         return 0;
  1797. }
  1798.  
  1799. /**
  1800.  * Generic embedded object [13.3].
  1801.  */
  1802.  
  1803. bool box_object(BOX_SPECIAL_PARAMS)
  1804. {
  1805.         struct object_params *params;
  1806.         struct object_param *param;
  1807.         dom_string *codebase, *classid, *data;
  1808.         dom_node *c;
  1809.         dom_exception err;
  1810.  
  1811.         if (box->style && css_computed_display(box->style,
  1812.                         box_is_root(n)) == CSS_DISPLAY_NONE)
  1813.                 return true;
  1814.  
  1815.         if (box_get_attribute(n, "usemap", content->bctx, &box->usemap) ==
  1816.                         false)
  1817.                 return false;
  1818.         if (box->usemap && box->usemap[0] == '#')
  1819.                 box->usemap++;
  1820.  
  1821.         params = talloc(content->bctx, struct object_params);
  1822.         if (params == NULL)
  1823.                 return false;
  1824.  
  1825.         talloc_set_destructor(params, box_object_talloc_destructor);
  1826.  
  1827.         params->data = NULL;
  1828.         params->type = NULL;
  1829.         params->codetype = NULL;
  1830.         params->codebase = NULL;
  1831.         params->classid = NULL;
  1832.         params->params = NULL;
  1833.  
  1834.         /* codebase, classid, and data are URLs
  1835.          * (codebase is the base for the other two) */
  1836.         err = dom_element_get_attribute(n, kstr_codebase, &codebase);
  1837.         if (err == DOM_NO_ERR && codebase != NULL) {
  1838.                 if (box_extract_link(dom_string_data(codebase),
  1839.                                 content->base_url,
  1840.                                 &params->codebase) == false) {
  1841.                         dom_string_unref(codebase);
  1842.                         return false;
  1843.                 }
  1844.                 dom_string_unref(codebase);
  1845.         }
  1846.         if (params->codebase == NULL)
  1847.                 params->codebase = nsurl_ref(content->base_url);
  1848.  
  1849.         err = dom_element_get_attribute(n, kstr_classid, &classid);
  1850.         if (err == DOM_NO_ERR && classid != NULL) {
  1851.                 if (box_extract_link(dom_string_data(classid), params->codebase,
  1852.                                 &params->classid) == false) {
  1853.                         dom_string_unref(classid);
  1854.                         return false;
  1855.                 }
  1856.                 dom_string_unref(classid);
  1857.         }
  1858.  
  1859.         err = dom_element_get_attribute(n, kstr_data, &data);
  1860.         if (err == DOM_NO_ERR && data != NULL) {
  1861.                 if (box_extract_link(dom_string_data(data), params->codebase,
  1862.                                 &params->data) == false) {
  1863.                         dom_string_unref(data);
  1864.                         return false;
  1865.                 }
  1866.                 dom_string_unref(data);
  1867.         }
  1868.  
  1869.         if (params->classid == NULL && params->data == NULL)
  1870.                 /* nothing to embed; ignore */
  1871.                 return true;
  1872.  
  1873.         /* Don't include ourself */
  1874.         if (params->classid != NULL && nsurl_compare(content->base_url,
  1875.                         params->classid, NSURL_COMPLETE))
  1876.                 return true;
  1877.  
  1878.         if (params->data != NULL && nsurl_compare(content->base_url,
  1879.                         params->data, NSURL_COMPLETE))
  1880.                 return true;
  1881.  
  1882.         /* codetype and type are MIME types */
  1883.         if (box_get_attribute(n, "codetype", params,
  1884.                         &params->codetype) == false)
  1885.                 return false;
  1886.         if (box_get_attribute(n, "type", params, &params->type) == false)
  1887.                 return false;
  1888.  
  1889.         /* classid && !data => classid is used (consult codetype)
  1890.          * (classid || !classid) && data => data is used (consult type)
  1891.          * !classid && !data => invalid; ignored */
  1892.  
  1893.         if (params->classid != NULL && params->data == NULL &&
  1894.                         params->codetype != NULL) {
  1895.                 lwc_string *icodetype;
  1896.                 lwc_error lerror;
  1897.  
  1898.                 lerror = lwc_intern_string(params->codetype,
  1899.                                 strlen(params->codetype), &icodetype);
  1900.                 if (lerror != lwc_error_ok)
  1901.                         return false;
  1902.  
  1903.                 if (content_factory_type_from_mime_type(icodetype) ==
  1904.                                 CONTENT_NONE) {
  1905.                         /* can't handle this MIME type */
  1906.                         lwc_string_unref(icodetype);
  1907.                         return true;
  1908.                 }
  1909.  
  1910.                 lwc_string_unref(icodetype);
  1911.         }
  1912.  
  1913.         if (params->data != NULL && params->type != NULL) {
  1914.                 lwc_string *itype;
  1915.                 lwc_error lerror;
  1916.  
  1917.                 lerror = lwc_intern_string(params->type, strlen(params->type),
  1918.                                 &itype);
  1919.                 if (lerror != lwc_error_ok)
  1920.                         return false;
  1921.  
  1922.                 if (content_factory_type_from_mime_type(itype) ==
  1923.                                 CONTENT_NONE) {
  1924.                         /* can't handle this MIME type */
  1925.                         lwc_string_unref(itype);
  1926.                         return true;
  1927.                 }
  1928.  
  1929.                 lwc_string_unref(itype);
  1930.         }
  1931.  
  1932.         /* add parameters to linked list */
  1933.         err = dom_node_get_first_child(n, &c);
  1934.         if (err != DOM_NO_ERR)
  1935.                 return false;
  1936.  
  1937.         while (c != NULL) {
  1938.                 dom_node *next;
  1939.                 dom_node_type type;
  1940.  
  1941.                 err = dom_node_get_node_type(c, &type);
  1942.                 if (err != DOM_NO_ERR) {
  1943.                         dom_node_unref(c);
  1944.                         return false;
  1945.                 }
  1946.  
  1947.                 if (type == DOM_ELEMENT_NODE) {
  1948.                         dom_string *name;
  1949.  
  1950.                         err = dom_node_get_node_name(c, &name);
  1951.                         if (err != DOM_NO_ERR) {
  1952.                                 dom_node_unref(c);
  1953.                                 return false;
  1954.                         }
  1955.  
  1956.                         if (!dom_string_caseless_lwc_isequal(name,
  1957.                                         corestring_lwc_param)) {
  1958.                                 /* The first non-param child is the start of
  1959.                                  * the alt html. Therefore, we should break
  1960.                                  * out of this loop. */
  1961.                                 dom_node_unref(c);
  1962.                                 break;
  1963.                         }
  1964.  
  1965.                         param = talloc(params, struct object_param);
  1966.                         if (param == NULL) {
  1967.                                 dom_node_unref(c);
  1968.                                 return false;
  1969.                         }
  1970.                         param->name = NULL;
  1971.                         param->value = NULL;
  1972.                         param->type = NULL;
  1973.                         param->valuetype = NULL;
  1974.                         param->next = NULL;
  1975.  
  1976.                         if (box_get_attribute(c, "name", param,
  1977.                                         &param->name) == false) {
  1978.                                 dom_node_unref(c);
  1979.                                 return false;
  1980.                         }
  1981.  
  1982.                         if (box_get_attribute(c, "value", param,
  1983.                                         &param->value) == false) {
  1984.                                 dom_node_unref(c);
  1985.                                 return false;
  1986.                         }
  1987.  
  1988.                         if (box_get_attribute(c, "type", param,
  1989.                                         &param->type) == false) {
  1990.                                 dom_node_unref(c);
  1991.                                 return false;
  1992.                         }
  1993.  
  1994.                         if (box_get_attribute(c, "valuetype", param,
  1995.                                         &param->valuetype) == false) {
  1996.                                 dom_node_unref(c);
  1997.                                 return false;
  1998.                         }
  1999.  
  2000.                         if (param->valuetype == NULL) {
  2001.                                 param->valuetype = talloc_strdup(param, "data");
  2002.                                 if (param->valuetype == NULL) {
  2003.                                         dom_node_unref(c);
  2004.                                         return false;
  2005.                                 }
  2006.                         }
  2007.  
  2008.                         param->next = params->params;
  2009.                         params->params = param;
  2010.                 }
  2011.  
  2012.                 err = dom_node_get_next_sibling(c, &next);
  2013.                 if (err != DOM_NO_ERR) {
  2014.                         dom_node_unref(c);
  2015.                         return false;
  2016.                 }
  2017.  
  2018.                 dom_node_unref(c);
  2019.                 c = next;
  2020.         }
  2021.  
  2022.         box->object_params = params;
  2023.  
  2024.         /* start fetch (MIME type is ok or not specified) */
  2025.         if (!html_fetch_object(content,
  2026.                         params->data ? params->data : params->classid,
  2027.                         box, CONTENT_ANY, content->base.available_width, 1000,
  2028.                         false))
  2029.                 return false;
  2030.  
  2031.         *convert_children = false;
  2032.         return true;
  2033. }
  2034.  
  2035.  
  2036. /**
  2037.  * Window subdivision [16.2.1].
  2038.  */
  2039.  
  2040. bool box_frameset(BOX_SPECIAL_PARAMS)
  2041. {
  2042.         bool ok;
  2043.  
  2044.         if (content->frameset) {
  2045.                 LOG(("Error: multiple framesets in document."));
  2046.                 /* Don't convert children */
  2047.                 if (convert_children)
  2048.                         *convert_children = false;
  2049.                 /* And ignore this spurious frameset */
  2050.                 box->type = BOX_NONE;
  2051.                 return true;
  2052.         }
  2053.  
  2054.         content->frameset = talloc_zero(content->bctx, struct content_html_frames);
  2055.         if (!content->frameset)
  2056.                 return false;
  2057.  
  2058.         ok = box_create_frameset(content->frameset, n, content);
  2059.         if (ok)
  2060.                 box->type = BOX_NONE;
  2061.  
  2062.         if (convert_children)
  2063.                 *convert_children = false;
  2064.         return ok;
  2065. }
  2066.  
  2067.  
  2068. /**
  2069.  * Destructor for content_html_frames, for <frame> elements
  2070.  *
  2071.  * \param b     The frame params being destroyed.
  2072.  * \return 0 to allow talloc to continue destroying the tree.
  2073.  */
  2074. static int box_frames_talloc_destructor(struct content_html_frames *f)
  2075. {
  2076.         if (f->url != NULL) {
  2077.                 nsurl_unref(f->url);
  2078.                 f->url = NULL;
  2079.         }
  2080.        
  2081.         return 0;
  2082. }
  2083.  
  2084. bool box_create_frameset(struct content_html_frames *f, dom_node *n,
  2085.                 html_content *content) {
  2086.         unsigned int row, col, index, i;
  2087.         unsigned int rows = 1, cols = 1;
  2088.         dom_string *s;
  2089.         dom_exception err;
  2090.         nsurl *url;
  2091.         struct frame_dimension *row_height = 0, *col_width = 0;
  2092.         dom_node *c, *next;
  2093.         struct content_html_frames *frame;
  2094.         bool default_border = true;
  2095.         colour default_border_colour = 0x000000;
  2096.  
  2097.         /* parse rows and columns */
  2098.         err = dom_element_get_attribute(n, kstr_rows, &s);
  2099.         if (err == DOM_NO_ERR && s != NULL) {
  2100.                 row_height = box_parse_multi_lengths(dom_string_data(s), &rows);
  2101.                 dom_string_unref(s);
  2102.                 if (row_height == NULL)
  2103.                         return false;
  2104.         } else {
  2105.                 row_height = calloc(1, sizeof(struct frame_dimension));
  2106.                 if (row_height == NULL)
  2107.                         return false;
  2108.                 row_height->value = 100;
  2109.                 row_height->unit = FRAME_DIMENSION_PERCENT;
  2110.         }
  2111.  
  2112.         err = dom_element_get_attribute(n, kstr_cols, &s);
  2113.         if (err == DOM_NO_ERR && s != NULL) {
  2114.                 col_width = box_parse_multi_lengths(dom_string_data(s), &cols);
  2115.                 dom_string_unref(s);
  2116.                 if (col_width == NULL)
  2117.                         return false;
  2118.         } else {
  2119.                 col_width = calloc(1, sizeof(struct frame_dimension));
  2120.                 if (col_width == NULL)
  2121.                         return false;
  2122.                 col_width->value = 100;
  2123.                 col_width->unit = FRAME_DIMENSION_PERCENT;
  2124.         }
  2125.  
  2126.         /* common extension: border="0|1" to control all children */
  2127.         err = dom_element_get_attribute(n, kstr_border, &s);
  2128.         if (err == DOM_NO_ERR && s != NULL) {
  2129.                 if ((dom_string_data(s)[0] == '0') &&
  2130.                                 (dom_string_data(s)[1] == '\0'))
  2131.                         default_border = false;
  2132.                 dom_string_unref(s);
  2133.         }
  2134.  
  2135.         /* common extension: frameborder="yes|no" to control all children */
  2136.         err = dom_element_get_attribute(n, kstr_frameborder, &s);
  2137.         if (err == DOM_NO_ERR && s != NULL) {
  2138.                 if (dom_string_caseless_lwc_isequal(s,
  2139.                                 corestring_lwc_no) == 0)
  2140.                         default_border = false;
  2141.                 dom_string_unref(s);
  2142.         }
  2143.  
  2144.         /* common extension: bordercolor="#RRGGBB|<named colour>" to control
  2145.          *all children */
  2146.         err = dom_element_get_attribute(n, kstr_bordercolor, &s);
  2147.         if (err == DOM_NO_ERR && s != NULL) {
  2148.                 css_color color;
  2149.  
  2150.                 if (nscss_parse_colour(dom_string_data(s), &color))
  2151.                         default_border_colour = nscss_color_to_ns(color);
  2152.  
  2153.                 dom_string_unref(s);
  2154.         }
  2155.  
  2156.         /* update frameset and create default children */
  2157.         f->cols = cols;
  2158.         f->rows = rows;
  2159.         f->scrolling = SCROLLING_NO;
  2160.         f->children = talloc_array(content->bctx, struct content_html_frames,
  2161.                                                                 (rows * cols));
  2162.  
  2163.         talloc_set_destructor(f->children, box_frames_talloc_destructor);
  2164.  
  2165.         for (row = 0; row < rows; row++) {
  2166.                 for (col = 0; col < cols; col++) {
  2167.                         index = (row * cols) + col;
  2168.                         frame = &f->children[index];
  2169.                         frame->cols = 0;
  2170.                         frame->rows = 0;
  2171.                         frame->width = col_width[col];
  2172.                         frame->height = row_height[row];
  2173.                         frame->margin_width = 0;
  2174.                         frame->margin_height = 0;
  2175.                         frame->name = NULL;
  2176.                         frame->url = NULL;
  2177.                         frame->no_resize = false;
  2178.                         frame->scrolling = SCROLLING_AUTO;
  2179.                         frame->border = default_border;
  2180.                         frame->border_colour = default_border_colour;
  2181.                         frame->children = NULL;
  2182.                 }
  2183.         }
  2184.         free(col_width);
  2185.         free(row_height);
  2186.  
  2187.         /* create the frameset windows */
  2188.         err = dom_node_get_first_child(n, &c);
  2189.         if (err != DOM_NO_ERR)
  2190.                 return false;
  2191.  
  2192.         for (row = 0; c != NULL && row < rows; row++) {
  2193.                 for (col = 0; c != NULL && col < cols; col++) {
  2194.                         while (c != NULL) {
  2195.                                 dom_node_type type;
  2196.                                 dom_string *name;
  2197.  
  2198.                                 err = dom_node_get_node_type(c, &type);
  2199.                                 if (err != DOM_NO_ERR) {
  2200.                                         dom_node_unref(c);
  2201.                                         return false;
  2202.                                 }
  2203.  
  2204.                                 err = dom_node_get_node_name(c, &name);
  2205.                                 if (err != DOM_NO_ERR) {
  2206.                                         dom_node_unref(c);
  2207.                                         return false;
  2208.                                 }
  2209.  
  2210.                                 if (type != DOM_ELEMENT_NODE ||
  2211.                                         (!dom_string_caseless_lwc_isequal(
  2212.                                                         name,
  2213.                                                         corestring_lwc_frame) &&
  2214.                                         !dom_string_caseless_lwc_isequal(
  2215.                                                         name,
  2216.                                                         corestring_lwc_frameset
  2217.                                                         ))) {
  2218.                                         err = dom_node_get_next_sibling(c,
  2219.                                                         &next);
  2220.                                         if (err != DOM_NO_ERR) {
  2221.                                                 dom_node_unref(c);
  2222.                                                 return false;
  2223.                                         }
  2224.  
  2225.                                         dom_node_unref(c);
  2226.                                         c = next;
  2227.                                 } else {
  2228.                                         /* Got a FRAME or FRAMESET element */
  2229.                                         break;
  2230.                                 }
  2231.                         }
  2232.  
  2233.                         if (c == NULL)
  2234.                                 break;
  2235.  
  2236.                         /* get current frame */
  2237.                         index = (row * cols) + col;
  2238.                         frame = &f->children[index];
  2239.  
  2240.                         /* nest framesets */
  2241.                         err = dom_node_get_node_name(c, &s);
  2242.                         if (err != DOM_NO_ERR) {
  2243.                                 dom_node_unref(c);
  2244.                                 return false;
  2245.                         }
  2246.  
  2247.                         if (dom_string_caseless_lwc_isequal(s,
  2248.                                         corestring_lwc_frameset)) {
  2249.                                 dom_string_unref(s);
  2250.                                 frame->border = 0;
  2251.                                 if (box_create_frameset(frame, c,
  2252.                                                 content) == false) {
  2253.                                         dom_node_unref(c);
  2254.                                         return false;
  2255.                                 }
  2256.  
  2257.                                 err = dom_node_get_next_sibling(c, &next);
  2258.                                 if (err != DOM_NO_ERR) {
  2259.                                         dom_node_unref(c);
  2260.                                         return false;
  2261.                                 }
  2262.  
  2263.                                 dom_node_unref(c);
  2264.                                 c = next;
  2265.                                 continue;
  2266.                         }
  2267.  
  2268.                         dom_string_unref(s);
  2269.  
  2270.                         /* get frame URL (not required) */
  2271.                         url = NULL;
  2272.                         err = dom_element_get_attribute(c, kstr_src, &s);
  2273.                         if (err == DOM_NO_ERR && s != NULL) {
  2274.                                 box_extract_link(dom_string_data(s),
  2275.                                                 content->base_url, &url);
  2276.                                 dom_string_unref(s);
  2277.                         }
  2278.  
  2279.                         /* copy url */
  2280.                         if (url != NULL) {
  2281.                                 /* no self-references */
  2282.                                 if (nsurl_compare(content->base_url, url,
  2283.                                                 NSURL_COMPLETE) == false)
  2284.                                         frame->url = url;
  2285.                                 url = NULL;
  2286.                         }
  2287.  
  2288.                         /* fill in specified values */
  2289.                         err = dom_element_get_attribute(c, kstr_name, &s);
  2290.                         if (err == DOM_NO_ERR && s != NULL) {
  2291.                                 frame->name = talloc_strdup(content->bctx,
  2292.                                                 dom_string_data(s));
  2293.                                 dom_string_unref(s);
  2294.                         }
  2295.  
  2296.                         dom_element_has_attribute(c, kstr_noresize,
  2297.                                         &frame->no_resize);
  2298.  
  2299.                         err = dom_element_get_attribute(c, kstr_frameborder,
  2300.                                         &s);
  2301.                         if (err == DOM_NO_ERR && s != NULL) {
  2302.                                 i = atoi(dom_string_data(s));
  2303.                                 frame->border = (i != 0);
  2304.                                 dom_string_unref(s);
  2305.                         }
  2306.  
  2307.                         err = dom_element_get_attribute(c, kstr_scrolling, &s);
  2308.                         if (err == DOM_NO_ERR && s != NULL) {
  2309.                                 if (dom_string_caseless_lwc_isequal(s,
  2310.                                                 corestring_lwc_yes))
  2311.                                         frame->scrolling = SCROLLING_YES;
  2312.                                 else if (dom_string_caseless_lwc_isequal(s,
  2313.                                                 corestring_lwc_no))
  2314.                                         frame->scrolling = SCROLLING_NO;
  2315.                                 dom_string_unref(s);
  2316.                         }
  2317.  
  2318.                         err = dom_element_get_attribute(c, kstr_marginwidth,
  2319.                                         &s);
  2320.                         if (err == DOM_NO_ERR && s != NULL) {
  2321.                                 frame->margin_width = atoi(dom_string_data(s));
  2322.                                 dom_string_unref(s);
  2323.                         }
  2324.  
  2325.                         err = dom_element_get_attribute(c, kstr_marginheight,
  2326.                                         &s);
  2327.                         if (err == DOM_NO_ERR && s != NULL) {
  2328.                                 frame->margin_height = atoi(dom_string_data(s));
  2329.                                 dom_string_unref(s);
  2330.                         }
  2331.  
  2332.                         err = dom_element_get_attribute(c, kstr_bordercolor,
  2333.                                         &s);
  2334.                         if (err == DOM_NO_ERR && s != NULL) {
  2335.                                 css_color color;
  2336.  
  2337.                                 if (nscss_parse_colour(dom_string_data(s),
  2338.                                                 &color))
  2339.                                         frame->border_colour =
  2340.                                                 nscss_color_to_ns(color);
  2341.  
  2342.                                 dom_string_unref(s);
  2343.                         }
  2344.  
  2345.                         /* advance */
  2346.                         err = dom_node_get_next_sibling(c, &next);
  2347.                         if (err != DOM_NO_ERR) {
  2348.                                 dom_node_unref(c);
  2349.                                 return false;
  2350.                         }
  2351.  
  2352.                         dom_node_unref(c);
  2353.                         c = next;
  2354.                 }
  2355.         }
  2356.  
  2357.         return true;
  2358. }
  2359.  
  2360.  
  2361. /**
  2362.  * Destructor for content_html_iframe, for <iframe> elements
  2363.  *
  2364.  * \param b     The iframe params being destroyed.
  2365.  * \return 0 to allow talloc to continue destroying the tree.
  2366.  */
  2367. static int box_iframes_talloc_destructor(struct content_html_iframe *f)
  2368. {
  2369.         if (f->url != NULL) {
  2370.                 nsurl_unref(f->url);
  2371.                 f->url = NULL;
  2372.         }
  2373.        
  2374.         return 0;
  2375. }
  2376.  
  2377.  
  2378. /**
  2379.  * Inline subwindow [16.5].
  2380.  */
  2381.  
  2382. bool box_iframe(BOX_SPECIAL_PARAMS)
  2383. {
  2384.         nsurl *url;
  2385.         dom_string *s;
  2386.         dom_exception err;
  2387.         struct content_html_iframe *iframe;
  2388.         int i;
  2389.  
  2390.         if (box->style && css_computed_display(box->style,
  2391.                         box_is_root(n)) == CSS_DISPLAY_NONE)
  2392.                 return true;
  2393.  
  2394.         if (box->style && css_computed_visibility(box->style) ==
  2395.                         CSS_VISIBILITY_HIDDEN)
  2396.                 /* Don't create iframe discriptors for invisible iframes
  2397.                  * TODO: handle hidden iframes at browser_window generation
  2398.                  * time instead? */
  2399.                 return true;
  2400.  
  2401.         /* get frame URL */
  2402.         err = dom_element_get_attribute(n, kstr_src, &s);
  2403.         if (err != DOM_NO_ERR || s == NULL)
  2404.                 return true;
  2405.         if (box_extract_link(dom_string_data(s), content->base_url,
  2406.                         &url) == false) {
  2407.                 dom_string_unref(s);
  2408.                 return false;
  2409.         }
  2410.         dom_string_unref(s);
  2411.         if (url == NULL)
  2412.                 return true;
  2413.  
  2414.         /* don't include ourself */
  2415.         if (nsurl_compare(content->base_url, url, NSURL_COMPLETE)) {
  2416.                 nsurl_unref(url);
  2417.                 return true;
  2418.         }
  2419.  
  2420.         /* create a new iframe */
  2421.         iframe = talloc(content->bctx, struct content_html_iframe);
  2422.         if (iframe == NULL) {
  2423.                 nsurl_unref(url);
  2424.                 return false;
  2425.         }
  2426.  
  2427.         talloc_set_destructor(iframe, box_iframes_talloc_destructor);
  2428.  
  2429.         iframe->box = box;
  2430.         iframe->margin_width = 0;
  2431.         iframe->margin_height = 0;
  2432.         iframe->name = NULL;
  2433.         iframe->url = url;
  2434.         iframe->scrolling = SCROLLING_AUTO;
  2435.         iframe->border = true;
  2436.  
  2437.         /* Add this iframe to the linked list of iframes */
  2438.         iframe->next = content->iframe;
  2439.         content->iframe = iframe;
  2440.  
  2441.         /* fill in specified values */
  2442.         err = dom_element_get_attribute(n, kstr_name, &s);
  2443.         if (err == DOM_NO_ERR && s != NULL) {
  2444.                 iframe->name = talloc_strdup(content->bctx, dom_string_data(s));
  2445.                 dom_string_unref(s);
  2446.         }
  2447.  
  2448.         err = dom_element_get_attribute(n, kstr_frameborder, &s);
  2449.         if (err == DOM_NO_ERR && s != NULL) {
  2450.                 i = atoi(dom_string_data(s));
  2451.                 iframe->border = (i != 0);
  2452.                 dom_string_unref(s);
  2453.         }
  2454.  
  2455.         err = dom_element_get_attribute(n, kstr_bordercolor, &s);
  2456.         if (err == DOM_NO_ERR && s != NULL) {
  2457.                 css_color color;
  2458.  
  2459.                 if (nscss_parse_colour(dom_string_data(s), &color))
  2460.                         iframe->border_colour = nscss_color_to_ns(color);
  2461.  
  2462.                 dom_string_unref(s);
  2463.         }
  2464.  
  2465.         err = dom_element_get_attribute(n, kstr_scrolling, &s);
  2466.         if (err == DOM_NO_ERR && s != NULL) {
  2467.                 if (dom_string_caseless_lwc_isequal(s,
  2468.                                 corestring_lwc_yes))
  2469.                         iframe->scrolling = SCROLLING_YES;
  2470.                 else if (dom_string_caseless_lwc_isequal(s,
  2471.                                 corestring_lwc_no))
  2472.                         iframe->scrolling = SCROLLING_NO;
  2473.                 dom_string_unref(s);
  2474.         }
  2475.  
  2476.         err = dom_element_get_attribute(n, kstr_marginwidth, &s);
  2477.         if (err == DOM_NO_ERR && s != NULL) {
  2478.                 iframe->margin_width = atoi(dom_string_data(s));
  2479.                 dom_string_unref(s);
  2480.         }
  2481.  
  2482.         err = dom_element_get_attribute(n, kstr_marginheight, &s);
  2483.         if (err == DOM_NO_ERR && s != NULL) {
  2484.                 iframe->margin_height = atoi(dom_string_data(s));
  2485.                 dom_string_unref(s);
  2486.         }
  2487.  
  2488.         /* box */
  2489.         assert(box->style);
  2490.         box->flags |= IFRAME;
  2491.  
  2492.         /* Showing iframe, so don't show alternate content */
  2493.         if (convert_children)
  2494.                 *convert_children = false;
  2495.         return true;
  2496. }
  2497.  
  2498.  
  2499. /**
  2500.  * Form control [17.4].
  2501.  */
  2502.  
  2503. bool box_input(BOX_SPECIAL_PARAMS)
  2504. {
  2505.         struct form_control *gadget = NULL;
  2506.         dom_string *type = NULL;
  2507.         dom_exception err;
  2508.         nsurl *url;
  2509.         nserror error;
  2510.  
  2511.         dom_element_get_attribute(n, kstr_type, &type);
  2512.  
  2513.         gadget = html_forms_get_control_for_node(content->forms, n);
  2514.         if (gadget == NULL)
  2515.                 goto no_memory;
  2516.         box->gadget = gadget;
  2517.         gadget->box = box;
  2518.  
  2519.         if (type && dom_string_caseless_lwc_isequal(type,
  2520.                         corestring_lwc_password)) {
  2521.                 if (box_input_text(n, content, box, 0, true) == false)
  2522.                         goto no_memory;
  2523.  
  2524.         } else if (type && dom_string_caseless_lwc_isequal(type,
  2525.                         corestring_lwc_file)) {
  2526.                 box->type = BOX_INLINE_BLOCK;
  2527.  
  2528.         } else if (type && dom_string_caseless_lwc_isequal(type,
  2529.                         corestring_lwc_hidden)) {
  2530.                 /* no box for hidden inputs */
  2531.                 box->type = BOX_NONE;
  2532.  
  2533.         } else if (type &&
  2534.                         (dom_string_caseless_lwc_isequal(type,
  2535.                                 corestring_lwc_checkbox) ||
  2536.                         dom_string_caseless_lwc_isequal(type,
  2537.                                 corestring_lwc_radio))) {
  2538.  
  2539.         } else if (type &&
  2540.                         (dom_string_caseless_lwc_isequal(type,
  2541.                                 corestring_lwc_submit) ||
  2542.                         dom_string_caseless_lwc_isequal(type,
  2543.                                 corestring_lwc_reset) ||
  2544.                         dom_string_caseless_lwc_isequal(type,
  2545.                                 corestring_lwc_button))) {
  2546.                 struct box *inline_container, *inline_box;
  2547.  
  2548.                 if (box_button(n, content, box, 0) == false)
  2549.                         goto no_memory;
  2550.  
  2551.                 inline_container = box_create(NULL, 0, false, 0, 0, 0, 0,
  2552.                                 content->bctx);
  2553.                 if (inline_container == NULL)
  2554.                         goto no_memory;
  2555.  
  2556.                 inline_container->type = BOX_INLINE_CONTAINER;
  2557.  
  2558.                 inline_box = box_create(NULL, box->style, false, 0, 0,
  2559.                                 box->title, 0, content->bctx);
  2560.                 if (inline_box == NULL)
  2561.                         goto no_memory;
  2562.  
  2563.                 inline_box->type = BOX_TEXT;
  2564.  
  2565.                 if (box->gadget->value != NULL)
  2566.                         inline_box->text = talloc_strdup(content->bctx,
  2567.                                         box->gadget->value);
  2568.                 else if (box->gadget->type == GADGET_SUBMIT)
  2569.                         inline_box->text = talloc_strdup(content->bctx,
  2570.                                         messages_get("Form_Submit"));
  2571.                 else if (box->gadget->type == GADGET_RESET)
  2572.                         inline_box->text = talloc_strdup(content->bctx,
  2573.                                         messages_get("Form_Reset"));
  2574.                 else
  2575.                         inline_box->text = talloc_strdup(content->bctx,
  2576.                                                          "Button");
  2577.  
  2578.                 if (inline_box->text == NULL)
  2579.                         goto no_memory;
  2580.  
  2581.                 inline_box->length = strlen(inline_box->text);
  2582.  
  2583.                 box_add_child(inline_container, inline_box);
  2584.  
  2585.                 box_add_child(box, inline_container);
  2586.  
  2587.         } else if (type && dom_string_caseless_lwc_isequal(type,
  2588.                         corestring_lwc_image)) {
  2589.                 gadget->type = GADGET_IMAGE;
  2590.  
  2591.                 if (box->style && css_computed_display(box->style,
  2592.                                 box_is_root(n)) != CSS_DISPLAY_NONE &&
  2593.                     nsoption_bool(foreground_images) == true) {
  2594.                         dom_string *s;
  2595.  
  2596.                         err = dom_element_get_attribute(n, kstr_src, &s);
  2597.                         if (err == DOM_NO_ERR && s != NULL) {
  2598.                                 error = nsurl_join(content->base_url,
  2599.                                                 dom_string_data(s), &url);
  2600.                                 dom_string_unref(s);
  2601.                                 if (error != NSERROR_OK)
  2602.                                         goto no_memory;
  2603.  
  2604.                                 /* if url is equivalent to the parent's url,
  2605.                                  * we've got infinite inclusion. stop it here
  2606.                                  */
  2607.                                 if (nsurl_compare(url, content->base_url,
  2608.                                                 NSURL_COMPLETE) == false) {
  2609.                                         if (!html_fetch_object(content, url,
  2610.                                                         box, image_types,
  2611.                                                         content->base.
  2612.                                                         available_width,
  2613.                                                         1000, false)) {
  2614.                                                 nsurl_unref(url);
  2615.                                                 goto no_memory;
  2616.                                         }
  2617.                                 }
  2618.                                 nsurl_unref(url);
  2619.                         }
  2620.                 }
  2621.         } else {
  2622.                 /* the default type is "text" */
  2623.                 if (box_input_text(n, content, box, 0, false) == false)
  2624.                         goto no_memory;
  2625.         }
  2626.  
  2627.         if (type)
  2628.                 dom_string_unref(type);
  2629.  
  2630.         *convert_children = false;
  2631.         return true;
  2632.  
  2633. no_memory:
  2634.         if (type)
  2635.                 dom_string_unref(type);
  2636.  
  2637.         return false;
  2638. }
  2639.  
  2640.  
  2641. /**
  2642.  * Helper function for box_input().
  2643.  */
  2644.  
  2645. bool box_input_text(BOX_SPECIAL_PARAMS, bool password)
  2646. {
  2647.         struct box *inline_container, *inline_box;
  2648.  
  2649.         box->type = BOX_INLINE_BLOCK;
  2650.  
  2651.         inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, content->bctx);
  2652.         if (!inline_container)
  2653.                 return false;
  2654.         inline_container->type = BOX_INLINE_CONTAINER;
  2655.         inline_box = box_create(NULL, box->style, false, 0, 0, box->title, 0,
  2656.                         content->bctx);
  2657.         if (!inline_box)
  2658.                 return false;
  2659.         inline_box->type = BOX_TEXT;
  2660.         if (password) {
  2661.                 inline_box->length = strlen(box->gadget->value);
  2662.                 inline_box->text = talloc_array(content->bctx, char,
  2663.                                 inline_box->length + 1);
  2664.                 if (!inline_box->text)
  2665.                         return false;
  2666.                 memset(inline_box->text, '*', inline_box->length);
  2667.                 inline_box->text[inline_box->length] = '\0';
  2668.         } else {
  2669.                 /* replace spaces/TABs with hard spaces to prevent line
  2670.                  * wrapping */
  2671.                 char *text = cnv_space2nbsp(box->gadget->value);
  2672.                 if (!text)
  2673.                         return false;
  2674.                 inline_box->text = talloc_strdup(content->bctx, text);
  2675.                 free(text);
  2676.                 if (!inline_box->text)
  2677.                         return false;
  2678.                 inline_box->length = strlen(inline_box->text);
  2679.         }
  2680.         box_add_child(inline_container, inline_box);
  2681.         box_add_child(box, inline_container);
  2682.  
  2683.         return true;
  2684. }
  2685.  
  2686.  
  2687. /**
  2688.  * Push button [17.5].
  2689.  */
  2690.  
  2691. bool box_button(BOX_SPECIAL_PARAMS)
  2692. {
  2693.         struct form_control *gadget;
  2694.  
  2695.         gadget = html_forms_get_control_for_node(content->forms, n);
  2696.         if (!gadget)
  2697.                 return false;
  2698.  
  2699.         box->gadget = gadget;
  2700.         gadget->box = box;
  2701.  
  2702.         box->type = BOX_INLINE_BLOCK;
  2703.  
  2704.         /* Just render the contents */
  2705.  
  2706.         return true;
  2707. }
  2708.  
  2709.  
  2710. /**
  2711.  * Option selector [17.6].
  2712.  */
  2713.  
  2714. bool box_select(BOX_SPECIAL_PARAMS)
  2715. {
  2716.         struct box *inline_container;
  2717.         struct box *inline_box;
  2718.         struct form_control *gadget;
  2719.         dom_node *c, *c2;
  2720.         dom_node *next, *next2;
  2721.         dom_exception err;
  2722.  
  2723.         gadget = html_forms_get_control_for_node(content->forms, n);
  2724.         if (gadget == NULL)
  2725.                 return false;
  2726.  
  2727.         err = dom_node_get_first_child(n, &c);
  2728.         if (err != DOM_NO_ERR)
  2729.                 return false;
  2730.  
  2731.         while (c != NULL) {
  2732.                 dom_string *name;
  2733.  
  2734.                 err = dom_node_get_node_name(c, &name);
  2735.                 if (err != DOM_NO_ERR) {
  2736.                         dom_node_unref(c);
  2737.                         return false;
  2738.                 }
  2739.  
  2740.                 if (dom_string_caseless_lwc_isequal(name,
  2741.                                 corestring_lwc_option)) {
  2742.                         dom_string_unref(name);
  2743.  
  2744.                         if (box_select_add_option(gadget, c) == false) {
  2745.                                 dom_node_unref(c);
  2746.                                 goto no_memory;
  2747.                         }
  2748.                 } else if (dom_string_caseless_lwc_isequal(name,
  2749.                                 corestring_lwc_optgroup)) {
  2750.                         dom_string_unref(name);
  2751.  
  2752.                         err = dom_node_get_first_child(c, &c2);
  2753.                         if (err != DOM_NO_ERR) {
  2754.                                 dom_node_unref(c);
  2755.                                 return false;
  2756.                         }
  2757.  
  2758.                         while (c2 != NULL) {
  2759.                                 dom_string *c2_name;
  2760.  
  2761.                                 err = dom_node_get_node_name(c2, &c2_name);
  2762.                                 if (err != DOM_NO_ERR) {
  2763.                                         dom_node_unref(c2);
  2764.                                         dom_node_unref(c);
  2765.                                         return false;
  2766.                                 }
  2767.                                
  2768.                                 if (dom_string_caseless_lwc_isequal(c2_name,
  2769.                                                 corestring_lwc_option)) {
  2770.                                         dom_string_unref(c2_name);
  2771.  
  2772.                                         if (box_select_add_option(gadget,
  2773.                                                         c2) == false) {
  2774.                                                 dom_node_unref(c2);
  2775.                                                 dom_node_unref(c);
  2776.                                                 goto no_memory;
  2777.                                         }
  2778.                                 } else {
  2779.                                         dom_string_unref(c2_name);
  2780.                                 }
  2781.  
  2782.                                 err = dom_node_get_next_sibling(c2, &next2);
  2783.                                 if (err != DOM_NO_ERR) {
  2784.                                         dom_node_unref(c2);
  2785.                                         dom_node_unref(c);
  2786.                                         return false;
  2787.                                 }
  2788.  
  2789.                                 dom_node_unref(c2);
  2790.                                 c2 = next2;
  2791.                         }
  2792.                 } else {
  2793.                         dom_string_unref(name);
  2794.                 }
  2795.        
  2796.                 err = dom_node_get_next_sibling(c, &next);
  2797.                 if (err != DOM_NO_ERR) {
  2798.                         dom_node_unref(c);
  2799.                         return false;
  2800.                 }
  2801.  
  2802.                 dom_node_unref(c);
  2803.                 c = next;
  2804.         }
  2805.  
  2806.         if (gadget->data.select.num_items == 0) {
  2807.                 /* no options: ignore entire select */
  2808.                 return true;
  2809.         }
  2810.  
  2811.         box->type = BOX_INLINE_BLOCK;
  2812.         box->gadget = gadget;
  2813.         gadget->box = box;
  2814.  
  2815.         inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, content->bctx);
  2816.         if (inline_container == NULL)
  2817.                 goto no_memory;
  2818.         inline_container->type = BOX_INLINE_CONTAINER;
  2819.         inline_box = box_create(NULL, box->style, false, 0, 0, box->title, 0,
  2820.                         content->bctx);
  2821.         if (inline_box == NULL)
  2822.                 goto no_memory;
  2823.         inline_box->type = BOX_TEXT;
  2824.         box_add_child(inline_container, inline_box);
  2825.         box_add_child(box, inline_container);
  2826.  
  2827.         if (gadget->data.select.multiple == false &&
  2828.                         gadget->data.select.num_selected == 0) {
  2829.                 gadget->data.select.current = gadget->data.select.items;
  2830.                 gadget->data.select.current->initial_selected =
  2831.                         gadget->data.select.current->selected = true;
  2832.                 gadget->data.select.num_selected = 1;
  2833.         }
  2834.  
  2835.         if (gadget->data.select.num_selected == 0)
  2836.                 inline_box->text = talloc_strdup(content->bctx,
  2837.                                 messages_get("Form_None"));
  2838.         else if (gadget->data.select.num_selected == 1)
  2839.                 inline_box->text = talloc_strdup(content->bctx,
  2840.                                 gadget->data.select.current->text);
  2841.         else
  2842.                 inline_box->text = talloc_strdup(content->bctx,
  2843.                                 messages_get("Form_Many"));
  2844.         if (inline_box->text == NULL)
  2845.                 goto no_memory;
  2846.  
  2847.         inline_box->length = strlen(inline_box->text);
  2848.  
  2849.         *convert_children = false;
  2850.         return true;
  2851.  
  2852. no_memory:
  2853.         return false;
  2854. }
  2855.  
  2856.  
  2857. /**
  2858.  * Add an option to a form select control (helper function for box_select()).
  2859.  *
  2860.  * \param  control  select containing the option
  2861.  * \param  n        xml element node for <option>
  2862.  * \return  true on success, false on memory exhaustion
  2863.  */
  2864.  
  2865. bool box_select_add_option(struct form_control *control, dom_node *n)
  2866. {
  2867.         char *value = NULL;
  2868.         char *text = NULL;
  2869.         char *text_nowrap = NULL;
  2870.         bool selected;
  2871.         dom_string *content, *s;
  2872.         dom_exception err;
  2873.  
  2874.         err = dom_node_get_text_content(n, &content);
  2875.         if (err != DOM_NO_ERR)
  2876.                 return false;
  2877.  
  2878.         if (content != NULL) {
  2879.                 text = squash_whitespace(dom_string_data(content));
  2880.                 dom_string_unref(content);
  2881.         } else {
  2882.                 text = strdup("");
  2883.         }
  2884.  
  2885.         if (text == NULL)
  2886.                 goto no_memory;
  2887.  
  2888.         err = dom_element_get_attribute(n, kstr_value, &s);
  2889.         if (err == DOM_NO_ERR && s != NULL) {
  2890.                 value = strdup(dom_string_data(s));
  2891.                 dom_string_unref(s);
  2892.         } else {
  2893.                 value = strdup(text);
  2894.         }
  2895.  
  2896.         if (value == NULL)
  2897.                 goto no_memory;
  2898.  
  2899.         dom_element_has_attribute(n, kstr_selected, &selected);
  2900.  
  2901.         /* replace spaces/TABs with hard spaces to prevent line wrapping */
  2902.         text_nowrap = cnv_space2nbsp(text);
  2903.         if (text_nowrap == NULL)
  2904.                 goto no_memory;
  2905.  
  2906.         if (form_add_option(control, value, text_nowrap, selected) == false)
  2907.                 goto no_memory;
  2908.  
  2909.         free(text);
  2910.  
  2911.         return true;
  2912.  
  2913. no_memory:
  2914.         free(value);
  2915.         free(text);
  2916.         free(text_nowrap);
  2917.         return false;
  2918. }
  2919.  
  2920.  
  2921. /**
  2922.  * Multi-line text field [17.7].
  2923.  */
  2924.  
  2925. bool box_textarea(BOX_SPECIAL_PARAMS)
  2926. {
  2927.         /* A textarea is an INLINE_BLOCK containing a single INLINE_CONTAINER,
  2928.          * which contains the text as runs of TEXT separated by BR. There is
  2929.          * at least one TEXT. The first and last boxes are TEXT.
  2930.          * Consecutive BR may not be present. These constraints are satisfied
  2931.          * by using a 0-length TEXT for blank lines. */
  2932.  
  2933.         const char *current;
  2934.         dom_string *area_data = NULL;
  2935.         dom_exception err;
  2936.         struct box *inline_container, *inline_box, *br_box;
  2937.         char *s;
  2938.         size_t len;
  2939.  
  2940.         box->type = BOX_INLINE_BLOCK;
  2941.         box->gadget = html_forms_get_control_for_node(content->forms, n);
  2942.         if (box->gadget == NULL)
  2943.                 return false;
  2944.         box->gadget->box = box;
  2945.  
  2946.         inline_container = box_create(NULL, 0, false, 0, 0, box->title, 0,
  2947.                         content->bctx);
  2948.         if (inline_container == NULL)
  2949.                 return false;
  2950.         inline_container->type = BOX_INLINE_CONTAINER;
  2951.         box_add_child(box, inline_container);
  2952.  
  2953.         err = dom_node_get_text_content(n, &area_data);
  2954.         if (err != DOM_NO_ERR)
  2955.                 return false;
  2956.  
  2957.         if (area_data != NULL) {
  2958.                 current = dom_string_data(area_data);
  2959.         } else {
  2960.                 /* No content, or failed reading it: use a blank string */
  2961.                 current = "";
  2962.         }
  2963.  
  2964.         while (true) {
  2965.                 /* BOX_TEXT */
  2966.                 len = strcspn(current, "\r\n");
  2967.                 s = talloc_strndup(content->bctx, current, len);
  2968.                 if (s == NULL) {
  2969.                         if (area_data != NULL)
  2970.                                 dom_string_unref(area_data);
  2971.                         return false;
  2972.                 }
  2973.  
  2974.                 inline_box = box_create(NULL, box->style, false, 0, 0,
  2975.                                 box->title, 0, content->bctx);
  2976.                 if (inline_box == NULL) {
  2977.                         if (area_data != NULL)
  2978.                                 dom_string_unref(area_data);
  2979.                         return false;
  2980.                 }
  2981.                 inline_box->type = BOX_TEXT;
  2982.                 inline_box->text = s;
  2983.                 inline_box->length = len;
  2984.                 box_add_child(inline_container, inline_box);
  2985.  
  2986.                 current += len;
  2987.                 if (current[0] == 0)
  2988.                         /* finished */
  2989.                         break;
  2990.  
  2991.                 /* BOX_BR */
  2992.                 br_box = box_create(NULL, box->style, false, 0, 0, box->title,
  2993.                                 0, content->bctx);
  2994.                 if (br_box == NULL) {
  2995.                         if (area_data != NULL)
  2996.                                 dom_string_unref(area_data);
  2997.                         return false;
  2998.                 }
  2999.                 br_box->type = BOX_BR;
  3000.                 box_add_child(inline_container, br_box);
  3001.  
  3002.                 if (current[0] == '\r' && current[1] == '\n')
  3003.                         current += 2;
  3004.                 else
  3005.                         current++;
  3006.         }
  3007.  
  3008.         if (area_data != NULL)
  3009.                 dom_string_unref(area_data);
  3010.  
  3011.         *convert_children = false;
  3012.         return true;
  3013. }
  3014.  
  3015.  
  3016. /**
  3017.  * Embedded object (not in any HTML specification:
  3018.  * see http://wp.netscape.com/assist/net_sites/new_html3_prop.html )
  3019.  */
  3020.  
  3021. bool box_embed(BOX_SPECIAL_PARAMS)
  3022. {
  3023.         struct object_params *params;
  3024.         struct object_param *param;
  3025.         dom_namednodemap *attrs;
  3026.         unsigned long idx;
  3027.         uint32_t num_attrs;
  3028.         dom_string *src;
  3029.         dom_exception err;
  3030.  
  3031.         if (box->style && css_computed_display(box->style,
  3032.                         box_is_root(n)) == CSS_DISPLAY_NONE)
  3033.                 return true;
  3034.  
  3035.         params = talloc(content->bctx, struct object_params);
  3036.         if (params == NULL)
  3037.                 return false;
  3038.  
  3039.         talloc_set_destructor(params, box_object_talloc_destructor);
  3040.  
  3041.         params->data = NULL;
  3042.         params->type = NULL;
  3043.         params->codetype = NULL;
  3044.         params->codebase = NULL;
  3045.         params->classid = NULL;
  3046.         params->params = NULL;
  3047.  
  3048.         /* src is a URL */
  3049.         err = dom_element_get_attribute(n, kstr_src, &src);
  3050.         if (err != DOM_NO_ERR || src == NULL)
  3051.                 return true;
  3052.         if (box_extract_link(dom_string_data(src), content->base_url,
  3053.                         &params->data) == false) {
  3054.                 dom_string_unref(src);
  3055.                 return false;
  3056.         }
  3057.  
  3058.         dom_string_unref(src);
  3059.  
  3060.         if (params->data == NULL)
  3061.                 return true;
  3062.  
  3063.         /* Don't include ourself */
  3064.         if (nsurl_compare(content->base_url, params->data, NSURL_COMPLETE))
  3065.                 return true;
  3066.  
  3067.         /* add attributes as parameters to linked list */
  3068.         err = dom_node_get_attributes(n, &attrs);
  3069.         if (err != DOM_NO_ERR)
  3070.                 return false;
  3071.  
  3072.         err = dom_namednodemap_get_length(attrs, &num_attrs);
  3073.         if (err != DOM_NO_ERR) {
  3074.                 dom_namednodemap_unref(attrs);
  3075.                 return false;
  3076.         }
  3077.  
  3078.         for (idx = 0; idx < num_attrs; idx++) {
  3079.                 dom_attr *attr;
  3080.                 dom_string *name, *value;
  3081.  
  3082.                 err = dom_namednodemap_item(attrs, idx, (void *) &attr);
  3083.                 if (err != DOM_NO_ERR) {
  3084.                         dom_namednodemap_unref(attrs);
  3085.                         return false;
  3086.                 }
  3087.  
  3088.                 err = dom_attr_get_name(attr, &name);
  3089.                 if (err != DOM_NO_ERR) {
  3090.                         dom_namednodemap_unref(attrs);
  3091.                         return false;
  3092.                 }
  3093.  
  3094.                 if (dom_string_caseless_lwc_isequal(name, corestring_lwc_src)) {
  3095.                         dom_string_unref(name);
  3096.                         continue;
  3097.                 }
  3098.  
  3099.                 err = dom_attr_get_value(attr, &value);
  3100.                 if (err != DOM_NO_ERR) {
  3101.                         dom_string_unref(name);
  3102.                         dom_namednodemap_unref(attrs);
  3103.                         return false;
  3104.                 }
  3105.  
  3106.                 param = talloc(content->bctx, struct object_param);
  3107.                 if (param == NULL) {
  3108.                         dom_string_unref(value);
  3109.                         dom_string_unref(name);
  3110.                         dom_namednodemap_unref(attrs);
  3111.                         return false;
  3112.                 }
  3113.  
  3114.                 param->name = talloc_strdup(content->bctx, dom_string_data(name));
  3115.                 param->value = talloc_strdup(content->bctx, dom_string_data(value));
  3116.                 param->type = NULL;
  3117.                 param->valuetype = talloc_strdup(content->bctx, "data");
  3118.                 param->next = NULL;
  3119.  
  3120.                 dom_string_unref(value);
  3121.                 dom_string_unref(name);
  3122.  
  3123.                 if (param->name == NULL || param->value == NULL ||
  3124.                                 param->valuetype == NULL) {
  3125.                         dom_namednodemap_unref(attrs);
  3126.                         return false;
  3127.                 }
  3128.  
  3129.                 param->next = params->params;
  3130.                 params->params = param;
  3131.         }
  3132.  
  3133.         dom_namednodemap_unref(attrs);
  3134.  
  3135.         box->object_params = params;
  3136.  
  3137.         /* start fetch */
  3138.         return html_fetch_object(content, params->data, box, CONTENT_ANY,
  3139.                         content->base.available_width, 1000, false);
  3140. }
  3141.  
  3142. /**
  3143.  * \}
  3144.  */
  3145.  
  3146.  
  3147. /**
  3148.  * Get the value of an XML element's attribute.
  3149.  *
  3150.  * \param  n          xmlNode, of type XML_ELEMENT_NODE
  3151.  * \param  attribute  name of attribute
  3152.  * \param  context    talloc context for result buffer
  3153.  * \param  value      updated to value, if the attribute is present
  3154.  * \return  true on success, false if attribute present but memory exhausted
  3155.  *
  3156.  * Note that returning true does not imply that the attribute was found. If the
  3157.  * attribute was not found, *value will be unchanged.
  3158.  */
  3159.  
  3160. bool box_get_attribute(dom_node *n, const char *attribute,
  3161.                 void *context, char **value)
  3162. {
  3163.         char *result;
  3164.         dom_string *attr, *attr_name;
  3165.         dom_exception err;
  3166.  
  3167.         err = dom_string_create_interned((const uint8_t *) attribute,
  3168.                         strlen(attribute), &attr_name);
  3169.         if (err != DOM_NO_ERR)
  3170.                 return false;
  3171.  
  3172.         err = dom_element_get_attribute(n, attr_name, &attr);
  3173.         if (err != DOM_NO_ERR) {
  3174.                 dom_string_unref(attr_name);
  3175.                 return false;
  3176.         }
  3177.  
  3178.         dom_string_unref(attr_name);
  3179.  
  3180.         if (attr != NULL) {
  3181.                 result = talloc_strdup(context, dom_string_data(attr));
  3182.  
  3183.                 dom_string_unref(attr);
  3184.        
  3185.                 if (result == NULL)
  3186.                         return false;
  3187.  
  3188.                 *value = result;
  3189.         }
  3190.  
  3191.         return true;
  3192. }
  3193.  
  3194.  
  3195. /**
  3196.  * Extract a URL from a relative link, handling junk like whitespace and
  3197.  * attempting to read a real URL from "javascript:" links.
  3198.  *
  3199.  * \param  rel     relative URL taken from page
  3200.  * \param  base    base for relative URLs
  3201.  * \param  result  updated to target URL on heap, unchanged if extract failed
  3202.  * \return  true on success, false on memory exhaustion
  3203.  */
  3204.  
  3205. bool box_extract_link(const char *rel, nsurl *base, nsurl **result)
  3206. {
  3207.         char *s, *s1, *apos0 = 0, *apos1 = 0, *quot0 = 0, *quot1 = 0;
  3208.         unsigned int i, j, end;
  3209.         nserror error;
  3210.  
  3211.         s1 = s = malloc(3 * strlen(rel) + 1);
  3212.         if (!s)
  3213.                 return false;
  3214.  
  3215.         /* copy to s, removing white space and control characters */
  3216.         for (i = 0; rel[i] && isspace(rel[i]); i++)
  3217.                 ;
  3218.         for (end = strlen(rel); end != i && isspace(rel[end - 1]); end--)
  3219.                 ;
  3220.         for (j = 0; i != end; i++) {
  3221.                 if ((unsigned char) rel[i] < 0x20) {
  3222.                         ; /* skip control characters */
  3223.                 } else if (rel[i] == ' ') {
  3224.                         s[j++] = '%';
  3225.                         s[j++] = '2';
  3226.                         s[j++] = '0';
  3227.                 } else {
  3228.                         s[j++] = rel[i];
  3229.                 }
  3230.         }
  3231.         s[j] = 0;
  3232.  
  3233.         /* extract first quoted string out of "javascript:" link */
  3234.         if (strncmp(s, "javascript:", 11) == 0) {
  3235.                 apos0 = strchr(s, '\'');
  3236.                 if (apos0)
  3237.                         apos1 = strchr(apos0 + 1, '\'');
  3238.                 quot0 = strchr(s, '"');
  3239.                 if (quot0)
  3240.                         quot1 = strchr(quot0 + 1, '"');
  3241.                 if (apos0 && apos1 && (!quot0 || !quot1 || apos0 < quot0)) {
  3242.                         *apos1 = 0;
  3243.                         s1 = apos0 + 1;
  3244.                 } else if (quot0 && quot1) {
  3245.                         *quot1 = 0;
  3246.                         s1 = quot0 + 1;
  3247.                 }
  3248.         }
  3249.  
  3250.         /* construct absolute URL */
  3251.         error = nsurl_join(base, s1, result);
  3252.         free(s);
  3253.         if (error != NSERROR_OK) {
  3254.                 *result = NULL;
  3255.                 return false;
  3256.         }
  3257.  
  3258.         return true;
  3259. }
  3260.  
  3261.  
  3262. /**
  3263.  * Parse a multi-length-list, as defined by HTML 4.01.
  3264.  *
  3265.  * \param  s        string to parse
  3266.  * \param  count    updated to number of entries
  3267.  * \return  array of struct box_multi_length, or 0 on memory exhaustion
  3268.  */
  3269.  
  3270. struct frame_dimension *box_parse_multi_lengths(const char *s,
  3271.                 unsigned int *count)
  3272. {
  3273.         char *end;
  3274.         unsigned int i, n;
  3275.         struct frame_dimension *length;
  3276.  
  3277.         for (i = 0, n = 1; s[i]; i++)
  3278.                 if (s[i] == ',')
  3279.                         n++;
  3280.  
  3281.         length = calloc(n, sizeof(struct frame_dimension));
  3282.         if (!length)
  3283.                 return NULL;
  3284.  
  3285.         for (i = 0; i != n; i++) {
  3286.                 while (isspace(*s))
  3287.                         s++;
  3288.                 length[i].value = strtof(s, &end);
  3289.                 if (length[i].value <= 0)
  3290.                         length[i].value = 1;
  3291.                 s = end;
  3292.                 switch (*s) {
  3293.                         case '%':
  3294.                                 length[i].unit = FRAME_DIMENSION_PERCENT;
  3295.                                 break;
  3296.                         case '*':
  3297.                                 length[i].unit = FRAME_DIMENSION_RELATIVE;
  3298.                                 break;
  3299.                         default:
  3300.                                 length[i].unit = FRAME_DIMENSION_PIXELS;
  3301.                                 break;
  3302.                 }
  3303.                 while (*s && *s != ',')
  3304.                         s++;
  3305.                 if (*s == ',')
  3306.                         s++;
  3307.         }
  3308.  
  3309.         *count = n;
  3310.         return length;
  3311. }
  3312.  
  3313.