diff options
Diffstat (limited to 'render/box_construct.c')
-rw-r--r-- | render/box_construct.c | 3137 |
1 files changed, 0 insertions, 3137 deletions
diff --git a/render/box_construct.c b/render/box_construct.c deleted file mode 100644 index 1aa99e2d1..000000000 --- a/render/box_construct.c +++ /dev/null @@ -1,3137 +0,0 @@ -/* - * Copyright 2005 James Bursa <bursa@users.sourceforge.net> - * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> - * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk> - * Copyright 2006 Richard Wilson <info@tinct.net> - * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org> - * - * This file is part of NetSurf, http://www.netsurf-browser.org/ - * - * NetSurf is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * NetSurf is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * Implementation of conversion from DOM tree to box tree. - */ - -#include <assert.h> -#include <stdio.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <strings.h> - -#include "utils/config.h" -#include "utils/nsoption.h" -#include "utils/corestrings.h" -#include "utils/log.h" -#include "utils/messages.h" -#include "utils/talloc.h" -#include "utils/string.h" -#include "utils/ascii.h" -#include "netsurf/css.h" -#include "netsurf/misc.h" -#include "netsurf/plot_style.h" -#include "content/content_protected.h" -#include "css/hints.h" -#include "css/select.h" -#include "css/utils.h" -#include "desktop/gui_internal.h" - -#include "render/box.h" -#include "render/box_textarea.h" -#include "render/form_internal.h" -#include "render/html_internal.h" - -/** - * Context for box tree construction - */ -struct box_construct_ctx { - html_content *content; /**< Content we're constructing for */ - - dom_node *n; /**< Current node to process */ - - struct box *root_box; /**< Root box in the tree */ - - box_construct_complete_cb cb; /**< Callback to invoke on completion */ - - int *bctx; /**< talloc context */ -}; - -/** - * Transient properties for construction of current node - */ -struct box_construct_props { - /** Style from which to inherit, or NULL if none */ - const css_computed_style *parent_style; - /** Current link target, or NULL if none */ - nsurl *href; - /** Current frame target, or NULL if none */ - const char *target; - /** Current title attribute, or NULL if none */ - const char *title; - /** Identity of the current block-level container */ - struct box *containing_block; - /** Current container for inlines, or NULL if none - * \note If non-NULL, will be the last child of containing_block */ - struct box *inline_container; - /** Whether the current node is the root of the DOM tree */ - bool node_is_root; -}; - -static const content_type image_types = CONTENT_IMAGE; - -/* the strings are not important, since we just compare the pointers */ -const char *TARGET_SELF = "_self"; -const char *TARGET_PARENT = "_parent"; -const char *TARGET_TOP = "_top"; -const char *TARGET_BLANK = "_blank"; - -static void convert_xml_to_box(struct box_construct_ctx *ctx); -static bool box_construct_element(struct box_construct_ctx *ctx, - bool *convert_children); -static void box_construct_element_after(dom_node *n, html_content *content); -static bool box_construct_text(struct box_construct_ctx *ctx); -static css_select_results * box_get_style(html_content *c, - const css_computed_style *parent_style, - const css_computed_style *root_style, dom_node *n); -static void box_text_transform(char *s, unsigned int len, - enum css_text_transform_e tt); -#define BOX_SPECIAL_PARAMS dom_node *n, html_content *content, \ - struct box *box, bool *convert_children -static bool box_a(BOX_SPECIAL_PARAMS); -static bool box_body(BOX_SPECIAL_PARAMS); -static bool box_br(BOX_SPECIAL_PARAMS); -static bool box_image(BOX_SPECIAL_PARAMS); -static bool box_textarea(BOX_SPECIAL_PARAMS); -static bool box_select(BOX_SPECIAL_PARAMS); -static bool box_input(BOX_SPECIAL_PARAMS); -static bool box_button(BOX_SPECIAL_PARAMS); -static bool box_frameset(BOX_SPECIAL_PARAMS); -static bool box_create_frameset(struct content_html_frames *f, dom_node *n, - html_content *content); -static bool box_select_add_option(struct form_control *control, dom_node *n); -static bool box_noscript(BOX_SPECIAL_PARAMS); -static bool box_object(BOX_SPECIAL_PARAMS); -static bool box_embed(BOX_SPECIAL_PARAMS); -static bool box_pre(BOX_SPECIAL_PARAMS); -static bool box_iframe(BOX_SPECIAL_PARAMS); -static bool box_get_attribute(dom_node *n, const char *attribute, - void *context, char **value); - -/* element_table must be sorted by name */ -struct element_entry { - char name[10]; /* element type */ - bool (*convert)(BOX_SPECIAL_PARAMS); -}; -static const struct element_entry element_table[] = { - {"a", box_a}, - {"body", box_body}, - {"br", box_br}, - {"button", box_button}, - {"embed", box_embed}, - {"frameset", box_frameset}, - {"iframe", box_iframe}, - {"image", box_image}, - {"img", box_image}, - {"input", box_input}, - {"noscript", box_noscript}, - {"object", box_object}, - {"pre", box_pre}, - {"select", box_select}, - {"textarea", box_textarea} -}; -#define ELEMENT_TABLE_COUNT (sizeof(element_table) / sizeof(element_table[0])) - -/** - * Construct a box tree from an xml tree and stylesheets. - * - * \param n xml tree - * \param c content of type CONTENT_HTML to construct box tree in - * \param cb callback to report conversion completion - * \return netsurf error code indicating status of call - */ - -nserror dom_to_box(dom_node *n, html_content *c, box_construct_complete_cb cb) -{ - struct box_construct_ctx *ctx; - - if (c->bctx == NULL) { - /* create a context allocation for this box tree */ - c->bctx = talloc_zero(0, int); - if (c->bctx == NULL) { - return NSERROR_NOMEM; - } - } - - ctx = malloc(sizeof(*ctx)); - if (ctx == NULL) { - return NSERROR_NOMEM; - } - - ctx->content = c; - ctx->n = dom_node_ref(n); - ctx->root_box = NULL; - ctx->cb = cb; - ctx->bctx = c->bctx; - - return guit->misc->schedule(0, (void *)convert_xml_to_box, ctx); -} - -/* mapping from CSS display to box type - * this table must be in sync with libcss' css_display enum */ -static const box_type box_map[] = { - 0, /*CSS_DISPLAY_INHERIT,*/ - BOX_INLINE, /*CSS_DISPLAY_INLINE,*/ - BOX_BLOCK, /*CSS_DISPLAY_BLOCK,*/ - BOX_BLOCK, /*CSS_DISPLAY_LIST_ITEM,*/ - BOX_INLINE, /*CSS_DISPLAY_RUN_IN,*/ - BOX_INLINE_BLOCK, /*CSS_DISPLAY_INLINE_BLOCK,*/ - BOX_TABLE, /*CSS_DISPLAY_TABLE,*/ - BOX_TABLE, /*CSS_DISPLAY_INLINE_TABLE,*/ - BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_ROW_GROUP,*/ - BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_HEADER_GROUP,*/ - BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_FOOTER_GROUP,*/ - BOX_TABLE_ROW, /*CSS_DISPLAY_TABLE_ROW,*/ - BOX_NONE, /*CSS_DISPLAY_TABLE_COLUMN_GROUP,*/ - BOX_NONE, /*CSS_DISPLAY_TABLE_COLUMN,*/ - BOX_TABLE_CELL, /*CSS_DISPLAY_TABLE_CELL,*/ - BOX_INLINE, /*CSS_DISPLAY_TABLE_CAPTION,*/ - BOX_NONE /*CSS_DISPLAY_NONE*/ -}; - -static inline struct box *box_for_node(dom_node *n) -{ - struct box *box = NULL; - dom_exception err; - - err = dom_node_get_user_data(n, corestring_dom___ns_key_box_node_data, - (void *) &box); - if (err != DOM_NO_ERR) - return NULL; - - return box; -} - -static inline bool box_is_root(dom_node *n) -{ - dom_node *parent; - dom_node_type type; - dom_exception err; - - err = dom_node_get_parent_node(n, &parent); - if (err != DOM_NO_ERR) - return false; - - if (parent != NULL) { - err = dom_node_get_node_type(parent, &type); - - dom_node_unref(parent); - - if (err != DOM_NO_ERR) - return false; - - if (type != DOM_DOCUMENT_NODE) - return false; - } - - return true; -} - -/** - * Find the next node in the DOM tree, completing - * element construction where appropriate. - * - * \param n Current node - * \param content Containing content - * \param convert_children Whether to consider children of \a n - * \return Next node to process, or NULL if complete - * - * \note \a n will be unreferenced - */ -static dom_node *next_node(dom_node *n, html_content *content, - bool convert_children) -{ - dom_node *next = NULL; - bool has_children; - dom_exception err; - - err = dom_node_has_child_nodes(n, &has_children); - if (err != DOM_NO_ERR) { - dom_node_unref(n); - return NULL; - } - - if (convert_children && has_children) { - err = dom_node_get_first_child(n, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(n); - return NULL; - } - dom_node_unref(n); - } else { - err = dom_node_get_next_sibling(n, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(n); - return NULL; - } - - if (next != NULL) { - if (box_for_node(n) != NULL) - box_construct_element_after(n, content); - dom_node_unref(n); - } else { - if (box_for_node(n) != NULL) - box_construct_element_after(n, content); - - while (box_is_root(n) == false) { - dom_node *parent = NULL; - dom_node *parent_next = NULL; - - err = dom_node_get_parent_node(n, &parent); - if (err != DOM_NO_ERR) { - dom_node_unref(n); - return NULL; - } - - assert(parent != NULL); - - err = dom_node_get_next_sibling(parent, - &parent_next); - if (err != DOM_NO_ERR) { - dom_node_unref(parent); - dom_node_unref(n); - return NULL; - } - - if (parent_next != NULL) { - dom_node_unref(parent_next); - dom_node_unref(parent); - break; - } - - dom_node_unref(n); - n = parent; - parent = NULL; - - if (box_for_node(n) != NULL) { - box_construct_element_after( - n, content); - } - } - - if (box_is_root(n) == false) { - dom_node *parent = NULL; - - err = dom_node_get_parent_node(n, &parent); - if (err != DOM_NO_ERR) { - dom_node_unref(n); - return NULL; - } - - assert(parent != NULL); - - err = dom_node_get_next_sibling(parent, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(parent); - dom_node_unref(n); - return NULL; - } - - if (box_for_node(parent) != NULL) { - box_construct_element_after(parent, - content); - } - - dom_node_unref(parent); - } - - dom_node_unref(n); - } - } - - return next; -} - -/** - * Convert an ELEMENT node to a box tree fragment, - * then schedule conversion of the next ELEMENT node - */ -void convert_xml_to_box(struct box_construct_ctx *ctx) -{ - dom_node *next; - bool convert_children; - uint32_t num_processed = 0; - const uint32_t max_processed_before_yield = 10; - - do { - convert_children = true; - - assert(ctx->n != NULL); - - if (box_construct_element(ctx, &convert_children) == false) { - ctx->cb(ctx->content, false); - dom_node_unref(ctx->n); - free(ctx); - return; - } - - /* Find next element to process, converting text nodes as we go */ - next = next_node(ctx->n, ctx->content, convert_children); - while (next != NULL) { - dom_node_type type; - dom_exception err; - - err = dom_node_get_node_type(next, &type); - if (err != DOM_NO_ERR) { - ctx->cb(ctx->content, false); - dom_node_unref(next); - free(ctx); - return; - } - - if (type == DOM_ELEMENT_NODE) - break; - - if (type == DOM_TEXT_NODE) { - ctx->n = next; - if (box_construct_text(ctx) == false) { - ctx->cb(ctx->content, false); - dom_node_unref(ctx->n); - free(ctx); - return; - } - } - - next = next_node(next, ctx->content, true); - } - - ctx->n = next; - - if (next == NULL) { - /* Conversion complete */ - struct box root; - - memset(&root, 0, sizeof(root)); - - root.type = BOX_BLOCK; - root.children = root.last = ctx->root_box; - root.children->parent = &root; - - /** \todo Remove box_normalise_block */ - if (box_normalise_block(&root, ctx->root_box, - ctx->content) == false) { - ctx->cb(ctx->content, false); - } else { - ctx->content->layout = root.children; - ctx->content->layout->parent = NULL; - - ctx->cb(ctx->content, true); - } - - assert(ctx->n == NULL); - - free(ctx); - return; - } - } while (++num_processed < max_processed_before_yield); - - /* More work to do: schedule a continuation */ - guit->misc->schedule(0, (void *)convert_xml_to_box, ctx); -} - -/** - * Construct a list marker box - * - * \param box Box to attach marker to - * \param title Current title attribute - * \param ctx Box construction context - * \param parent Current block-level container - * \return true on success, false on memory exhaustion - */ -static bool box_construct_marker(struct box *box, const char *title, - struct box_construct_ctx *ctx, struct box *parent) -{ - lwc_string *image_uri; - struct box *marker; - - marker = box_create(NULL, box->style, false, NULL, NULL, title, - NULL, ctx->bctx); - if (marker == false) - return false; - - marker->type = BOX_BLOCK; - - /** \todo marker content (list-style-type) */ - switch (css_computed_list_style_type(box->style)) { - case CSS_LIST_STYLE_TYPE_DISC: - /* 2022 BULLET */ - marker->text = (char *) "\342\200\242"; - marker->length = 3; - break; - case CSS_LIST_STYLE_TYPE_CIRCLE: - /* 25CB WHITE CIRCLE */ - marker->text = (char *) "\342\227\213"; - marker->length = 3; - break; - case CSS_LIST_STYLE_TYPE_SQUARE: - /* 25AA BLACK SMALL SQUARE */ - marker->text = (char *) "\342\226\252"; - marker->length = 3; - break; - case CSS_LIST_STYLE_TYPE_DECIMAL: - case CSS_LIST_STYLE_TYPE_LOWER_ALPHA: - case CSS_LIST_STYLE_TYPE_LOWER_ROMAN: - case CSS_LIST_STYLE_TYPE_UPPER_ALPHA: - case CSS_LIST_STYLE_TYPE_UPPER_ROMAN: - default: - if (parent->last) { - struct box *last = parent->last; - - /* Drill down into last child of parent - * to find the list marker (if any) - * - * Floated list boxes end up as: - * - * parent - * BOX_INLINE_CONTAINER - * BOX_FLOAT_{LEFT,RIGHT} - * BOX_BLOCK <-- list box - * ... - */ - while (last != NULL && last->list_marker == NULL) { - struct box *last_inner = last; - - while (last_inner != NULL) { - if (last_inner->list_marker != NULL) - break; - if (last_inner->type == - BOX_INLINE_CONTAINER || - last_inner->type == - BOX_FLOAT_LEFT || - last_inner->type == - BOX_FLOAT_RIGHT) { - last_inner = last_inner->last; - } else { - last_inner = NULL; - } - } - if (last_inner != NULL) { - last = last_inner; - } else { - last = last->prev; - } - } - - if (last && last->list_marker) { - marker->rows = last->list_marker->rows + 1; - } - } - - marker->text = talloc_array(ctx->bctx, char, 20); - if (marker->text == NULL) - return false; - - snprintf(marker->text, 20, "%u.", marker->rows); - marker->length = strlen(marker->text); - break; - case CSS_LIST_STYLE_TYPE_NONE: - marker->text = 0; - marker->length = 0; - break; - } - - if (css_computed_list_style_image(box->style, &image_uri) == CSS_LIST_STYLE_IMAGE_URI && - (image_uri != NULL) && - (nsoption_bool(foreground_images) == true)) { - nsurl *url; - nserror error; - - /* TODO: we get a url out of libcss as a lwc string, but - * earlier we already had it as a nsurl after we - * nsurl_joined it. Can this be improved? - * For now, just making another nsurl. */ - error = nsurl_create(lwc_string_data(image_uri), &url); - if (error != NSERROR_OK) - return false; - - if (html_fetch_object(ctx->content, url, marker, image_types, - ctx->content->base.available_width, 1000, false) == - false) { - nsurl_unref(url); - return false; - } - nsurl_unref(url); - } - - box->list_marker = marker; - marker->parent = box; - - return true; -} - -/** - * Construct the box required for a generated element. - * - * \param n XML node of type XML_ELEMENT_NODE - * \param content Content of type CONTENT_HTML that is being processed - * \param box Box which may have generated content - * \param style Complete computed style for pseudo element, or NULL - * - * TODO: - * This is currently incomplete. It just does enough to support the clearfix - * hack. ( http://www.positioniseverything.net/easyclearing.html ) - */ -static void box_construct_generate(dom_node *n, html_content *content, - struct box *box, const css_computed_style *style) -{ - struct box *gen = NULL; - enum css_display_e computed_display; - const css_computed_content_item *c_item; - - /* Nothing to generate if the parent box is not a block */ - if (box->type != BOX_BLOCK) - return; - - /* To determine if an element has a pseudo element, we select - * for it and test to see if the returned style's content - * property is set to normal. */ - if (style == NULL || - css_computed_content(style, &c_item) == - CSS_CONTENT_NORMAL) { - /* No pseudo element */ - return; - } - - /* create box for this element */ - computed_display = ns_computed_display(style, box_is_root(n)); - if (computed_display == CSS_DISPLAY_BLOCK || - computed_display == CSS_DISPLAY_TABLE) { - /* currently only support block level boxes */ - - /** \todo Not wise to drop const from the computed style */ - gen = box_create(NULL, (css_computed_style *) style, - false, NULL, NULL, NULL, NULL, content->bctx); - if (gen == NULL) { - return; - } - - /* set box type from computed display */ - gen->type = box_map[ns_computed_display( - style, box_is_root(n))]; - - box_add_child(box, gen); - } -} - -/** - * Extract transient construction properties - * - * \param n Current DOM node to convert - * \param props Property object to populate - */ -static void box_extract_properties(dom_node *n, - struct box_construct_props *props) -{ - memset(props, 0, sizeof(*props)); - - props->node_is_root = box_is_root(n); - - /* Extract properties from containing DOM node */ - if (props->node_is_root == false) { - dom_node *current_node = n; - dom_node *parent_node = NULL; - struct box *parent_box; - dom_exception err; - - /* Find ancestor node containing parent box */ - while (true) { - err = dom_node_get_parent_node(current_node, - &parent_node); - if (err != DOM_NO_ERR || parent_node == NULL) - break; - - parent_box = box_for_node(parent_node); - - if (parent_box != NULL) { - props->parent_style = parent_box->style; - props->href = parent_box->href; - props->target = parent_box->target; - props->title = parent_box->title; - - dom_node_unref(parent_node); - break; - } else { - if (current_node != n) - dom_node_unref(current_node); - current_node = parent_node; - parent_node = NULL; - } - } - - /* Find containing block (may be parent) */ - while (true) { - struct box *b; - - err = dom_node_get_parent_node(current_node, - &parent_node); - if (err != DOM_NO_ERR || parent_node == NULL) { - if (current_node != n) - dom_node_unref(current_node); - break; - } - - if (current_node != n) - dom_node_unref(current_node); - - b = box_for_node(parent_node); - - /* Children of nodes that created an inline box - * will generate boxes which are attached as - * _siblings_ of the box generated for their - * parent node. Note, however, that we'll still - * use the parent node's styling as the parent - * style, above. */ - if (b != NULL && b->type != BOX_INLINE && - b->type != BOX_BR) { - props->containing_block = b; - - dom_node_unref(parent_node); - break; - } else { - current_node = parent_node; - parent_node = NULL; - } - } - } - - /* Compute current inline container, if any */ - if (props->containing_block != NULL && - props->containing_block->last != NULL && - props->containing_block->last->type == - BOX_INLINE_CONTAINER) - props->inline_container = props->containing_block->last; -} - -/** - * Construct the box tree for an XML element. - * - * \param ctx Tree construction context - * \param convert_children Whether to convert children - * \return true on success, false on memory exhaustion - */ - -bool box_construct_element(struct box_construct_ctx *ctx, - bool *convert_children) -{ - dom_string *title0, *s; - lwc_string *id = NULL; - struct box *box = NULL, *old_box; - css_select_results *styles = NULL; - struct element_entry *element; - lwc_string *bgimage_uri; - dom_exception err; - struct box_construct_props props; - const css_computed_style *root_style = NULL; - - assert(ctx->n != NULL); - - box_extract_properties(ctx->n, &props); - - if (props.containing_block != NULL) { - /* In case the containing block is a pre block, we clear - * the PRE_STRIP flag since it is not used if we follow - * the pre with a tag */ - props.containing_block->flags &= ~PRE_STRIP; - } - - if (props.node_is_root == false) { - root_style = ctx->root_box->style; - } - - styles = box_get_style(ctx->content, props.parent_style, root_style, - ctx->n); - if (styles == NULL) - return false; - - /* Extract title attribute, if present */ - err = dom_element_get_attribute(ctx->n, corestring_dom_title, &title0); - if (err != DOM_NO_ERR) - return false; - - if (title0 != NULL) { - char *t = squash_whitespace(dom_string_data(title0)); - - dom_string_unref(title0); - - if (t == NULL) - return false; - - props.title = talloc_strdup(ctx->bctx, t); - - free(t); - - if (props.title == NULL) - return false; - } - - /* Extract id attribute, if present */ - err = dom_element_get_attribute(ctx->n, corestring_dom_id, &s); - if (err != DOM_NO_ERR) - return false; - - if (s != NULL) { - err = dom_string_intern(s, &id); - if (err != DOM_NO_ERR) - id = NULL; - - dom_string_unref(s); - } - - box = box_create(styles, styles->styles[CSS_PSEUDO_ELEMENT_NONE], false, - props.href, props.target, props.title, id, - ctx->bctx); - if (box == NULL) - return false; - - /* If this is the root box, add it to the context */ - if (props.node_is_root) - ctx->root_box = box; - - /* Deal with colspan/rowspan */ - err = dom_element_get_attribute(ctx->n, corestring_dom_colspan, &s); - if (err != DOM_NO_ERR) - return false; - - if (s != NULL) { - const char *val = dom_string_data(s); - - if ('0' <= val[0] && val[0] <= '9') - box->columns = strtol(val, NULL, 10); - - dom_string_unref(s); - } - - err = dom_element_get_attribute(ctx->n, corestring_dom_rowspan, &s); - if (err != DOM_NO_ERR) - return false; - - if (s != NULL) { - const char *val = dom_string_data(s); - - if ('0' <= val[0] && val[0] <= '9') - box->rows = strtol(val, NULL, 10); - - dom_string_unref(s); - } - - /* Set box type from computed display */ - if ((css_computed_position(box->style) == CSS_POSITION_ABSOLUTE || - css_computed_position(box->style) == - CSS_POSITION_FIXED) && - (ns_computed_display_static(box->style) == - CSS_DISPLAY_INLINE || - ns_computed_display_static(box->style) == - CSS_DISPLAY_INLINE_BLOCK || - ns_computed_display_static(box->style) == - CSS_DISPLAY_INLINE_TABLE)) { - /* Special case for absolute positioning: make absolute inlines - * into inline block so that the boxes are constructed in an - * inline container as if they were not absolutely positioned. - * Layout expects and handles this. */ - box->type = box_map[CSS_DISPLAY_INLINE_BLOCK]; - } else if (props.node_is_root) { - /* Special case for root element: force it to BLOCK, or the - * rest of the layout will break. */ - box->type = BOX_BLOCK; - } else { - /* Normal mapping */ - box->type = box_map[ns_computed_display(box->style, - props.node_is_root)]; - } - - err = dom_node_get_node_name(ctx->n, &s); - if (err != DOM_NO_ERR || s == NULL) - return false; - - /* Special elements */ - element = bsearch(dom_string_data(s), element_table, - ELEMENT_TABLE_COUNT, sizeof(element_table[0]), - (int (*)(const void *, const void *)) strcasecmp); - - dom_string_unref(s); - - if (element != NULL) { - /* A special convert function exists for this element */ - if (element->convert(ctx->n, ctx->content, box, - convert_children) == false) - return false; - } - - /* Handle the :before pseudo element */ - if (!(box->flags & IS_REPLACED)) { - box_construct_generate(ctx->n, ctx->content, box, - box->styles->styles[CSS_PSEUDO_ELEMENT_BEFORE]); - } - - if (box->type == BOX_NONE || (ns_computed_display(box->style, - props.node_is_root) == CSS_DISPLAY_NONE && - props.node_is_root == false)) { - css_select_results_destroy(styles); - box->styles = NULL; - box->style = NULL; - - /* Invalidate associated gadget, if any */ - if (box->gadget != NULL) { - box->gadget->box = NULL; - box->gadget = NULL; - } - - /* Can't do this, because the lifetimes of boxes and gadgets - * are inextricably linked. Fortunately, talloc will save us - * (for now) */ - /* box_free_box(box); */ - - *convert_children = false; - - return true; - } - - /* Attach DOM node to box */ - err = dom_node_set_user_data(ctx->n, - corestring_dom___ns_key_box_node_data, box, NULL, - (void *) &old_box); - if (err != DOM_NO_ERR) - return false; - - /* Attach box to DOM node */ - box->node = dom_node_ref(ctx->n); - - if (props.inline_container == NULL && - (box->type == BOX_INLINE || - box->type == BOX_BR || - box->type == BOX_INLINE_BLOCK || - css_computed_float(box->style) == CSS_FLOAT_LEFT || - css_computed_float(box->style) == CSS_FLOAT_RIGHT) && - props.node_is_root == false) { - /* Found an inline child of a block without a current container - * (i.e. this box is the first child of its parent, or was - * preceded by block-level siblings) */ - assert(props.containing_block != NULL && - "Box must have containing block."); - - props.inline_container = box_create(NULL, NULL, false, NULL, - NULL, NULL, NULL, ctx->bctx); - if (props.inline_container == NULL) - return false; - - props.inline_container->type = BOX_INLINE_CONTAINER; - - box_add_child(props.containing_block, props.inline_container); - } - - /* Kick off fetch for any background image */ - if (css_computed_background_image(box->style, &bgimage_uri) == - CSS_BACKGROUND_IMAGE_IMAGE && bgimage_uri != NULL && - nsoption_bool(background_images) == true) { - nsurl *url; - nserror error; - - /* TODO: we get a url out of libcss as a lwc string, but - * earlier we already had it as a nsurl after we - * nsurl_joined it. Can this be improved? - * For now, just making another nsurl. */ - error = nsurl_create(lwc_string_data(bgimage_uri), &url); - if (error == NSERROR_OK) { - /* Fetch image if we got a valid URL */ - if (html_fetch_object(ctx->content, url, box, - image_types, - ctx->content->base.available_width, - 1000, true) == false) { - nsurl_unref(url); - return false; - } - nsurl_unref(url); - } - } - - if (*convert_children) - box->flags |= CONVERT_CHILDREN; - - if (box->type == BOX_INLINE || box->type == BOX_BR || - box->type == BOX_INLINE_BLOCK) { - /* Inline container must exist, as we'll have - * created it above if it didn't */ - assert(props.inline_container != NULL); - - box_add_child(props.inline_container, box); - } else { - if (ns_computed_display(box->style, props.node_is_root) == - CSS_DISPLAY_LIST_ITEM) { - /* List item: compute marker */ - if (box_construct_marker(box, props.title, ctx, - props.containing_block) == false) - return false; - } - - if (props.node_is_root == false && - (css_computed_float(box->style) == - CSS_FLOAT_LEFT || - css_computed_float(box->style) == - CSS_FLOAT_RIGHT)) { - /* Float: insert a float between the parent and box. */ - struct box *flt = box_create(NULL, NULL, false, - props.href, props.target, props.title, - NULL, ctx->bctx); - if (flt == NULL) - return false; - - if (css_computed_float(box->style) == CSS_FLOAT_LEFT) - flt->type = BOX_FLOAT_LEFT; - else - flt->type = BOX_FLOAT_RIGHT; - - box_add_child(props.inline_container, flt); - box_add_child(flt, box); - } else { - /* Non-floated block-level box: add to containing block - * if there is one. If we're the root box, then there - * won't be. */ - if (props.containing_block != NULL) - box_add_child(props.containing_block, box); - } - } - - return true; -} - -/** - * Complete construction of the box tree for an element. - * - * \param n DOM node to construct for - * \param content Containing document - * - * This will be called after all children of an element have been processed - */ -void box_construct_element_after(dom_node *n, html_content *content) -{ - struct box_construct_props props; - struct box *box = box_for_node(n); - - assert(box != NULL); - - box_extract_properties(n, &props); - - if (box->type == BOX_INLINE || box->type == BOX_BR) { - /* Insert INLINE_END into containing block */ - struct box *inline_end; - bool has_children; - dom_exception err; - - err = dom_node_has_child_nodes(n, &has_children); - if (err != DOM_NO_ERR) - return; - - if (has_children == false || - (box->flags & CONVERT_CHILDREN) == 0) { - /* No children, or didn't want children converted */ - return; - } - - if (props.inline_container == NULL) { - /* Create inline container if we don't have one */ - props.inline_container = box_create(NULL, NULL, false, - NULL, NULL, NULL, NULL, content->bctx); - if (props.inline_container == NULL) - return; - - props.inline_container->type = BOX_INLINE_CONTAINER; - - box_add_child(props.containing_block, - props.inline_container); - } - - inline_end = box_create(NULL, box->style, false, - box->href, box->target, box->title, - box->id == NULL ? NULL : - lwc_string_ref(box->id), content->bctx); - if (inline_end != NULL) { - inline_end->type = BOX_INLINE_END; - - assert(props.inline_container != NULL); - - box_add_child(props.inline_container, inline_end); - - box->inline_end = inline_end; - inline_end->inline_end = box; - } - } else if (!(box->flags & IS_REPLACED)) { - /* Handle the :after pseudo element */ - box_construct_generate(n, content, box, - box->styles->styles[CSS_PSEUDO_ELEMENT_AFTER]); - } -} - -/** - * Construct the box tree for an XML text node. - * - * \param ctx Tree construction context - * \return true on success, false on memory exhaustion - */ - -bool box_construct_text(struct box_construct_ctx *ctx) -{ - struct box_construct_props props; - struct box *box = NULL; - dom_string *content; - dom_exception err; - - assert(ctx->n != NULL); - - box_extract_properties(ctx->n, &props); - - assert(props.containing_block != NULL); - - err = dom_characterdata_get_data(ctx->n, &content); - if (err != DOM_NO_ERR || content == NULL) - return false; - - if (css_computed_white_space(props.parent_style) == - CSS_WHITE_SPACE_NORMAL || - css_computed_white_space(props.parent_style) == - CSS_WHITE_SPACE_NOWRAP) { - char *text; - - text = squash_whitespace(dom_string_data(content)); - - dom_string_unref(content); - - if (text == NULL) - return false; - - /* if the text is just a space, combine it with the preceding - * text node, if any */ - if (text[0] == ' ' && text[1] == 0) { - if (props.inline_container != NULL) { - assert(props.inline_container->last != NULL); - - props.inline_container->last->space = - UNKNOWN_WIDTH; - } - - free(text); - - return true; - } - - if (props.inline_container == NULL) { - /* Child of a block without a current container - * (i.e. this box is the first child of its parent, or - * was preceded by block-level siblings) */ - props.inline_container = box_create(NULL, NULL, false, - NULL, NULL, NULL, NULL, ctx->bctx); - if (props.inline_container == NULL) { - free(text); - return false; - } - - props.inline_container->type = BOX_INLINE_CONTAINER; - - box_add_child(props.containing_block, - props.inline_container); - } - - /** \todo Dropping const here is not clever */ - box = box_create(NULL, - (css_computed_style *) props.parent_style, - false, props.href, props.target, props.title, - NULL, ctx->bctx); - if (box == NULL) { - free(text); - return false; - } - - box->type = BOX_TEXT; - - box->text = talloc_strdup(ctx->bctx, text); - free(text); - if (box->text == NULL) - return false; - - box->length = strlen(box->text); - - /* strip ending space char off */ - if (box->length > 1 && box->text[box->length - 1] == ' ') { - box->space = UNKNOWN_WIDTH; - box->length--; - } - - if (css_computed_text_transform(props.parent_style) != - CSS_TEXT_TRANSFORM_NONE) - box_text_transform(box->text, box->length, - css_computed_text_transform( - props.parent_style)); - - box_add_child(props.inline_container, box); - - if (box->text[0] == ' ') { - box->length--; - - memmove(box->text, &box->text[1], box->length); - - if (box->prev != NULL) - box->prev->space = UNKNOWN_WIDTH; - } - } else { - /* white-space: pre */ - char *text; - size_t text_len = dom_string_byte_length(content); - size_t i; - char *current; - enum css_white_space_e white_space = - css_computed_white_space(props.parent_style); - - /* note: pre-wrap/pre-line are unimplemented */ - assert(white_space == CSS_WHITE_SPACE_PRE || - white_space == CSS_WHITE_SPACE_PRE_LINE || - white_space == CSS_WHITE_SPACE_PRE_WRAP); - - text = malloc(text_len + 1); - dom_string_unref(content); - - if (text == NULL) - return false; - - memcpy(text, dom_string_data(content), text_len); - text[text_len] = '\0'; - - /* TODO: Handle tabs properly */ - for (i = 0; i < text_len; i++) - if (text[i] == '\t') - text[i] = ' '; - - if (css_computed_text_transform(props.parent_style) != - CSS_TEXT_TRANSFORM_NONE) - box_text_transform(text, strlen(text), - css_computed_text_transform( - props.parent_style)); - - current = text; - - /* swallow a single leading new line */ - if (props.containing_block->flags & PRE_STRIP) { - switch (*current) { - case '\n': - current++; - break; - case '\r': - current++; - if (*current == '\n') - current++; - break; - } - props.containing_block->flags &= ~PRE_STRIP; - } - - do { - size_t len = strcspn(current, "\r\n"); - - char old = current[len]; - - current[len] = 0; - - if (props.inline_container == NULL) { - /* Child of a block without a current container - * (i.e. this box is the first child of its - * parent, or was preceded by block-level - * siblings) */ - props.inline_container = box_create(NULL, NULL, - false, NULL, NULL, NULL, NULL, - ctx->bctx); - if (props.inline_container == NULL) { - free(text); - return false; - } - - props.inline_container->type = - BOX_INLINE_CONTAINER; - - box_add_child(props.containing_block, - props.inline_container); - } - - /** \todo Dropping const isn't clever */ - box = box_create(NULL, - (css_computed_style *) props.parent_style, - false, props.href, props.target, props.title, - NULL, ctx->bctx); - if (box == NULL) { - free(text); - return false; - } - - box->type = BOX_TEXT; - - box->text = talloc_strdup(ctx->bctx, current); - if (box->text == NULL) { - free(text); - return false; - } - - box->length = strlen(box->text); - - box_add_child(props.inline_container, box); - - current[len] = old; - - current += len; - - if (current[0] != '\0') { - /* Linebreak: create new inline container */ - props.inline_container = box_create(NULL, NULL, - false, NULL, NULL, NULL, NULL, - ctx->bctx); - if (props.inline_container == NULL) { - free(text); - return false; - } - - props.inline_container->type = - BOX_INLINE_CONTAINER; - - box_add_child(props.containing_block, - props.inline_container); - - if (current[0] == '\r' && current[1] == '\n') - current += 2; - else - current++; - } - } while (*current); - - free(text); - } - - return true; -} - -/** - * Get the style for an element. - * - * \param c content of type CONTENT_HTML that is being processed - * \param parent_style style at this point in xml tree, or NULL for root - * \param root_style root node's style, or NULL for root - * \param n node in xml tree - * \return the new style, or NULL on memory exhaustion - */ -css_select_results *box_get_style(html_content *c, - const css_computed_style *parent_style, - const css_computed_style *root_style, dom_node *n) -{ - dom_string *s; - dom_exception err; - css_stylesheet *inline_style = NULL; - css_select_results *styles; - nscss_select_ctx ctx; - - /* Firstly, construct inline stylesheet, if any */ - err = dom_element_get_attribute(n, corestring_dom_style, &s); - if (err != DOM_NO_ERR) - return NULL; - - if (s != NULL) { - inline_style = nscss_create_inline_style( - (const uint8_t *) dom_string_data(s), - dom_string_byte_length(s), - c->encoding, - nsurl_access(c->base_url), - c->quirks != DOM_DOCUMENT_QUIRKS_MODE_NONE); - - dom_string_unref(s); - - if (inline_style == NULL) - return NULL; - } - - /* Populate selection context */ - ctx.ctx = c->select_ctx; - ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); - ctx.base_url = c->base_url; - ctx.universal = c->universal; - ctx.root_style = root_style; - ctx.parent_style = parent_style; - - /* Select style for element */ - styles = nscss_get_style(&ctx, n, CSS_MEDIA_SCREEN, inline_style); - - /* No longer need inline style */ - if (inline_style != NULL) - css_stylesheet_destroy(inline_style); - - return styles; -} - - -/** - * Apply the CSS text-transform property to given text for its ASCII chars. - * - * \param s string to transform - * \param len length of s - * \param tt transform type - */ - -void box_text_transform(char *s, unsigned int len, enum css_text_transform_e tt) -{ - unsigned int i; - if (len == 0) - return; - switch (tt) { - case CSS_TEXT_TRANSFORM_UPPERCASE: - for (i = 0; i < len; ++i) - if ((unsigned char) s[i] < 0x80) - s[i] = toupper(s[i]); - break; - case CSS_TEXT_TRANSFORM_LOWERCASE: - for (i = 0; i < len; ++i) - if ((unsigned char) s[i] < 0x80) - s[i] = tolower(s[i]); - break; - case CSS_TEXT_TRANSFORM_CAPITALIZE: - if ((unsigned char) s[0] < 0x80) - s[0] = toupper(s[0]); - for (i = 1; i < len; ++i) - if ((unsigned char) s[i] < 0x80 && - isspace(s[i - 1])) - s[i] = toupper(s[i]); - break; - default: - break; - } -} - - -/** - * \name Special case element handlers - * - * These functions are called by box_construct_element() when an element is - * being converted, according to the entries in element_table. - * - * The parameters are the xmlNode, the content for the document, and a partly - * filled in box structure for the element. - * - * Return true on success, false on memory exhaustion. Set *convert_children - * to false if children of this element in the XML tree should be skipped (for - * example, if they have been processed in some special way already). - * - * Elements ordered as in the HTML 4.01 specification. Section numbers in - * brackets [] refer to the spec. - * - * \{ - */ - -/** - * Document body [7.5.1]. - */ - -bool box_body(BOX_SPECIAL_PARAMS) -{ - css_color color; - - css_computed_background_color(box->style, &color); - if (nscss_color_is_transparent(color)) - content->background_colour = NS_TRANSPARENT; - else - content->background_colour = nscss_color_to_ns(color); - - return true; -} - - -/** - * Forced line break [9.3.2]. - */ - -bool box_br(BOX_SPECIAL_PARAMS) -{ - box->type = BOX_BR; - return true; -} - -/** - * Preformatted text [9.3.4]. - */ - -bool box_pre(BOX_SPECIAL_PARAMS) -{ - box->flags |= PRE_STRIP; - return true; -} - -/** - * Anchor [12.2]. - */ - -bool box_a(BOX_SPECIAL_PARAMS) -{ - bool ok; - nsurl *url; - dom_string *s; - dom_exception err; - - err = dom_element_get_attribute(n, corestring_dom_href, &s); - if (err == DOM_NO_ERR && s != NULL) { - ok = box_extract_link(content, s, content->base_url, &url); - dom_string_unref(s); - if (!ok) - return false; - if (url) { - if (box->href != NULL) - nsurl_unref(box->href); - box->href = url; - } - } - - /* name and id share the same namespace */ - err = dom_element_get_attribute(n, corestring_dom_name, &s); - if (err == DOM_NO_ERR && s != NULL) { - lwc_string *lwc_name; - - err = dom_string_intern(s, &lwc_name); - - dom_string_unref(s); - - if (err == DOM_NO_ERR) { - /* name replaces existing id - * TODO: really? */ - if (box->id != NULL) - lwc_string_unref(box->id); - - box->id = lwc_name; - } - } - - /* target frame [16.3] */ - err = dom_element_get_attribute(n, corestring_dom_target, &s); - if (err == DOM_NO_ERR && s != NULL) { - if (dom_string_caseless_lwc_isequal(s, - corestring_lwc__blank)) - box->target = TARGET_BLANK; - else if (dom_string_caseless_lwc_isequal(s, - corestring_lwc__top)) - box->target = TARGET_TOP; - else if (dom_string_caseless_lwc_isequal(s, - corestring_lwc__parent)) - box->target = TARGET_PARENT; - else if (dom_string_caseless_lwc_isequal(s, - corestring_lwc__self)) - /* the default may have been overridden by a - * <base target=...>, so this is different to 0 */ - box->target = TARGET_SELF; - else { - /* 6.16 says that frame names must begin with [a-zA-Z] - * This doesn't match reality, so just take anything */ - box->target = talloc_strdup(content->bctx, - dom_string_data(s)); - if (!box->target) { - dom_string_unref(s); - return false; - } - } - dom_string_unref(s); - } - - return true; -} - - -/** - * Embedded image [13.2]. - */ - -bool box_image(BOX_SPECIAL_PARAMS) -{ - bool ok; - dom_string *s; - dom_exception err; - nsurl *url; - enum css_width_e wtype; - enum css_height_e htype; - css_fixed value = 0; - css_unit wunit = CSS_UNIT_PX; - css_unit hunit = CSS_UNIT_PX; - - if (box->style && ns_computed_display(box->style, - box_is_root(n)) == CSS_DISPLAY_NONE) - return true; - - /* handle alt text */ - err = dom_element_get_attribute(n, corestring_dom_alt, &s); - if (err == DOM_NO_ERR && s != NULL) { - char *alt = squash_whitespace(dom_string_data(s)); - dom_string_unref(s); - if (alt == NULL) - return false; - box->text = talloc_strdup(content->bctx, alt); - free(alt); - if (box->text == NULL) - return false; - box->length = strlen(box->text); - } - - if (nsoption_bool(foreground_images) == false) { - return true; - } - - /* imagemap associated with this image */ - if (!box_get_attribute(n, "usemap", content->bctx, &box->usemap)) - return false; - if (box->usemap && box->usemap[0] == '#') - box->usemap++; - - /* get image URL */ - err = dom_element_get_attribute(n, corestring_dom_src, &s); - if (err != DOM_NO_ERR || s == NULL) - return true; - - if (box_extract_link(content, s, content->base_url, &url) == false) { - dom_string_unref(s); - return false; - } - - dom_string_unref(s); - - if (url == NULL) - return true; - - /* start fetch */ - box->flags |= IS_REPLACED; - ok = html_fetch_object(content, url, box, image_types, - content->base.available_width, 1000, false); - nsurl_unref(url); - - wtype = css_computed_width(box->style, &value, &wunit); - htype = css_computed_height(box->style, &value, &hunit); - - if (wtype == CSS_WIDTH_SET && wunit != CSS_UNIT_PCT && - htype == CSS_HEIGHT_SET && hunit != CSS_UNIT_PCT) { - /* We know the dimensions the image will be shown at before it's - * fetched. */ - box->flags |= REPLACE_DIM; - } - - return ok; -} - - -/** - * Noscript element - */ - -bool box_noscript(BOX_SPECIAL_PARAMS) -{ - /* If scripting is enabled, do not display the contents of noscript */ - if (content->enable_scripting) - *convert_children = false; - - return true; -} - - -/** - * Destructor for object_params, for <object> elements - * - * \param o The object params being destroyed. - * \return 0 to allow talloc to continue destroying the tree. - */ -static int box_object_talloc_destructor(struct object_params *o) -{ - if (o->codebase != NULL) - nsurl_unref(o->codebase); - if (o->classid != NULL) - nsurl_unref(o->classid); - if (o->data != NULL) - nsurl_unref(o->data); - - return 0; -} - -/** - * Generic embedded object [13.3]. - */ - -bool box_object(BOX_SPECIAL_PARAMS) -{ - struct object_params *params; - struct object_param *param; - dom_string *codebase, *classid, *data; - dom_node *c; - dom_exception err; - - if (box->style && ns_computed_display(box->style, - box_is_root(n)) == CSS_DISPLAY_NONE) - return true; - - if (box_get_attribute(n, "usemap", content->bctx, &box->usemap) == - false) - return false; - if (box->usemap && box->usemap[0] == '#') - box->usemap++; - - params = talloc(content->bctx, struct object_params); - if (params == NULL) - return false; - - talloc_set_destructor(params, box_object_talloc_destructor); - - params->data = NULL; - params->type = NULL; - params->codetype = NULL; - params->codebase = NULL; - params->classid = NULL; - params->params = NULL; - - /* codebase, classid, and data are URLs - * (codebase is the base for the other two) */ - err = dom_element_get_attribute(n, corestring_dom_codebase, &codebase); - if (err == DOM_NO_ERR && codebase != NULL) { - if (box_extract_link(content, codebase, content->base_url, - ¶ms->codebase) == false) { - dom_string_unref(codebase); - return false; - } - dom_string_unref(codebase); - } - if (params->codebase == NULL) - params->codebase = nsurl_ref(content->base_url); - - err = dom_element_get_attribute(n, corestring_dom_classid, &classid); - if (err == DOM_NO_ERR && classid != NULL) { - if (box_extract_link(content, classid, - params->codebase, ¶ms->classid) == false) { - dom_string_unref(classid); - return false; - } - dom_string_unref(classid); - } - - err = dom_element_get_attribute(n, corestring_dom_data, &data); - if (err == DOM_NO_ERR && data != NULL) { - if (box_extract_link(content, data, - params->codebase, ¶ms->data) == false) { - dom_string_unref(data); - return false; - } - dom_string_unref(data); - } - - if (params->classid == NULL && params->data == NULL) - /* nothing to embed; ignore */ - return true; - - /* Don't include ourself */ - if (params->classid != NULL && nsurl_compare(content->base_url, - params->classid, NSURL_COMPLETE)) - return true; - - if (params->data != NULL && nsurl_compare(content->base_url, - params->data, NSURL_COMPLETE)) - return true; - - /* codetype and type are MIME types */ - if (box_get_attribute(n, "codetype", params, - ¶ms->codetype) == false) - return false; - if (box_get_attribute(n, "type", params, ¶ms->type) == false) - return false; - - /* classid && !data => classid is used (consult codetype) - * (classid || !classid) && data => data is used (consult type) - * !classid && !data => invalid; ignored */ - - if (params->classid != NULL && params->data == NULL && - params->codetype != NULL) { - lwc_string *icodetype; - lwc_error lerror; - - lerror = lwc_intern_string(params->codetype, - strlen(params->codetype), &icodetype); - if (lerror != lwc_error_ok) - return false; - - if (content_factory_type_from_mime_type(icodetype) == - CONTENT_NONE) { - /* can't handle this MIME type */ - lwc_string_unref(icodetype); - return true; - } - - lwc_string_unref(icodetype); - } - - if (params->data != NULL && params->type != NULL) { - lwc_string *itype; - lwc_error lerror; - - lerror = lwc_intern_string(params->type, strlen(params->type), - &itype); - if (lerror != lwc_error_ok) - return false; - - if (content_factory_type_from_mime_type(itype) == - CONTENT_NONE) { - /* can't handle this MIME type */ - lwc_string_unref(itype); - return true; - } - - lwc_string_unref(itype); - } - - /* add parameters to linked list */ - err = dom_node_get_first_child(n, &c); - if (err != DOM_NO_ERR) - return false; - - while (c != NULL) { - dom_node *next; - dom_node_type type; - - err = dom_node_get_node_type(c, &type); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - if (type == DOM_ELEMENT_NODE) { - dom_string *name; - - err = dom_node_get_node_name(c, &name); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - if (!dom_string_caseless_lwc_isequal(name, - corestring_lwc_param)) { - /* The first non-param child is the start of - * the alt html. Therefore, we should break - * out of this loop. */ - dom_node_unref(c); - break; - } - - param = talloc(params, struct object_param); - if (param == NULL) { - dom_node_unref(c); - return false; - } - param->name = NULL; - param->value = NULL; - param->type = NULL; - param->valuetype = NULL; - param->next = NULL; - - if (box_get_attribute(c, "name", param, - ¶m->name) == false) { - dom_node_unref(c); - return false; - } - - if (box_get_attribute(c, "value", param, - ¶m->value) == false) { - dom_node_unref(c); - return false; - } - - if (box_get_attribute(c, "type", param, - ¶m->type) == false) { - dom_node_unref(c); - return false; - } - - if (box_get_attribute(c, "valuetype", param, - ¶m->valuetype) == false) { - dom_node_unref(c); - return false; - } - - if (param->valuetype == NULL) { - param->valuetype = talloc_strdup(param, "data"); - if (param->valuetype == NULL) { - dom_node_unref(c); - return false; - } - } - - param->next = params->params; - params->params = param; - } - - err = dom_node_get_next_sibling(c, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - dom_node_unref(c); - c = next; - } - - box->object_params = params; - - /* start fetch (MIME type is ok or not specified) */ - box->flags |= IS_REPLACED; - if (!html_fetch_object(content, - params->data ? params->data : params->classid, - box, CONTENT_ANY, content->base.available_width, 1000, - false)) - return false; - - *convert_children = false; - return true; -} - - -/** - * Window subdivision [16.2.1]. - */ - -bool box_frameset(BOX_SPECIAL_PARAMS) -{ - bool ok; - - if (content->frameset) { - NSLOG(netsurf, INFO, "Error: multiple framesets in document."); - /* Don't convert children */ - if (convert_children) - *convert_children = false; - /* And ignore this spurious frameset */ - box->type = BOX_NONE; - return true; - } - - content->frameset = talloc_zero(content->bctx, struct content_html_frames); - if (!content->frameset) - return false; - - ok = box_create_frameset(content->frameset, n, content); - if (ok) - box->type = BOX_NONE; - - if (convert_children) - *convert_children = false; - return ok; -} - - -/** - * Destructor for content_html_frames, for frame elements - * - * \param f The frame params being destroyed. - * \return 0 to allow talloc to continue destroying the tree. - */ -static int box_frames_talloc_destructor(struct content_html_frames *f) -{ - if (f->url != NULL) { - nsurl_unref(f->url); - f->url = NULL; - } - - return 0; -} - - -/** - * Parse a multi-length-list, as defined by HTML 4.01. - * - * \param ds dom string to parse - * \param count updated to number of entries - * \return array of struct box_multi_length, or 0 on memory exhaustion - */ -static struct frame_dimension * -box_parse_multi_lengths(const dom_string *ds, unsigned int *count) -{ - char *end; - unsigned int i, n; - struct frame_dimension *length; - const char *s; - - s = dom_string_data(ds); - - for (i = 0, n = 1; s[i]; i++) - if (s[i] == ',') - n++; - - length = calloc(n, sizeof(struct frame_dimension)); - if (!length) - return NULL; - - for (i = 0; i != n; i++) { - while (ascii_is_space(*s)) { - s++; - } - length[i].value = strtof(s, &end); - if (length[i].value <= 0) { - length[i].value = 1; - } - s = end; - switch (*s) { - case '%': - length[i].unit = FRAME_DIMENSION_PERCENT; - break; - case '*': - length[i].unit = FRAME_DIMENSION_RELATIVE; - break; - default: - length[i].unit = FRAME_DIMENSION_PIXELS; - break; - } - while (*s && *s != ',') { - s++; - } - if (*s == ',') { - s++; - } - } - - *count = n; - return length; -} - - -bool box_create_frameset(struct content_html_frames *f, dom_node *n, - html_content *content) { - unsigned int row, col, index, i; - unsigned int rows = 1, cols = 1; - dom_string *s; - dom_exception err; - nsurl *url; - struct frame_dimension *row_height = 0, *col_width = 0; - dom_node *c, *next; - struct content_html_frames *frame; - bool default_border = true; - colour default_border_colour = 0x000000; - - /* parse rows and columns */ - err = dom_element_get_attribute(n, corestring_dom_rows, &s); - if (err == DOM_NO_ERR && s != NULL) { - row_height = box_parse_multi_lengths(s, &rows); - dom_string_unref(s); - if (row_height == NULL) - return false; - } else { - row_height = calloc(1, sizeof(struct frame_dimension)); - if (row_height == NULL) - return false; - row_height->value = 100; - row_height->unit = FRAME_DIMENSION_PERCENT; - } - - err = dom_element_get_attribute(n, corestring_dom_cols, &s); - if (err == DOM_NO_ERR && s != NULL) { - col_width = box_parse_multi_lengths(s, &cols); - dom_string_unref(s); - if (col_width == NULL) { - free(row_height); - return false; - } - } else { - col_width = calloc(1, sizeof(struct frame_dimension)); - if (col_width == NULL) { - free(row_height); - return false; - } - col_width->value = 100; - col_width->unit = FRAME_DIMENSION_PERCENT; - } - - /* common extension: border="0|1" to control all children */ - err = dom_element_get_attribute(n, corestring_dom_border, &s); - if (err == DOM_NO_ERR && s != NULL) { - if ((dom_string_data(s)[0] == '0') && - (dom_string_data(s)[1] == '\0')) - default_border = false; - dom_string_unref(s); - } - - /* common extension: frameborder="yes|no" to control all children */ - err = dom_element_get_attribute(n, corestring_dom_frameborder, &s); - if (err == DOM_NO_ERR && s != NULL) { - if (dom_string_caseless_lwc_isequal(s, - corestring_lwc_no) == 0) - default_border = false; - dom_string_unref(s); - } - - /* common extension: bordercolor="#RRGGBB|<named colour>" to control - *all children */ - err = dom_element_get_attribute(n, corestring_dom_bordercolor, &s); - if (err == DOM_NO_ERR && s != NULL) { - css_color color; - - if (nscss_parse_colour(dom_string_data(s), &color)) - default_border_colour = nscss_color_to_ns(color); - - dom_string_unref(s); - } - - /* update frameset and create default children */ - f->cols = cols; - f->rows = rows; - f->scrolling = BW_SCROLLING_NO; - f->children = talloc_array(content->bctx, struct content_html_frames, - (rows * cols)); - - talloc_set_destructor(f->children, box_frames_talloc_destructor); - - for (row = 0; row < rows; row++) { - for (col = 0; col < cols; col++) { - index = (row * cols) + col; - frame = &f->children[index]; - frame->cols = 0; - frame->rows = 0; - frame->width = col_width[col]; - frame->height = row_height[row]; - frame->margin_width = 0; - frame->margin_height = 0; - frame->name = NULL; - frame->url = NULL; - frame->no_resize = false; - frame->scrolling = BW_SCROLLING_AUTO; - frame->border = default_border; - frame->border_colour = default_border_colour; - frame->children = NULL; - } - } - free(col_width); - free(row_height); - - /* create the frameset windows */ - err = dom_node_get_first_child(n, &c); - if (err != DOM_NO_ERR) - return false; - - for (row = 0; c != NULL && row < rows; row++) { - for (col = 0; c != NULL && col < cols; col++) { - while (c != NULL) { - dom_node_type type; - dom_string *name; - - err = dom_node_get_node_type(c, &type); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - err = dom_node_get_node_name(c, &name); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - if (type != DOM_ELEMENT_NODE || - (!dom_string_caseless_lwc_isequal( - name, - corestring_lwc_frame) && - !dom_string_caseless_lwc_isequal( - name, - corestring_lwc_frameset - ))) { - err = dom_node_get_next_sibling(c, - &next); - if (err != DOM_NO_ERR) { - dom_string_unref(name); - dom_node_unref(c); - return false; - } - - dom_string_unref(name); - dom_node_unref(c); - c = next; - } else { - /* Got a FRAME or FRAMESET element */ - dom_string_unref(name); - break; - } - } - - if (c == NULL) - break; - - /* get current frame */ - index = (row * cols) + col; - frame = &f->children[index]; - - /* nest framesets */ - err = dom_node_get_node_name(c, &s); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - if (dom_string_caseless_lwc_isequal(s, - corestring_lwc_frameset)) { - dom_string_unref(s); - frame->border = 0; - if (box_create_frameset(frame, c, - content) == false) { - dom_node_unref(c); - return false; - } - - err = dom_node_get_next_sibling(c, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - dom_node_unref(c); - c = next; - continue; - } - - dom_string_unref(s); - - /* get frame URL (not required) */ - url = NULL; - err = dom_element_get_attribute(c, corestring_dom_src, &s); - if (err == DOM_NO_ERR && s != NULL) { - box_extract_link(content, s, content->base_url, - &url); - dom_string_unref(s); - } - - /* copy url */ - if (url != NULL) { - /* no self-references */ - if (nsurl_compare(content->base_url, url, - NSURL_COMPLETE) == false) - frame->url = url; - url = NULL; - } - - /* fill in specified values */ - err = dom_element_get_attribute(c, corestring_dom_name, &s); - if (err == DOM_NO_ERR && s != NULL) { - frame->name = talloc_strdup(content->bctx, - dom_string_data(s)); - dom_string_unref(s); - } - - dom_element_has_attribute(c, corestring_dom_noresize, - &frame->no_resize); - - err = dom_element_get_attribute(c, corestring_dom_frameborder, - &s); - if (err == DOM_NO_ERR && s != NULL) { - i = atoi(dom_string_data(s)); - frame->border = (i != 0); - dom_string_unref(s); - } - - err = dom_element_get_attribute(c, corestring_dom_scrolling, &s); - if (err == DOM_NO_ERR && s != NULL) { - if (dom_string_caseless_lwc_isequal(s, - corestring_lwc_yes)) - frame->scrolling = BW_SCROLLING_YES; - else if (dom_string_caseless_lwc_isequal(s, - corestring_lwc_no)) - frame->scrolling = BW_SCROLLING_NO; - dom_string_unref(s); - } - - err = dom_element_get_attribute(c, corestring_dom_marginwidth, - &s); - if (err == DOM_NO_ERR && s != NULL) { - frame->margin_width = atoi(dom_string_data(s)); - dom_string_unref(s); - } - - err = dom_element_get_attribute(c, corestring_dom_marginheight, - &s); - if (err == DOM_NO_ERR && s != NULL) { - frame->margin_height = atoi(dom_string_data(s)); - dom_string_unref(s); - } - - err = dom_element_get_attribute(c, corestring_dom_bordercolor, - &s); - if (err == DOM_NO_ERR && s != NULL) { - css_color color; - - if (nscss_parse_colour(dom_string_data(s), - &color)) - frame->border_colour = - nscss_color_to_ns(color); - - dom_string_unref(s); - } - - /* advance */ - err = dom_node_get_next_sibling(c, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - dom_node_unref(c); - c = next; - } - } - - /* If the last child wasn't a frame, we still need to unref it */ - if (c != NULL) { - dom_node_unref(c); - } - - return true; -} - - -/** - * Destructor for content_html_iframe, for <iframe> elements - * - * \param f The iframe params being destroyed. - * \return 0 to allow talloc to continue destroying the tree. - */ -static int box_iframes_talloc_destructor(struct content_html_iframe *f) -{ - if (f->url != NULL) { - nsurl_unref(f->url); - f->url = NULL; - } - - return 0; -} - - -/** - * Inline subwindow [16.5]. - */ - -bool box_iframe(BOX_SPECIAL_PARAMS) -{ - nsurl *url; - dom_string *s; - dom_exception err; - struct content_html_iframe *iframe; - int i; - - if (box->style && ns_computed_display(box->style, - box_is_root(n)) == CSS_DISPLAY_NONE) - return true; - - if (box->style && css_computed_visibility(box->style) == - CSS_VISIBILITY_HIDDEN) - /* Don't create iframe discriptors for invisible iframes - * TODO: handle hidden iframes at browser_window generation - * time instead? */ - return true; - - /* get frame URL */ - err = dom_element_get_attribute(n, corestring_dom_src, &s); - if (err != DOM_NO_ERR || s == NULL) - return true; - if (box_extract_link(content, s, content->base_url, &url) == false) { - dom_string_unref(s); - return false; - } - dom_string_unref(s); - if (url == NULL) - return true; - - /* don't include ourself */ - if (nsurl_compare(content->base_url, url, NSURL_COMPLETE)) { - nsurl_unref(url); - return true; - } - - /* create a new iframe */ - iframe = talloc(content->bctx, struct content_html_iframe); - if (iframe == NULL) { - nsurl_unref(url); - return false; - } - - talloc_set_destructor(iframe, box_iframes_talloc_destructor); - - iframe->box = box; - iframe->margin_width = 0; - iframe->margin_height = 0; - iframe->name = NULL; - iframe->url = url; - iframe->scrolling = BW_SCROLLING_AUTO; - iframe->border = true; - - /* Add this iframe to the linked list of iframes */ - iframe->next = content->iframe; - content->iframe = iframe; - - /* fill in specified values */ - err = dom_element_get_attribute(n, corestring_dom_name, &s); - if (err == DOM_NO_ERR && s != NULL) { - iframe->name = talloc_strdup(content->bctx, dom_string_data(s)); - dom_string_unref(s); - } - - err = dom_element_get_attribute(n, corestring_dom_frameborder, &s); - if (err == DOM_NO_ERR && s != NULL) { - i = atoi(dom_string_data(s)); - iframe->border = (i != 0); - dom_string_unref(s); - } - - err = dom_element_get_attribute(n, corestring_dom_bordercolor, &s); - if (err == DOM_NO_ERR && s != NULL) { - css_color color; - - if (nscss_parse_colour(dom_string_data(s), &color)) - iframe->border_colour = nscss_color_to_ns(color); - - dom_string_unref(s); - } - - err = dom_element_get_attribute(n, corestring_dom_scrolling, &s); - if (err == DOM_NO_ERR && s != NULL) { - if (dom_string_caseless_lwc_isequal(s, - corestring_lwc_yes)) - iframe->scrolling = BW_SCROLLING_YES; - else if (dom_string_caseless_lwc_isequal(s, - corestring_lwc_no)) - iframe->scrolling = BW_SCROLLING_NO; - dom_string_unref(s); - } - - err = dom_element_get_attribute(n, corestring_dom_marginwidth, &s); - if (err == DOM_NO_ERR && s != NULL) { - iframe->margin_width = atoi(dom_string_data(s)); - dom_string_unref(s); - } - - err = dom_element_get_attribute(n, corestring_dom_marginheight, &s); - if (err == DOM_NO_ERR && s != NULL) { - iframe->margin_height = atoi(dom_string_data(s)); - dom_string_unref(s); - } - - /* box */ - assert(box->style); - box->flags |= IFRAME; - box->flags |= IS_REPLACED; - - /* Showing iframe, so don't show alternate content */ - if (convert_children) - *convert_children = false; - return true; -} - - -/** - * Helper function for adding textarea widget to box. - * - * This is a load of hacks to ensure boxes replaced with textareas - * can be handled by the layout code. - */ - -static bool box_input_text(html_content *html, struct box *box, - struct dom_node *node) -{ - struct box *inline_container, *inline_box; - - box->type = BOX_INLINE_BLOCK; - - inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, html->bctx); - if (!inline_container) - return false; - inline_container->type = BOX_INLINE_CONTAINER; - inline_box = box_create(NULL, box->style, false, 0, 0, box->title, 0, - html->bctx); - if (!inline_box) - return false; - inline_box->type = BOX_TEXT; - inline_box->text = talloc_strdup(html->bctx, ""); - - box_add_child(inline_container, inline_box); - box_add_child(box, inline_container); - - return box_textarea_create_textarea(html, box, node); -} - - -/** - * Form control [17.4]. - */ - -bool box_input(BOX_SPECIAL_PARAMS) -{ - struct form_control *gadget = NULL; - dom_string *type = NULL; - dom_exception err; - nsurl *url; - nserror error; - - dom_element_get_attribute(n, corestring_dom_type, &type); - - gadget = html_forms_get_control_for_node(content->forms, n); - if (gadget == NULL) - goto no_memory; - box->gadget = gadget; - box->flags |= IS_REPLACED; - gadget->box = box; - gadget->html = content; - - if (type && dom_string_caseless_lwc_isequal(type, - corestring_lwc_password)) { - if (box_input_text(content, box, n) == false) - goto no_memory; - - } else if (type && dom_string_caseless_lwc_isequal(type, - corestring_lwc_file)) { - box->type = BOX_INLINE_BLOCK; - - } else if (type && dom_string_caseless_lwc_isequal(type, - corestring_lwc_hidden)) { - /* no box for hidden inputs */ - box->type = BOX_NONE; - - } else if (type && - (dom_string_caseless_lwc_isequal(type, - corestring_lwc_checkbox) || - dom_string_caseless_lwc_isequal(type, - corestring_lwc_radio))) { - - } else if (type && - (dom_string_caseless_lwc_isequal(type, - corestring_lwc_submit) || - dom_string_caseless_lwc_isequal(type, - corestring_lwc_reset) || - dom_string_caseless_lwc_isequal(type, - corestring_lwc_button))) { - struct box *inline_container, *inline_box; - - if (box_button(n, content, box, 0) == false) - goto no_memory; - - inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, - content->bctx); - if (inline_container == NULL) - goto no_memory; - - inline_container->type = BOX_INLINE_CONTAINER; - - inline_box = box_create(NULL, box->style, false, 0, 0, - box->title, 0, content->bctx); - if (inline_box == NULL) - goto no_memory; - - inline_box->type = BOX_TEXT; - - if (box->gadget->value != NULL) - inline_box->text = talloc_strdup(content->bctx, - box->gadget->value); - else if (box->gadget->type == GADGET_SUBMIT) - inline_box->text = talloc_strdup(content->bctx, - messages_get("Form_Submit")); - else if (box->gadget->type == GADGET_RESET) - inline_box->text = talloc_strdup(content->bctx, - messages_get("Form_Reset")); - else - inline_box->text = talloc_strdup(content->bctx, - "Button"); - - if (inline_box->text == NULL) - goto no_memory; - - inline_box->length = strlen(inline_box->text); - - box_add_child(inline_container, inline_box); - - box_add_child(box, inline_container); - - } else if (type && dom_string_caseless_lwc_isequal(type, - corestring_lwc_image)) { - gadget->type = GADGET_IMAGE; - - if (box->style && ns_computed_display(box->style, - box_is_root(n)) != CSS_DISPLAY_NONE && - nsoption_bool(foreground_images) == true) { - dom_string *s; - - err = dom_element_get_attribute(n, corestring_dom_src, &s); - if (err == DOM_NO_ERR && s != NULL) { - error = nsurl_join(content->base_url, - dom_string_data(s), &url); - dom_string_unref(s); - if (error != NSERROR_OK) - goto no_memory; - - /* if url is equivalent to the parent's url, - * we've got infinite inclusion. stop it here - */ - if (nsurl_compare(url, content->base_url, - NSURL_COMPLETE) == false) { - if (!html_fetch_object(content, url, - box, image_types, - content->base. - available_width, - 1000, false)) { - nsurl_unref(url); - goto no_memory; - } - } - nsurl_unref(url); - } - } - } else { - /* the default type is "text" */ - if (box_input_text(content, box, n) == false) - goto no_memory; - } - - if (type) - dom_string_unref(type); - - *convert_children = false; - return true; - -no_memory: - if (type) - dom_string_unref(type); - - return false; -} - - -/** - * Push button [17.5]. - */ - -bool box_button(BOX_SPECIAL_PARAMS) -{ - struct form_control *gadget; - - gadget = html_forms_get_control_for_node(content->forms, n); - if (!gadget) - return false; - - gadget->html = content; - box->gadget = gadget; - box->flags |= IS_REPLACED; - gadget->box = box; - - box->type = BOX_INLINE_BLOCK; - - /* Just render the contents */ - - return true; -} - - -/** - * Option selector [17.6]. - */ - -bool box_select(BOX_SPECIAL_PARAMS) -{ - struct box *inline_container; - struct box *inline_box; - struct form_control *gadget; - dom_node *c, *c2; - dom_node *next, *next2; - dom_exception err; - - gadget = html_forms_get_control_for_node(content->forms, n); - if (gadget == NULL) - return false; - - gadget->html = content; - err = dom_node_get_first_child(n, &c); - if (err != DOM_NO_ERR) { - form_free_control(gadget); - return false; - } - - while (c != NULL) { - dom_string *name; - - err = dom_node_get_node_name(c, &name); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - form_free_control(gadget); - return false; - } - - if (dom_string_caseless_lwc_isequal(name, - corestring_lwc_option)) { - dom_string_unref(name); - - if (box_select_add_option(gadget, c) == false) { - dom_node_unref(c); - form_free_control(gadget); - return false; - } - } else if (dom_string_caseless_lwc_isequal(name, - corestring_lwc_optgroup)) { - dom_string_unref(name); - - err = dom_node_get_first_child(c, &c2); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - form_free_control(gadget); - return false; - } - - while (c2 != NULL) { - dom_string *c2_name; - - err = dom_node_get_node_name(c2, &c2_name); - if (err != DOM_NO_ERR) { - dom_node_unref(c2); - dom_node_unref(c); - form_free_control(gadget); - return false; - } - - if (dom_string_caseless_lwc_isequal(c2_name, - corestring_lwc_option)) { - dom_string_unref(c2_name); - - if (box_select_add_option(gadget, - c2) == false) { - dom_node_unref(c2); - dom_node_unref(c); - return false; - } - } else { - dom_string_unref(c2_name); - } - - err = dom_node_get_next_sibling(c2, &next2); - if (err != DOM_NO_ERR) { - dom_node_unref(c2); - dom_node_unref(c); - return false; - } - - dom_node_unref(c2); - c2 = next2; - } - } else { - dom_string_unref(name); - } - - err = dom_node_get_next_sibling(c, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - form_free_control(gadget); - return false; - } - - dom_node_unref(c); - c = next; - } - - if (gadget->data.select.num_items == 0) { - /* no options: ignore entire select */ - form_free_control(gadget); - return true; - } - - box->type = BOX_INLINE_BLOCK; - box->gadget = gadget; - box->flags |= IS_REPLACED; - gadget->box = box; - - inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, content->bctx); - if (inline_container == NULL) - goto no_memory; - inline_container->type = BOX_INLINE_CONTAINER; - inline_box = box_create(NULL, box->style, false, 0, 0, box->title, 0, - content->bctx); - if (inline_box == NULL) - goto no_memory; - inline_box->type = BOX_TEXT; - box_add_child(inline_container, inline_box); - box_add_child(box, inline_container); - - if (gadget->data.select.multiple == false && - gadget->data.select.num_selected == 0) { - gadget->data.select.current = gadget->data.select.items; - gadget->data.select.current->initial_selected = - gadget->data.select.current->selected = true; - gadget->data.select.num_selected = 1; - dom_html_option_element_set_selected( - gadget->data.select.current->node, true); - } - - if (gadget->data.select.num_selected == 0) - inline_box->text = talloc_strdup(content->bctx, - messages_get("Form_None")); - else if (gadget->data.select.num_selected == 1) - inline_box->text = talloc_strdup(content->bctx, - gadget->data.select.current->text); - else - inline_box->text = talloc_strdup(content->bctx, - messages_get("Form_Many")); - if (inline_box->text == NULL) - goto no_memory; - - inline_box->length = strlen(inline_box->text); - - *convert_children = false; - return true; - -no_memory: - return false; -} - - -/** - * Add an option to a form select control (helper function for box_select()). - * - * \param control select containing the <option> - * \param n xml element node for <option> - * \return true on success, false on memory exhaustion - */ - -bool box_select_add_option(struct form_control *control, dom_node *n) -{ - char *value = NULL; - char *text = NULL; - char *text_nowrap = NULL; - bool selected; - dom_string *content, *s; - dom_exception err; - - err = dom_node_get_text_content(n, &content); - if (err != DOM_NO_ERR) - return false; - - if (content != NULL) { - text = squash_whitespace(dom_string_data(content)); - dom_string_unref(content); - } else { - text = strdup(""); - } - - if (text == NULL) - goto no_memory; - - err = dom_element_get_attribute(n, corestring_dom_value, &s); - if (err == DOM_NO_ERR && s != NULL) { - value = strdup(dom_string_data(s)); - dom_string_unref(s); - } else { - value = strdup(text); - } - - if (value == NULL) - goto no_memory; - - dom_element_has_attribute(n, corestring_dom_selected, &selected); - - /* replace spaces/TABs with hard spaces to prevent line wrapping */ - text_nowrap = cnv_space2nbsp(text); - if (text_nowrap == NULL) - goto no_memory; - - if (form_add_option(control, value, text_nowrap, selected, n) == false) - goto no_memory; - - free(text); - - return true; - -no_memory: - free(value); - free(text); - free(text_nowrap); - return false; -} - - -/** - * Multi-line text field [17.7]. - */ - -bool box_textarea(BOX_SPECIAL_PARAMS) -{ - /* Get the form_control for the DOM node */ - box->gadget = html_forms_get_control_for_node(content->forms, n); - if (box->gadget == NULL) - return false; - - box->flags |= IS_REPLACED; - box->gadget->html = content; - box->gadget->box = box; - - if (!box_input_text(content, box, n)) - return false; - - *convert_children = false; - return true; -} - - -/** - * Embedded object (not in any HTML specification: - * see http://wp.netscape.com/assist/net_sites/new_html3_prop.html ) - */ - -bool box_embed(BOX_SPECIAL_PARAMS) -{ - struct object_params *params; - struct object_param *param; - dom_namednodemap *attrs; - unsigned long idx; - uint32_t num_attrs; - dom_string *src; - dom_exception err; - - if (box->style && ns_computed_display(box->style, - box_is_root(n)) == CSS_DISPLAY_NONE) - return true; - - params = talloc(content->bctx, struct object_params); - if (params == NULL) - return false; - - talloc_set_destructor(params, box_object_talloc_destructor); - - params->data = NULL; - params->type = NULL; - params->codetype = NULL; - params->codebase = NULL; - params->classid = NULL; - params->params = NULL; - - /* src is a URL */ - err = dom_element_get_attribute(n, corestring_dom_src, &src); - if (err != DOM_NO_ERR || src == NULL) - return true; - if (box_extract_link(content, src, content->base_url, - ¶ms->data) == false) { - dom_string_unref(src); - return false; - } - - dom_string_unref(src); - - if (params->data == NULL) - return true; - - /* Don't include ourself */ - if (nsurl_compare(content->base_url, params->data, NSURL_COMPLETE)) - return true; - - /* add attributes as parameters to linked list */ - err = dom_node_get_attributes(n, &attrs); - if (err != DOM_NO_ERR) - return false; - - err = dom_namednodemap_get_length(attrs, &num_attrs); - if (err != DOM_NO_ERR) { - dom_namednodemap_unref(attrs); - return false; - } - - for (idx = 0; idx < num_attrs; idx++) { - dom_attr *attr; - dom_string *name, *value; - - err = dom_namednodemap_item(attrs, idx, (void *) &attr); - if (err != DOM_NO_ERR) { - dom_namednodemap_unref(attrs); - return false; - } - - err = dom_attr_get_name(attr, &name); - if (err != DOM_NO_ERR) { - dom_namednodemap_unref(attrs); - return false; - } - - if (dom_string_caseless_lwc_isequal(name, corestring_lwc_src)) { - dom_string_unref(name); - continue; - } - - err = dom_attr_get_value(attr, &value); - if (err != DOM_NO_ERR) { - dom_string_unref(name); - dom_namednodemap_unref(attrs); - return false; - } - - param = talloc(content->bctx, struct object_param); - if (param == NULL) { - dom_string_unref(value); - dom_string_unref(name); - dom_namednodemap_unref(attrs); - return false; - } - - param->name = talloc_strdup(content->bctx, dom_string_data(name)); - param->value = talloc_strdup(content->bctx, dom_string_data(value)); - param->type = NULL; - param->valuetype = talloc_strdup(content->bctx, "data"); - param->next = NULL; - - dom_string_unref(value); - dom_string_unref(name); - - if (param->name == NULL || param->value == NULL || - param->valuetype == NULL) { - dom_namednodemap_unref(attrs); - return false; - } - - param->next = params->params; - params->params = param; - } - - dom_namednodemap_unref(attrs); - - box->object_params = params; - - /* start fetch */ - box->flags |= IS_REPLACED; - return html_fetch_object(content, params->data, box, CONTENT_ANY, - content->base.available_width, 1000, false); -} - -/** - * \} - */ - - -/** - * Get the value of an XML element's attribute. - * - * \param n xmlNode, of type XML_ELEMENT_NODE - * \param attribute name of attribute - * \param context talloc context for result buffer - * \param value updated to value, if the attribute is present - * \return true on success, false if attribute present but memory exhausted - * - * Note that returning true does not imply that the attribute was found. If the - * attribute was not found, *value will be unchanged. - */ - -bool box_get_attribute(dom_node *n, const char *attribute, - void *context, char **value) -{ - char *result; - dom_string *attr, *attr_name; - dom_exception err; - - err = dom_string_create_interned((const uint8_t *) attribute, - strlen(attribute), &attr_name); - if (err != DOM_NO_ERR) - return false; - - err = dom_element_get_attribute(n, attr_name, &attr); - if (err != DOM_NO_ERR) { - dom_string_unref(attr_name); - return false; - } - - dom_string_unref(attr_name); - - if (attr != NULL) { - result = talloc_strdup(context, dom_string_data(attr)); - - dom_string_unref(attr); - - if (result == NULL) - return false; - - *value = result; - } - - return true; -} - - -/* exported function documented in render/box.h */ -bool -box_extract_link(const html_content *content, - const dom_string *dsrel, - nsurl *base, - nsurl **result) -{ - char *s, *s1, *apos0 = 0, *apos1 = 0, *quot0 = 0, *quot1 = 0; - unsigned int i, j, end; - nserror error; - const char *rel; - - rel = dom_string_data(dsrel); - - s1 = s = malloc(3 * strlen(rel) + 1); - if (!s) - return false; - - /* copy to s, removing white space and control characters */ - for (i = 0; rel[i] && ascii_is_space(rel[i]); i++) - ; - for (end = strlen(rel); - (end != i) && ascii_is_space(rel[end - 1]); - end--) - ; - for (j = 0; i != end; i++) { - if ((unsigned char) rel[i] < 0x20) { - ; /* skip control characters */ - } else if (rel[i] == ' ') { - s[j++] = '%'; - s[j++] = '2'; - s[j++] = '0'; - } else { - s[j++] = rel[i]; - } - } - s[j] = 0; - - if (content->enable_scripting == false) { - /* extract first quoted string out of "javascript:" link */ - if (strncmp(s, "javascript:", 11) == 0) { - apos0 = strchr(s, '\''); - if (apos0) - apos1 = strchr(apos0 + 1, '\''); - quot0 = strchr(s, '"'); - if (quot0) - quot1 = strchr(quot0 + 1, '"'); - if (apos0 && apos1 && - (!quot0 || !quot1 || apos0 < quot0)) { - *apos1 = 0; - s1 = apos0 + 1; - } else if (quot0 && quot1) { - *quot1 = 0; - s1 = quot0 + 1; - } - } - } - - /* construct absolute URL */ - error = nsurl_join(base, s1, result); - free(s); - if (error != NSERROR_OK) { - *result = NULL; - return false; - } - - return true; -} - - - |