diff options
author | Vincent Sanders <vince@kyllikki.org> | 2020-04-29 20:37:42 +0100 |
---|---|---|
committer | Vincent Sanders <vince@kyllikki.org> | 2020-04-29 20:37:42 +0100 |
commit | e8d0ba15ad4a91b4644cac0095f40bb3246ec509 (patch) | |
tree | 933e479208aec34f88b483b7e63c057d350339a0 | |
parent | a64261c3bdde79d11c86960181f7b8b5255539be (diff) | |
download | netsurf-e8d0ba15ad4a91b4644cac0095f40bb3246ec509.tar.gz netsurf-e8d0ba15ad4a91b4644cac0095f40bb3246ec509.tar.bz2 |
split html box processing code
reduce the module size of the html box handling code by
splitting into smaller sections.
No functional code change.
23 files changed, 2961 insertions, 2674 deletions
diff --git a/content/handlers/html/Makefile b/content/handlers/html/Makefile index 6b4c1e8ee..19766247d 100644 --- a/content/handlers/html/Makefile +++ b/content/handlers/html/Makefile @@ -1,7 +1,23 @@ # HTML content handler sources -S_HTML := box.c box_construct.c box_normalise.c box_textarea.c \ - font.c form.c imagemap.c layout.c search.c table.c \ - html.c html_css.c html_css_fetcher.c html_script.c \ - interaction.c html_redraw.c html_redraw_border.c \ - html_forms.c html_object.c +S_HTML := box_construct.c \ + box_inspect.c \ + box_manipulate.c \ + box_normalise.c \ + box_special.c \ + box_textarea.c \ + font.c \ + form.c \ + imagemap.c \ + layout.c \ + search.c \ + table.c \ + html.c \ + html_css.c \ + html_css_fetcher.c \ + html_script.c \ + interaction.c \ + html_redraw.c \ + html_redraw_border.c \ + html_forms.c \ + html_object.c diff --git a/content/handlers/html/box.h b/content/handlers/html/box.h index 1781d1332..b4da031f1 100644 --- a/content/handlers/html/box.h +++ b/content/handlers/html/box.h @@ -1,6 +1,7 @@ /* * Copyright 2005 James Bursa <bursa@users.sourceforge.net> * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> + * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -19,69 +20,8 @@ /** * \file - * Box tree construction and manipulation interface. + * Box interface. * - * This stage of rendering converts a tree of dom_nodes (produced by libdom) - * to a tree of struct box. The box tree represents the structure of the - * document as given by the CSS display and float properties. - * - * For example, consider the following HTML: - * \code - * <h1>Example Heading</h1> - * <p>Example paragraph <em>with emphasised text</em> etc.</p> \endcode - * - * This would produce approximately the following box tree with default CSS - * rules: - * \code - * BOX_BLOCK (corresponds to h1) - * BOX_INLINE_CONTAINER - * BOX_INLINE "Example Heading" - * BOX_BLOCK (p) - * BOX_INLINE_CONTAINER - * BOX_INLINE "Example paragraph " - * BOX_INLINE "with emphasised text" (em) - * BOX_INLINE "etc." \endcode - * - * Note that the em has been collapsed into the INLINE_CONTAINER. - * - * If these CSS rules were applied: - * \code - * h1 { display: table-cell } - * p { display: table-cell } - * em { float: left; width: 5em } \endcode - * - * then the box tree would instead look like this: - * \code - * BOX_TABLE - * BOX_TABLE_ROW_GROUP - * BOX_TABLE_ROW - * BOX_TABLE_CELL (h1) - * BOX_INLINE_CONTAINER - * BOX_INLINE "Example Heading" - * BOX_TABLE_CELL (p) - * BOX_INLINE_CONTAINER - * BOX_INLINE "Example paragraph " - * BOX_FLOAT_LEFT (em) - * BOX_BLOCK - * BOX_INLINE_CONTAINER - * BOX_INLINE "with emphasised text" - * BOX_INLINE "etc." \endcode - * - * Here implied boxes have been added and a float is present. - * - * A box tree is "normalized" if the following is satisfied: - * \code - * parent permitted child nodes - * BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE - * INLINE_CONTAINER INLINE, INLINE_BLOCK, FLOAT_LEFT, FLOAT_RIGHT, BR, TEXT, - * INLINE_END - * INLINE none - * TABLE at least 1 TABLE_ROW_GROUP - * TABLE_ROW_GROUP at least 1 TABLE_ROW - * TABLE_ROW at least 1 TABLE_CELL - * TABLE_CELL BLOCK, INLINE_CONTAINER, TABLE (same as BLOCK) - * FLOAT_(LEFT|RIGHT) exactly 1 BLOCK or TABLE - * \endcode */ #ifndef NETSURF_HTML_BOX_H @@ -89,7 +29,6 @@ #include <limits.h> #include <stdbool.h> -#include <stdio.h> #include <libcss/libcss.h> #include "content/handlers/css/utils.h" @@ -517,197 +456,4 @@ extern const char *TARGET_TOP; extern const char *TARGET_BLANK; -/** - * Create a box tree node. - * - * \param styles selection results for the box, or NULL - * \param style computed style for the box (not copied), or 0 - * \param style_owned whether style is owned by this box - * \param href href for the box (copied), or 0 - * \param target target for the box (not copied), or 0 - * \param title title for the box (not copied), or 0 - * \param id id for the box (not copied), or 0 - * \param context context for allocations - * \return allocated and initialised box, or 0 on memory exhaustion - * - * styles is always owned by the box, if it is set. - * style is only owned by the box in the case of implied boxes. - */ -struct box * box_create(css_select_results *styles, css_computed_style *style, bool style_owned, struct nsurl *href, const char *target, const char *title, lwc_string *id, void *context); - - -/** - * Add a child to a box tree node. - * - * \param parent box giving birth - * \param child box to link as last child of parent - */ -void box_add_child(struct box *parent, struct box *child); - - -/** - * Insert a new box as a sibling to a box in a tree. - * - * \param box box already in tree - * \param new_box box to link into tree as next sibling - */ -void box_insert_sibling(struct box *box, struct box *new_box); - - -/** - * Unlink a box from the box tree and then free it recursively. - * - * \param box box to unlink and free recursively. - */ -void box_unlink_and_free(struct box *box); - - -/** - * Free a box tree recursively. - * - * \param box box to free recursively - * - * The box and all its children is freed. - */ -void box_free(struct box *box); - - -/** - * Free the data in a single box structure. - * - * \param box box to free - */ -void box_free_box(struct box *box); - - -/** - * Find the absolute coordinates of a box. - * - * \param box the box to calculate coordinates of - * \param x updated to x coordinate - * \param y updated to y coordinate - */ -void box_coords(struct box *box, int *x, int *y); - - -/** - * Find the bounds of a box. - * - * \param box the box to calculate bounds of - * \param r receives bounds - */ -void box_bounds(struct box *box, struct rect *r); - - -/** - * Find the boxes at a point. - * - * \param len_ctx CSS length conversion context for document. - * \param box box to search children of - * \param x point to find, in global document coordinates - * \param y point to find, in global document coordinates - * \param box_x position of box, in global document coordinates, updated - * to position of returned box, if any - * \param box_y position of box, in global document coordinates, updated - * to position of returned box, if any - * \return box at given point, or 0 if none found - * - * To find all the boxes in the hierarchy at a certain point, use code like - * this: - * \code - * struct box *box = top_of_document_to_search; - * int box_x = 0, box_y = 0; - * - * while ((box = box_at_point(len_ctx, box, x, y, &box_x, &box_y))) { - * // process box - * } - * \endcode - */ -struct box *box_at_point(const nscss_len_ctx *len_ctx, struct box *box, const int x, const int y, int *box_x, int *box_y); - - -/** - * Peform pick text on browser window contents to locate the box under - * the mouse pointer, or nearest in the given direction if the pointer is - * not over a text box. - * - * \param html an HTML content - * \param x coordinate of mouse - * \param y coordinate of mouse - * \param dir direction to search (-1 = above-left, +1 = below-right) - * \param dx receives x ordinate of mouse relative to text box - * \param dy receives y ordinate of mouse relative to text box - */ -struct box *box_pick_text_box(struct html_content *html, int x, int y, int dir, int *dx, int *dy); - - -/** - * Find a box based upon its id attribute. - * - * \param box box tree to search - * \param id id to look for - * \return the box or 0 if not found - */ -struct box *box_find_by_id(struct box *box, lwc_string *id); - - -/** - * Determine if a box is visible when the tree is rendered. - * - * \param box box to check - * \return true iff the box is rendered - */ -bool box_visible(struct box *box); - - -/** - * Print a box tree to a file. - */ -void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style); - - -/** - * Applies the given scroll setup to a box. This includes scroll - * creation/deletion as well as scroll dimension updates. - * - * \param c content in which the box is located - * \param box the box to handle the scrolls for - * \param bottom whether the horizontal scrollbar should be present - * \param right whether the vertical scrollbar should be present - * \return true on success false otherwise - */ -nserror box_handle_scrollbars(struct content *c, struct box *box, - bool bottom, bool right); - - -/** - * Determine if a box has a vertical scrollbar. - * - * \param box scrolling box - * \return the box has a vertical scrollbar - */ -bool box_vscrollbar_present(const struct box *box); - - -/** - * Determine if a box has a horizontal scrollbar. - * - * \param box scrolling box - * \return the box has a horizontal scrollbar - */ -bool box_hscrollbar_present(const struct box *box); - - -/** - * Check if layout box is a first child. - * - * \param[in] b Box to check. - * \return true iff box is first child. - */ -static inline bool box_is_first_child(struct box *b) -{ - return (b->parent == NULL || b == b->parent->children); -} - - #endif diff --git a/content/handlers/html/box_construct.c b/content/handlers/html/box_construct.c index a83330240..1637c0fb1 100644 --- a/content/handlers/html/box_construct.c +++ b/content/handlers/html/box_construct.c @@ -25,37 +25,25 @@ * 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 <dom/dom.h> + +#include "utils/errors.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 "html/html.h" +#include "html/html_internal.h" #include "html/box.h" +#include "html/box_manipulate.h" #include "html/box_construct.h" +#include "html/box_special.h" #include "html/box_normalise.h" -#include "html/box_textarea.h" #include "html/form_internal.h" -#include "html/html_internal.h" /** * Context for box tree construction @@ -159,1752 +147,6 @@ static inline bool box_is_root(dom_node *n) /** - * 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; -} - - -/** - * 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; -} - - -/** - * 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; -} - - -/** - * create a frameset box tree - */ -static 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); - } - - if (dom_element_has_attribute(c, corestring_dom_noresize, - &frame->no_resize) != DOM_NO_ERR) { - /* If we can't read the attribute for some reason, - * assume we didn't have it. - */ - frame->no_resize = false; - } - - 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; -} - - -/** - * Get the value of a dom node element's attribute. - * - * \param n dom 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 returning true does not imply that the attribute was found. If the - * attribute was not found, *value will be unchanged. - */ -static 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; -} - - -/** - * 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); -} - - -/** - * 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 - */ -static 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; - - if (dom_element_has_attribute(n, corestring_dom_selected, &selected) != DOM_NO_ERR) { - /* Assume not selected if we can't read the attribute presence */ - selected = false; - } - - /* 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; -} - - -/** - * \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. - * - * \{ - */ - -/** - * special element handler for Anchor [12.2]. - */ -static bool -box_a(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - 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; -} - - -/** - * Document body special element handler [7.5.1]. - */ -static bool -box_body(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - 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; -} - - -/** - * special element handler for forced line break [9.3.2]. - */ -static bool -box_br(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - box->type = BOX_BR; - return true; -} - - -/** - * special element handler for Push button [17.5]. - */ -static bool -box_button(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - 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; -} - - -/** - * Canvas element - */ -static bool -box_canvas(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - /* If scripting is not enabled display the contents of canvas */ - if (!content->enable_scripting) { - return true; - } - *convert_children = false; - - return true; -} - - -/** - * Embedded object (not in any HTML specification: - * see http://wp.netscape.com/assist/net_sites/new_html3_prop.html ) - */ -static bool -box_embed(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - 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_node_unref(attr); - dom_namednodemap_unref(attrs); - return false; - } - - if (dom_string_caseless_lwc_isequal(name, corestring_lwc_src)) { - dom_node_unref(attr); - dom_string_unref(name); - continue; - } - - err = dom_attr_get_value(attr, &value); - if (err != DOM_NO_ERR) { - dom_node_unref(attr); - dom_string_unref(name); - dom_namednodemap_unref(attrs); - return false; - } - - param = talloc(content->bctx, struct object_param); - if (param == NULL) { - dom_node_unref(attr); - 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); - dom_node_unref(attr); - - 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); -} - - -/** - * Window subdivision [16.2.1]. - */ -static bool -box_frameset(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - 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; -} - - -/** - * Inline subwindow [16.5]. - */ -static bool -box_iframe(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - 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; -} - - -/** - * Embedded image [13.2]. - */ -static bool -box_image(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - 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; -} - - -/** - * Form control [17.4]. - */ -static bool -box_input(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - struct form_control *gadget; - dom_string *type = NULL; - dom_exception err; - nsurl *url; - nserror error; - - gadget = html_forms_get_control_for_node(content->forms, n); - if (gadget == NULL) { - return false; - } - - box->gadget = gadget; - box->flags |= IS_REPLACED; - gadget->box = box; - gadget->html = content; - - /* get entry type */ - err = dom_element_get_attribute(n, corestring_dom_type, &type); - if ((err != DOM_NO_ERR) || (type == NULL)) { - /* no type so "text" is assumed */ - if (box_input_text(content, box, n) == false) { - return false; - } - *convert_children = false; - return true; - } - - if (dom_string_caseless_lwc_isequal(type, corestring_lwc_password)) { - if (box_input_text(content, box, n) == false) - goto no_memory; - - } else if (dom_string_caseless_lwc_isequal(type, corestring_lwc_file)) { - box->type = BOX_INLINE_BLOCK; - - } else if (dom_string_caseless_lwc_isequal(type, - corestring_lwc_hidden)) { - /* no box for hidden inputs */ - box->type = BOX_NONE; - - } else if ((dom_string_caseless_lwc_isequal(type, - corestring_lwc_checkbox) || - dom_string_caseless_lwc_isequal(type, - corestring_lwc_radio))) { - - } else if (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 (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 { - /* unhandled type the default is "text" */ - if (box_input_text(content, box, n) == false) - goto no_memory; - } - - dom_string_unref(type); - - *convert_children = false; - - return true; - -no_memory: - dom_string_unref(type); - - return false; -} - - -/** - * Noscript element - */ -static bool -box_noscript(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - /* If scripting is enabled, do not display the contents of noscript */ - if (content->enable_scripting) { - *convert_children = false; - } - - return true; -} - - -/** - * Generic embedded object [13.3]. - */ -static bool -box_object(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - 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_string_unref(name); - dom_node_unref(c); - break; - } - dom_string_unref(name); - - 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; -} - - -/** - * Preformatted text [9.3.4]. - */ -static bool -box_pre(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - box->flags |= PRE_STRIP; - return true; -} - - -/** - * Option selector [17.6]. - */ -static bool -box_select(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - 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); - form_free_control(gadget); - 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); - form_free_control(gadget); - 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; -} - - -/** - * Multi-line text field [17.7]. - */ -static bool box_textarea(dom_node *n, - html_content *content, - struct box *box, - bool *convert_children) -{ - /* 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; -} - - -/** - * \} - */ - - -/** * Extract transient construction properties * * \param n Current DOM node to convert @@ -2245,93 +487,6 @@ box_construct_marker(struct box *box, /** - * call an elements special handler - */ -static bool -convert_special_elements(dom_node *node, - html_content *content, - struct box *box, - bool *convert_children) -{ - dom_exception exc; - dom_html_element_type tag_type; - bool res; - - exc = dom_html_element_get_tag_type(node, &tag_type); - if (exc != DOM_NO_ERR) { - tag_type = DOM_HTML_ELEMENT_TYPE__UNKNOWN; - } - - switch (tag_type) { - case DOM_HTML_ELEMENT_TYPE_A: - res = box_a(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_BODY: - res = box_body(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_BR: - res = box_br(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_BUTTON: - res = box_button(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_CANVAS: - res = box_canvas(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_EMBED: - res = box_embed(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_FRAMESET: - res = box_frameset(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_IFRAME: - res = box_iframe(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_IMG: - res = box_image(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_INPUT: - res = box_input(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_NOSCRIPT: - res = box_noscript(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_OBJECT: - res = box_object(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_PRE: - res = box_pre(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_SELECT: - res = box_select(node, content, box, convert_children); - break; - - case DOM_HTML_ELEMENT_TYPE_TEXTAREA: - res = box_textarea(node, content, box, convert_children); - break; - - default: - res = true; - } - - return res; -} - - -/** * Construct the box tree for an XML element. * * \param ctx Tree construction context diff --git a/content/handlers/html/box_construct.h b/content/handlers/html/box_construct.h index e93f515e4..f4bd119b1 100644 --- a/content/handlers/html/box_construct.h +++ b/content/handlers/html/box_construct.h @@ -19,6 +19,54 @@ /** * \file * HTML Box tree construction interface. + * + * This stage of rendering converts a tree of dom_nodes (produced by libdom) + * to a tree of struct box. The box tree represents the structure of the + * document as given by the CSS display and float properties. + * + * For example, consider the following HTML: + * \code + * <h1>Example Heading</h1> + * <p>Example paragraph <em>with emphasised text</em> etc.</p> \endcode + * + * This would produce approximately the following box tree with default CSS + * rules: + * \code + * BOX_BLOCK (corresponds to h1) + * BOX_INLINE_CONTAINER + * BOX_INLINE "Example Heading" + * BOX_BLOCK (p) + * BOX_INLINE_CONTAINER + * BOX_INLINE "Example paragraph " + * BOX_INLINE "with emphasised text" (em) + * BOX_INLINE "etc." \endcode + * + * Note that the em has been collapsed into the INLINE_CONTAINER. + * + * If these CSS rules were applied: + * \code + * h1 { display: table-cell } + * p { display: table-cell } + * em { float: left; width: 5em } \endcode + * + * then the box tree would instead look like this: + * \code + * BOX_TABLE + * BOX_TABLE_ROW_GROUP + * BOX_TABLE_ROW + * BOX_TABLE_CELL (h1) + * BOX_INLINE_CONTAINER + * BOX_INLINE "Example Heading" + * BOX_TABLE_CELL (p) + * BOX_INLINE_CONTAINER + * BOX_INLINE "Example paragraph " + * BOX_FLOAT_LEFT (em) + * BOX_BLOCK + * BOX_INLINE_CONTAINER + * BOX_INLINE "with emphasised text" + * BOX_INLINE "etc." \endcode + * + * Here implied boxes have been added and a float is present. */ #ifndef NETSURF_HTML_BOX_CONSTRUCT_H diff --git a/content/handlers/html/box.c b/content/handlers/html/box_inspect.c index 0ad10619b..268ed3d95 100644 --- a/content/handlers/html/box.c +++ b/content/handlers/html/box_inspect.c @@ -1,8 +1,6 @@ /* - * Copyright 2005-2007 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 2008 Michael Drake <tlsa@netsurf-browser.org> + * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -21,276 +19,39 @@ /** * \file - * implementation of box tree manipulation. + * implementation of box tree inspection. */ -#include <assert.h> -#include <stdbool.h> #include <stdio.h> -#include <string.h> #include <dom/dom.h> -#include "utils/nsoption.h" -#include "utils/log.h" -#include "utils/talloc.h" #include "utils/nsurl.h" -#include "netsurf/misc.h" +#include "utils/errors.h" +#include "netsurf/types.h" #include "netsurf/content.h" #include "netsurf/mouse.h" #include "css/utils.h" #include "css/dump.h" #include "desktop/scrollbar.h" -#include "desktop/gui_internal.h" -#include "desktop/search.h" #include "html/box.h" -#include "html/form_internal.h" +#include "html/box_inspect.h" #include "html/html_internal.h" -#include "html/interaction.h" - -#define box_is_float(box) (box->type == BOX_FLOAT_LEFT || \ - box->type == BOX_FLOAT_RIGHT) /** - * Destructor for box nodes which own styles - * - * \param b The box being destroyed. - * \return 0 to allow talloc to continue destroying the tree. + * Direction to move in a box-tree walk */ -static int box_talloc_destructor(struct box *b) -{ - struct html_scrollbar_data *data; - - if ((b->flags & STYLE_OWNED) && b->style != NULL) { - css_computed_style_destroy(b->style); - b->style = NULL; - } - - if (b->styles != NULL) { - css_select_results_destroy(b->styles); - b->styles = NULL; - } - - if (b->href != NULL) - nsurl_unref(b->href); - - if (b->id != NULL) { - lwc_string_unref(b->id); - } - - if (b->node != NULL) { - dom_node_unref(b->node); - } - - if (b->scroll_x != NULL) { - data = scrollbar_get_data(b->scroll_x); - scrollbar_destroy(b->scroll_x); - free(data); - } - - if (b->scroll_y != NULL) { - data = scrollbar_get_data(b->scroll_y); - scrollbar_destroy(b->scroll_y); - free(data); - } - - return 0; -} - - -/* Exported function documented in html/box.h */ -struct box * -box_create(css_select_results *styles, - css_computed_style *style, - bool style_owned, - nsurl *href, - const char *target, - const char *title, - lwc_string *id, - void *context) -{ - unsigned int i; - struct box *box; - - box = talloc(context, struct box); - if (!box) { - return 0; - } - - talloc_set_destructor(box, box_talloc_destructor); - - box->type = BOX_INLINE; - box->flags = 0; - box->flags = style_owned ? (box->flags | STYLE_OWNED) : box->flags; - box->styles = styles; - box->style = style; - box->x = box->y = 0; - box->width = UNKNOWN_WIDTH; - box->height = 0; - box->descendant_x0 = box->descendant_y0 = 0; - box->descendant_x1 = box->descendant_y1 = 0; - for (i = 0; i != 4; i++) - box->margin[i] = box->padding[i] = box->border[i].width = 0; - box->scroll_x = box->scroll_y = NULL; - box->min_width = 0; - box->max_width = UNKNOWN_MAX_WIDTH; - box->byte_offset = 0; - box->text = NULL; - box->length = 0; - box->space = 0; - box->href = (href == NULL) ? NULL : nsurl_ref(href); - box->target = target; - box->title = title; - box->columns = 1; - box->rows = 1; - box->start_column = 0; - box->next = NULL; - box->prev = NULL; - box->children = NULL; - box->last = NULL; - box->parent = NULL; - box->inline_end = NULL; - box->float_children = NULL; - box->float_container = NULL; - box->next_float = NULL; - box->cached_place_below_level = 0; - box->list_marker = NULL; - box->col = NULL; - box->gadget = NULL; - box->usemap = NULL; - box->id = id; - box->background = NULL; - box->object = NULL; - box->object_params = NULL; - box->iframe = NULL; - box->node = NULL; - - return box; -} - - -/* Exported function documented in html/box.h */ -void box_add_child(struct box *parent, struct box *child) -{ - assert(parent); - assert(child); - - if (parent->children != 0) { /* has children already */ - parent->last->next = child; - child->prev = parent->last; - } else { /* this is the first child */ - parent->children = child; - child->prev = 0; - } - - parent->last = child; - child->parent = parent; -} - - -/* Exported function documented in html/box.h */ -void box_insert_sibling(struct box *box, struct box *new_box) -{ - new_box->parent = box->parent; - new_box->prev = box; - new_box->next = box->next; - box->next = new_box; - if (new_box->next) - new_box->next->prev = new_box; - else if (new_box->parent) - new_box->parent->last = new_box; -} - - -/* Exported function documented in html/box.h */ -void box_unlink_and_free(struct box *box) -{ - struct box *parent = box->parent; - struct box *next = box->next; - struct box *prev = box->prev; - - if (parent) { - if (parent->children == box) - parent->children = next; - if (parent->last == box) - parent->last = next ? next : prev; - } - - if (prev) - prev->next = next; - if (next) - next->prev = prev; - - box_free(box); -} - - -/* Exported function documented in html/box.h */ -void box_free(struct box *box) -{ - struct box *child, *next; - - /* free children first */ - for (child = box->children; child; child = next) { - next = child->next; - box_free(child); - } - - /* last this box */ - box_free_box(box); -} - - -/* Exported function documented in html/box.h */ -void box_free_box(struct box *box) -{ - if (!(box->flags & CLONE)) { - if (box->gadget) - form_free_control(box->gadget); - if (box->scroll_x != NULL) - scrollbar_destroy(box->scroll_x); - if (box->scroll_y != NULL) - scrollbar_destroy(box->scroll_y); - if (box->styles != NULL) - css_select_results_destroy(box->styles); - } - - talloc_free(box); -} - - -/* Exported function documented in html/box.h */ -void box_coords(struct box *box, int *x, int *y) -{ - *x = box->x; - *y = box->y; - while (box->parent) { - if (box_is_float(box)) { - assert(box->float_container); - box = box->float_container; - } else { - box = box->parent; - } - *x += box->x - scrollbar_get_offset(box->scroll_x); - *y += box->y - scrollbar_get_offset(box->scroll_y); - } -} - - -/* Exported function documented in html/box.h */ -void box_bounds(struct box *box, struct rect *r) -{ - int width, height; - - box_coords(box, &r->x0, &r->y0); - - width = box->padding[LEFT] + box->width + box->padding[RIGHT]; - height = box->padding[TOP] + box->height + box->padding[BOTTOM]; - - r->x1 = r->x0 + width; - r->y1 = r->y0 + height; -} +enum box_walk_dir { + BOX_WALK_CHILDREN, + BOX_WALK_PARENT, + BOX_WALK_NEXT_SIBLING, + BOX_WALK_FLOAT_CHILDREN, + BOX_WALK_NEXT_FLOAT_SIBLING, + BOX_WALK_FLOAT_CONTAINER +}; +#define box_is_float(box) (box->type == BOX_FLOAT_LEFT || \ + box->type == BOX_FLOAT_RIGHT) /** * Determine if a point lies within a box. @@ -309,60 +70,61 @@ void box_bounds(struct box *box, struct rect *r) * * This is a helper function for box_at_point(). */ -static bool box_contains_point( - const nscss_len_ctx *len_ctx, - const struct box *box, - int x, - int y, - bool *physically) +static bool +box_contains_point(const nscss_len_ctx *len_ctx, + const struct box *box, + int x, + int y, + bool *physically) { css_computed_clip_rect css_rect; if (box->style != NULL && - css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE && - css_computed_clip(box->style, &css_rect) == - CSS_CLIP_RECT) { + css_computed_position(box->style) == CSS_POSITION_ABSOLUTE && + css_computed_clip(box->style, &css_rect) == CSS_CLIP_RECT) { /* We have an absolutly positioned box with a clip rect */ struct rect r = { - .x0 = box->border[LEFT].width, - .y0 = box->border[TOP].width, - .x1 = box->padding[LEFT] + box->width + - box->border[RIGHT].width + - box->padding[RIGHT], - .y1 = box->padding[TOP] + box->height + - box->border[BOTTOM].width + - box->padding[BOTTOM] + .x0 = box->border[LEFT].width, + .y0 = box->border[TOP].width, + .x1 = box->padding[LEFT] + box->width + + box->border[RIGHT].width + + box->padding[RIGHT], + .y1 = box->padding[TOP] + box->height + + box->border[BOTTOM].width + + box->padding[BOTTOM] }; - if (x >= r.x0 && x < r.x1 && y >= r.y0 && y < r.y1) + if (x >= r.x0 && x < r.x1 && y >= r.y0 && y < r.y1) { *physically = true; - else + } else { *physically = false; + } /* Adjust rect to css clip region */ if (css_rect.left_auto == false) { r.x0 += FIXTOINT(nscss_len2px(len_ctx, - css_rect.left, css_rect.lunit, - box->style)); + css_rect.left, + css_rect.lunit, + box->style)); } if (css_rect.top_auto == false) { r.y0 += FIXTOINT(nscss_len2px(len_ctx, - css_rect.top, css_rect.tunit, - box->style)); + css_rect.top, + css_rect.tunit, + box->style)); } if (css_rect.right_auto == false) { r.x1 = box->border[LEFT].width + - FIXTOINT(nscss_len2px(len_ctx, - css_rect.right, - css_rect.runit, - box->style)); + FIXTOINT(nscss_len2px(len_ctx, + css_rect.right, + css_rect.runit, + box->style)); } if (css_rect.bottom_auto == false) { r.y1 = box->border[TOP].width + - FIXTOINT(nscss_len2px(len_ctx, - css_rect.bottom, - css_rect.bunit, - box->style)); + FIXTOINT(nscss_len2px(len_ctx, + css_rect.bottom, + css_rect.bunit, + box->style)); } /* Test if point is in clipped box */ @@ -375,43 +137,43 @@ static bool box_contains_point( return false; } if (x >= -box->border[LEFT].width && - x < box->padding[LEFT] + box->width + - box->padding[RIGHT] + box->border[RIGHT].width && - y >= -box->border[TOP].width && - y < box->padding[TOP] + box->height + - box->padding[BOTTOM] + box->border[BOTTOM].width) { + x < box->padding[LEFT] + box->width + + box->padding[RIGHT] + box->border[RIGHT].width && + y >= -box->border[TOP].width && + y < box->padding[TOP] + box->height + + box->padding[BOTTOM] + box->border[BOTTOM].width) { *physically = true; return true; } if (box->list_marker && box->list_marker->x - box->x <= x + - box->list_marker->border[LEFT].width && - x < box->list_marker->x - box->x + - box->list_marker->padding[LEFT] + - box->list_marker->width + - box->list_marker->border[RIGHT].width + - box->list_marker->padding[RIGHT] && - box->list_marker->y - box->y <= y + - box->list_marker->border[TOP].width && - y < box->list_marker->y - box->y + - box->list_marker->padding[TOP] + - box->list_marker->height + - box->list_marker->border[BOTTOM].width + - box->list_marker->padding[BOTTOM]) { + box->list_marker->border[LEFT].width && + x < box->list_marker->x - box->x + + box->list_marker->padding[LEFT] + + box->list_marker->width + + box->list_marker->border[RIGHT].width + + box->list_marker->padding[RIGHT] && + box->list_marker->y - box->y <= y + + box->list_marker->border[TOP].width && + y < box->list_marker->y - box->y + + box->list_marker->padding[TOP] + + box->list_marker->height + + box->list_marker->border[BOTTOM].width + + box->list_marker->padding[BOTTOM]) { *physically = true; return true; } if ((box->style && css_computed_overflow_x(box->style) == - CSS_OVERFLOW_VISIBLE) || !box->style) { + CSS_OVERFLOW_VISIBLE) || !box->style) { if (box->descendant_x0 <= x && - x < box->descendant_x1) { + x < box->descendant_x1) { *physically = false; return true; } } if ((box->style && css_computed_overflow_y(box->style) == - CSS_OVERFLOW_VISIBLE) || !box->style) { + CSS_OVERFLOW_VISIBLE) || !box->style) { if (box->descendant_y0 <= y && - y < box->descendant_y1) { + y < box->descendant_y1) { *physically = false; return true; } @@ -421,25 +183,12 @@ static bool box_contains_point( /** - * Direction to move in a box-tree walk - */ -enum box_walk_dir { - BOX_WALK_CHILDREN, - BOX_WALK_PARENT, - BOX_WALK_NEXT_SIBLING, - BOX_WALK_FLOAT_CHILDREN, - BOX_WALK_NEXT_FLOAT_SIBLING, - BOX_WALK_FLOAT_CONTAINER -}; - - -/** * Move from box to next box in given direction, adjusting for box coord change * - * \param b box to move from from - * \param dir direction to move in - * \param x box's global x-coord, updated to position of next box - * \param y box's global y-coord, updated to position of next box + * \param b box to move from from + * \param dir direction to move in + * \param x box's global x-coord, updated to position of next box + * \param y box's global y-coord, updated to position of next box * * If no box can be found in given direction, NULL is returned. */ @@ -525,8 +274,8 @@ box_move_xy(struct box *b, enum box_walk_dir dir, int *x, int *y) * This walks to a boxes float children before its children. When walking * children, floating boxes are skipped. */ -static inline struct box *box_next_xy(struct box *b, int *x, int *y, - bool skip_children) +static inline struct box * +box_next_xy(struct box *b, int *x, int *y, bool skip_children) { struct box *n; int tx, ty; @@ -546,7 +295,7 @@ static inline struct box *box_next_xy(struct box *b, int *x, int *y, *y = ty; return n; } -done_float_children: + done_float_children: tx = *x; ty = *y; n = box_move_xy(b, BOX_WALK_CHILDREN, &tx, &ty); @@ -557,7 +306,7 @@ done_float_children: return n; } -skip_children: + skip_children: tx = *x; ty = *y; n = box_move_xy(b, BOX_WALK_NEXT_FLOAT_SIBLING, &tx, &ty); if (n) { @@ -616,38 +365,6 @@ skip_children: } -/* Exported function documented in html/box.h */ -struct box * -box_at_point(const nscss_len_ctx *len_ctx, - struct box *box, - const int x, const int y, - int *box_x, int *box_y) -{ - bool skip_children; - bool physically; - - assert(box); - - skip_children = false; - while ((box = box_next_xy(box, box_x, box_y, skip_children))) { - if (box_contains_point(len_ctx, box, x - *box_x, y - *box_y, - &physically)) { - *box_x -= scrollbar_get_offset(box->scroll_x); - *box_y -= scrollbar_get_offset(box->scroll_y); - - if (physically) - return box; - - skip_children = false; - } else { - skip_children = true; - } - } - - return NULL; -} - - /** * Check whether box is nearer mouse coordinates than current nearest box * @@ -746,9 +463,15 @@ box_nearer_text_box(struct box *box, * updated if a descendant of box is nearer than old nearest * \return true if mouse point is inside text_box */ -static bool box_nearest_text_box(struct box *box, int bx, int by, - int fx, int fy, int x, int y, int dir, struct box **nearest, - int *tx, int *ty, int *nr_xd, int *nr_yd) +static bool +box_nearest_text_box(struct box *box, + int bx, int by, + int fx, int fy, + int x, int y, + int dir, + struct box **nearest, + int *tx, int *ty, + int *nr_xd, int *nr_yd) { struct box *child = box->children; int c_bx, c_by; @@ -771,16 +494,16 @@ static bool box_nearest_text_box(struct box *box, int bx, int by, while (child) { if (child->type == BOX_FLOAT_LEFT || - child->type == BOX_FLOAT_RIGHT) { + child->type == BOX_FLOAT_RIGHT) { c_bx = fx + child->x - - scrollbar_get_offset(child->scroll_x); + scrollbar_get_offset(child->scroll_x); c_by = fy + child->y - - scrollbar_get_offset(child->scroll_y); + scrollbar_get_offset(child->scroll_y); } else { c_bx = bx + child->x - - scrollbar_get_offset(child->scroll_x); + scrollbar_get_offset(child->scroll_x); c_by = by + child->y - - scrollbar_get_offset(child->scroll_y); + scrollbar_get_offset(child->scroll_y); } if (child->float_children) { c_fx = c_bx; @@ -791,8 +514,8 @@ static bool box_nearest_text_box(struct box *box, int bx, int by, } if (in_box && child->text && !child->object) { if (box_nearer_text_box(child, - c_bx, c_by, x, y, dir, nearest, - tx, ty, nr_xd, nr_yd)) + c_bx, c_by, x, y, dir, nearest, + tx, ty, nr_xd, nr_yd)) return true; } else { if (child->list_marker) { @@ -805,8 +528,9 @@ static bool box_nearest_text_box(struct box *box, int bx, int by, return true; } if (box_nearest_text_box(child, c_bx, c_by, - c_fx, c_fy, x, y, dir, nearest, tx, ty, - nr_xd, nr_yd)) + c_fx, c_fy, + x, y, dir, nearest, tx, ty, + nr_xd, nr_yd)) return true; } child = child->next; @@ -817,55 +541,67 @@ static bool box_nearest_text_box(struct box *box, int bx, int by, /* Exported function documented in html/box.h */ -struct box * -box_pick_text_box(struct html_content *html, - int x, int y, - int dir, - int *dx, int *dy) +void box_coords(struct box *box, int *x, int *y) { - struct box *text_box = NULL; - struct box *box; - int nr_xd, nr_yd; - int bx, by; - int fx, fy; - int tx, ty; + *x = box->x; + *y = box->y; + while (box->parent) { + if (box_is_float(box)) { + assert(box->float_container); + box = box->float_container; + } else { + box = box->parent; + } + *x += box->x - scrollbar_get_offset(box->scroll_x); + *y += box->y - scrollbar_get_offset(box->scroll_y); + } +} - if (html == NULL) - return NULL; - box = html->layout; - bx = box->margin[LEFT]; - by = box->margin[TOP]; - fx = bx; - fy = by; +/* Exported function documented in html/box.h */ +void box_bounds(struct box *box, struct rect *r) +{ + int width, height; - if (!box_nearest_text_box(box, bx, by, fx, fy, x, y, - dir, &text_box, &tx, &ty, &nr_xd, &nr_yd)) { - if (text_box && text_box->text && !text_box->object) { - int w = (text_box->padding[LEFT] + - text_box->width + - text_box->padding[RIGHT]); - int h = (text_box->padding[TOP] + - text_box->height + - text_box->padding[BOTTOM]); - int x1, y1; + box_coords(box, &r->x0, &r->y0); - y1 = ty + h; - x1 = tx + w; + width = box->padding[LEFT] + box->width + box->padding[RIGHT]; + height = box->padding[TOP] + box->height + box->padding[BOTTOM]; - /* ensure point lies within the text box */ - if (x < tx) x = tx; - if (y < ty) y = ty; - if (y > y1) y = y1; - if (x > x1) x = x1; + r->x1 = r->x0 + width; + r->y1 = r->y0 + height; +} + + +/* Exported function documented in html/box.h */ +struct box * +box_at_point(const nscss_len_ctx *len_ctx, + struct box *box, + const int x, const int y, + int *box_x, int *box_y) +{ + bool skip_children; + bool physically; + + assert(box); + + skip_children = false; + while ((box = box_next_xy(box, box_x, box_y, skip_children))) { + if (box_contains_point(len_ctx, box, x - *box_x, y - *box_y, + &physically)) { + *box_x -= scrollbar_get_offset(box->scroll_x); + *box_y -= scrollbar_get_offset(box->scroll_y); + + if (physically) + return box; + + skip_children = false; + } else { + skip_children = true; } } - /* return coordinates relative to box */ - *dx = x - tx; - *dy = y - ty; - - return text_box; + return NULL; } @@ -876,13 +612,15 @@ struct box *box_find_by_id(struct box *box, lwc_string *id) bool m; if (box->id != NULL && - lwc_string_isequal(id, box->id, &m) == lwc_error_ok && - m == true) + lwc_string_isequal(id, box->id, &m) == lwc_error_ok && + m == true) { return box; + } for (a = box->children; a; a = a->next) { - if ((b = box_find_by_id(a, id)) != NULL) + if ((b = box_find_by_id(a, id)) != NULL) { return b; + } } return NULL; @@ -893,9 +631,10 @@ struct box *box_find_by_id(struct box *box, lwc_string *id) bool box_visible(struct box *box) { /* visibility: hidden */ - if (box->style && css_computed_visibility(box->style) == - CSS_VISIBILITY_HIDDEN) + if (box->style && + css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN) { return false; + } return true; } @@ -907,50 +646,92 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style) unsigned int i; struct box *c, *prev; - for (i = 0; i != depth; i++) + for (i = 0; i != depth; i++) { fprintf(stream, " "); + } fprintf(stream, "%p ", box); - fprintf(stream, "x%i y%i w%i h%i ", box->x, box->y, - box->width, box->height); - if (box->max_width != UNKNOWN_MAX_WIDTH) + fprintf(stream, "x%i y%i w%i h%i ", + box->x, box->y, box->width, box->height); + if (box->max_width != UNKNOWN_MAX_WIDTH) { fprintf(stream, "min%i max%i ", box->min_width, box->max_width); + } fprintf(stream, "(%i %i %i %i) ", - box->descendant_x0, box->descendant_y0, - box->descendant_x1, box->descendant_y1); + box->descendant_x0, box->descendant_y0, + box->descendant_x1, box->descendant_y1); fprintf(stream, "m(%i %i %i %i) ", - box->margin[TOP], box->margin[LEFT], - box->margin[BOTTOM], box->margin[RIGHT]); + box->margin[TOP], box->margin[LEFT], + box->margin[BOTTOM], box->margin[RIGHT]); switch (box->type) { - case BOX_BLOCK: fprintf(stream, "BLOCK "); break; - case BOX_INLINE_CONTAINER: fprintf(stream, "INLINE_CONTAINER "); break; - case BOX_INLINE: fprintf(stream, "INLINE "); break; - case BOX_INLINE_END: fprintf(stream, "INLINE_END "); break; - case BOX_INLINE_BLOCK: fprintf(stream, "INLINE_BLOCK "); break; - case BOX_TABLE: fprintf(stream, "TABLE [columns %i] ", - box->columns); break; - case BOX_TABLE_ROW: fprintf(stream, "TABLE_ROW "); break; - case BOX_TABLE_CELL: fprintf(stream, "TABLE_CELL [columns %i, " - "start %i, rows %i] ", box->columns, - box->start_column, box->rows); break; - case BOX_TABLE_ROW_GROUP: fprintf(stream, "TABLE_ROW_GROUP "); break; - case BOX_FLOAT_LEFT: fprintf(stream, "FLOAT_LEFT "); break; - case BOX_FLOAT_RIGHT: fprintf(stream, "FLOAT_RIGHT "); break; - case BOX_BR: fprintf(stream, "BR "); break; - case BOX_TEXT: fprintf(stream, "TEXT "); break; - default: fprintf(stream, "Unknown box type "); + case BOX_BLOCK: + fprintf(stream, "BLOCK "); + break; + + case BOX_INLINE_CONTAINER: + fprintf(stream, "INLINE_CONTAINER "); + break; + + case BOX_INLINE: + fprintf(stream, "INLINE "); + break; + + case BOX_INLINE_END: + fprintf(stream, "INLINE_END "); + break; + + case BOX_INLINE_BLOCK: + fprintf(stream, "INLINE_BLOCK "); + break; + + case BOX_TABLE: + fprintf(stream, "TABLE [columns %i] ", box->columns); + break; + + case BOX_TABLE_ROW: + fprintf(stream, "TABLE_ROW "); + break; + + case BOX_TABLE_CELL: + fprintf(stream, "TABLE_CELL [columns %i, start %i, rows %i] ", + box->columns, + box->start_column, + box->rows); + break; + + case BOX_TABLE_ROW_GROUP: + fprintf(stream, "TABLE_ROW_GROUP "); + break; + + case BOX_FLOAT_LEFT: + fprintf(stream, "FLOAT_LEFT "); + break; + + case BOX_FLOAT_RIGHT: + fprintf(stream, "FLOAT_RIGHT "); + break; + + case BOX_BR: + fprintf(stream, "BR "); + break; + + case BOX_TEXT: + fprintf(stream, "TEXT "); + break; + + default: + fprintf(stream, "Unknown box type "); } if (box->text) fprintf(stream, "%li '%.*s' ", (unsigned long) box->byte_offset, - (int) box->length, box->text); + (int) box->length, box->text); if (box->space) fprintf(stream, "space "); if (box->object) { fprintf(stream, "(object '%s') ", - nsurl_access(hlcache_handle_get_url(box->object))); + nsurl_access(hlcache_handle_get_url(box->object))); } if (box->iframe) { fprintf(stream, "(iframe) "); @@ -977,16 +758,23 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style) fprintf(stream, " float_container %p", box->float_container); if (box->col) { fprintf(stream, " (columns"); - for (i = 0; i != box->columns; i++) + for (i = 0; i != box->columns; i++) { fprintf(stream, " (%s %s %i %i %i)", - ((const char *[]) {"UNKNOWN", "FIXED", - "AUTO", "PERCENT", "RELATIVE"}) - [box->col[i].type], - ((const char *[]) {"normal", + ((const char *[]) { + "UNKNOWN", + "FIXED", + "AUTO", + "PERCENT", + "RELATIVE" + }) + [box->col[i].type], + ((const char *[]) { + "normal", "positioned"}) - [box->col[i].positioned], - box->col[i].width, - box->col[i].min, box->col[i].max); + [box->col[i].positioned], + box->col[i].width, + box->col[i].min, box->col[i].max); + } fprintf(stream, ")"); } if (box->node != NULL) { @@ -1009,133 +797,89 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style) ; if (box->last != c) fprintf(stream, "warning: box->last %p (should be %p) " - "(box %p)\n", box->last, c, box); + "(box %p)\n", box->last, c, box); for (prev = 0, c = box->children; c; prev = c, c = c->next) { if (c->parent != box) fprintf(stream, "warning: box->parent %p (should be " - "%p) (box on next line)\n", - c->parent, box); + "%p) (box on next line)\n", + c->parent, box); if (c->prev != prev) fprintf(stream, "warning: box->prev %p (should be " - "%p) (box on next line)\n", - c->prev, prev); + "%p) (box on next line)\n", + c->prev, prev); box_dump(stream, c, depth + 1, style); } } /* exported interface documented in html/box.h */ -nserror -box_handle_scrollbars(struct content *c, - struct box *box, - bool bottom, - bool right) +bool box_vscrollbar_present(const struct box * const box) { - struct html_scrollbar_data *data; - int visible_width, visible_height; - int full_width, full_height; - nserror res; - - if (!bottom && box->scroll_x != NULL) { - data = scrollbar_get_data(box->scroll_x); - scrollbar_destroy(box->scroll_x); - free(data); - box->scroll_x = NULL; - } - - if (!right && box->scroll_y != NULL) { - data = scrollbar_get_data(box->scroll_y); - scrollbar_destroy(box->scroll_y); - free(data); - box->scroll_y = NULL; - } - - if (!bottom && !right) { - return NSERROR_OK; - } - - visible_width = box->width + box->padding[RIGHT] + box->padding[LEFT]; - visible_height = box->height + box->padding[TOP] + box->padding[BOTTOM]; - - full_width = ((box->descendant_x1 - box->border[RIGHT].width) > - visible_width) ? - box->descendant_x1 + box->padding[RIGHT] : - visible_width; - full_height = ((box->descendant_y1 - box->border[BOTTOM].width) > - visible_height) ? - box->descendant_y1 + box->padding[BOTTOM] : - visible_height; - - if (right) { - if (box->scroll_y == NULL) { - data = malloc(sizeof(struct html_scrollbar_data)); - if (data == NULL) { - return NSERROR_NOMEM; - } - data->c = c; - data->box = box; - res = scrollbar_create(false, - visible_height, - full_height, - visible_height, - data, - html_overflow_scroll_callback, - &(box->scroll_y)); - if (res != NSERROR_OK) { - return res; - } - } else { - scrollbar_set_extents(box->scroll_y, - visible_height, - visible_height, - full_height); - } - } - if (bottom) { - if (box->scroll_x == NULL) { - data = malloc(sizeof(struct html_scrollbar_data)); - if (data == NULL) { - return NSERROR_OK; - } - data->c = c; - data->box = box; - res = scrollbar_create(true, - visible_width - (right ? SCROLLBAR_WIDTH : 0), - full_width, - visible_width, - data, - html_overflow_scroll_callback, - &box->scroll_x); - if (res != NSERROR_OK) { - return res; - } - } else { - scrollbar_set_extents(box->scroll_x, - visible_width - - (right ? SCROLLBAR_WIDTH : 0), - visible_width, full_width); - } - } - - if (right && bottom) { - scrollbar_make_pair(box->scroll_x, box->scroll_y); - } - - return NSERROR_OK; + return box->padding[TOP] + + box->height + + box->padding[BOTTOM] + + box->border[BOTTOM].width < box->descendant_y1; } /* exported interface documented in html/box.h */ -bool box_vscrollbar_present(const struct box * const box) +bool box_hscrollbar_present(const struct box * const box) { - return box->padding[TOP] + box->height + box->padding[BOTTOM] + - box->border[BOTTOM].width < box->descendant_y1; + return box->padding[LEFT] + + box->width + + box->padding[RIGHT] + + box->border[RIGHT].width < box->descendant_x1; } -/* exported interface documented in html/box.h */ -bool box_hscrollbar_present(const struct box * const box) +/* Exported function documented in html/box.h */ +struct box * +box_pick_text_box(struct html_content *html, + int x, int y, + int dir, + int *dx, int *dy) { - return box->padding[LEFT] + box->width + box->padding[RIGHT] + - box->border[RIGHT].width < box->descendant_x1; + struct box *text_box = NULL; + struct box *box; + int nr_xd, nr_yd; + int bx, by; + int fx, fy; + int tx, ty; + + if (html == NULL) + return NULL; + + box = html->layout; + bx = box->margin[LEFT]; + by = box->margin[TOP]; + fx = bx; + fy = by; + + if (!box_nearest_text_box(box, bx, by, fx, fy, x, y, + dir, &text_box, &tx, &ty, &nr_xd, &nr_yd)) { + if (text_box && text_box->text && !text_box->object) { + int w = (text_box->padding[LEFT] + + text_box->width + + text_box->padding[RIGHT]); + int h = (text_box->padding[TOP] + + text_box->height + + text_box->padding[BOTTOM]); + int x1, y1; + + y1 = ty + h; + x1 = tx + w; + + /* ensure point lies within the text box */ + if (x < tx) x = tx; + if (y < ty) y = ty; + if (y > y1) y = y1; + if (x > x1) x = x1; + } + } + + /* return coordinates relative to box */ + *dx = x - tx; + *dy = y - ty; + + return text_box; } diff --git a/content/handlers/html/box_inspect.h b/content/handlers/html/box_inspect.h new file mode 100644 index 000000000..d50b84813 --- /dev/null +++ b/content/handlers/html/box_inspect.h @@ -0,0 +1,143 @@ +/* + * Copyright 2020 Vincent Sanders <vince@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 + * HTML Box tree inspection interface. + */ + +#ifndef NETSURF_HTML_BOX_INSPECT_H +#define NETSURF_HTML_BOX_INSPECT_H + +/** + * Find the absolute coordinates of a box. + * + * \param box the box to calculate coordinates of + * \param x updated to x coordinate + * \param y updated to y coordinate + */ +void box_coords(struct box *box, int *x, int *y); + + +/** + * Find the bounds of a box. + * + * \param box the box to calculate bounds of + * \param r receives bounds + */ +void box_bounds(struct box *box, struct rect *r); + + +/** + * Find the boxes at a point. + * + * \param len_ctx CSS length conversion context for document. + * \param box box to search children of + * \param x point to find, in global document coordinates + * \param y point to find, in global document coordinates + * \param box_x position of box, in global document coordinates, updated + * to position of returned box, if any + * \param box_y position of box, in global document coordinates, updated + * to position of returned box, if any + * \return box at given point, or 0 if none found + * + * To find all the boxes in the hierarchy at a certain point, use code like + * this: + * \code + * struct box *box = top_of_document_to_search; + * int box_x = 0, box_y = 0; + * + * while ((box = box_at_point(len_ctx, box, x, y, &box_x, &box_y))) { + * // process box + * } + * \endcode + */ +struct box *box_at_point(const nscss_len_ctx *len_ctx, struct box *box, const int x, const int y, int *box_x, int *box_y); + + +/** + * Find a box based upon its id attribute. + * + * \param box box tree to search + * \param id id to look for + * \return the box or 0 if not found + */ +struct box *box_find_by_id(struct box *box, lwc_string *id); + + +/** + * Determine if a box is visible when the tree is rendered. + * + * \param box box to check + * \return true iff the box is rendered + */ +bool box_visible(struct box *box); + + +/** + * Print a box tree to a file. + */ +void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style); + + +/** + * Determine if a box has a vertical scrollbar. + * + * \param box scrolling box + * \return the box has a vertical scrollbar + */ +bool box_vscrollbar_present(const struct box *box); + + +/** + * Determine if a box has a horizontal scrollbar. + * + * \param box scrolling box + * \return the box has a horizontal scrollbar + */ +bool box_hscrollbar_present(const struct box *box); + + +/** + * Peform pick text on browser window contents to locate the box under + * the mouse pointer, or nearest in the given direction if the pointer is + * not over a text box. + * + * \param html an HTML content + * \param x coordinate of mouse + * \param y coordinate of mouse + * \param dir direction to search (-1 = above-left, +1 = below-right) + * \param dx receives x ordinate of mouse relative to text box + * \param dy receives y ordinate of mouse relative to text box + */ +struct box *box_pick_text_box(struct html_content *html, int x, int y, int dir, int *dx, int *dy); + + +/** + * Check if layout box is a first child. + * + * \param[in] b Box to check. + * \return true iff box is first child. + */ +static inline bool box_is_first_child(struct box *b) +{ + return (b->parent == NULL || b == b->parent->children); +} + + +#endif diff --git a/content/handlers/html/box_manipulate.c b/content/handlers/html/box_manipulate.c new file mode 100644 index 000000000..0bc1c9fea --- /dev/null +++ b/content/handlers/html/box_manipulate.c @@ -0,0 +1,350 @@ +/* + * Copyright 2005-2007 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 2008 Michael Drake <tlsa@netsurf-browser.org> + * Copyright 2020 Vincent Sanders <vince@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 box tree manipulation. + */ + + +#include "utils/errors.h" +#include "utils/talloc.h" +#include "netsurf/types.h" +#include "netsurf/mouse.h" +#include "desktop/scrollbar.h" + +#include "html/box.h" +#include "html/box_manipulate.h" +#include "html/form_internal.h" +#include "html/html_internal.h" +#include "html/interaction.h" + + +/** + * Destructor for box nodes which own styles + * + * \param b The box being destroyed. + * \return 0 to allow talloc to continue destroying the tree. + */ +static int box_talloc_destructor(struct box *b) +{ + struct html_scrollbar_data *data; + + if ((b->flags & STYLE_OWNED) && b->style != NULL) { + css_computed_style_destroy(b->style); + b->style = NULL; + } + + if (b->styles != NULL) { + css_select_results_destroy(b->styles); + b->styles = NULL; + } + + if (b->href != NULL) + nsurl_unref(b->href); + + if (b->id != NULL) { + lwc_string_unref(b->id); + } + + if (b->node != NULL) { + dom_node_unref(b->node); + } + + if (b->scroll_x != NULL) { + data = scrollbar_get_data(b->scroll_x); + scrollbar_destroy(b->scroll_x); + free(data); + } + + if (b->scroll_y != NULL) { + data = scrollbar_get_data(b->scroll_y); + scrollbar_destroy(b->scroll_y); + free(data); + } + + return 0; +} + + +/* Exported function documented in html/box.h */ +struct box * +box_create(css_select_results *styles, + css_computed_style *style, + bool style_owned, + nsurl *href, + const char *target, + const char *title, + lwc_string *id, + void *context) +{ + unsigned int i; + struct box *box; + + box = talloc(context, struct box); + if (!box) { + return 0; + } + + talloc_set_destructor(box, box_talloc_destructor); + + box->type = BOX_INLINE; + box->flags = 0; + box->flags = style_owned ? (box->flags | STYLE_OWNED) : box->flags; + box->styles = styles; + box->style = style; + box->x = box->y = 0; + box->width = UNKNOWN_WIDTH; + box->height = 0; + box->descendant_x0 = box->descendant_y0 = 0; + box->descendant_x1 = box->descendant_y1 = 0; + for (i = 0; i != 4; i++) + box->margin[i] = box->padding[i] = box->border[i].width = 0; + box->scroll_x = box->scroll_y = NULL; + box->min_width = 0; + box->max_width = UNKNOWN_MAX_WIDTH; + box->byte_offset = 0; + box->text = NULL; + box->length = 0; + box->space = 0; + box->href = (href == NULL) ? NULL : nsurl_ref(href); + box->target = target; + box->title = title; + box->columns = 1; + box->rows = 1; + box->start_column = 0; + box->next = NULL; + box->prev = NULL; + box->children = NULL; + box->last = NULL; + box->parent = NULL; + box->inline_end = NULL; + box->float_children = NULL; + box->float_container = NULL; + box->next_float = NULL; + box->cached_place_below_level = 0; + box->list_marker = NULL; + box->col = NULL; + box->gadget = NULL; + box->usemap = NULL; + box->id = id; + box->background = NULL; + box->object = NULL; + box->object_params = NULL; + box->iframe = NULL; + box->node = NULL; + + return box; +} + + +/* Exported function documented in html/box.h */ +void box_add_child(struct box *parent, struct box *child) +{ + assert(parent); + assert(child); + + if (parent->children != 0) { /* has children already */ + parent->last->next = child; + child->prev = parent->last; + } else { /* this is the first child */ + parent->children = child; + child->prev = 0; + } + + parent->last = child; + child->parent = parent; +} + + +/* Exported function documented in html/box.h */ +void box_insert_sibling(struct box *box, struct box *new_box) +{ + new_box->parent = box->parent; + new_box->prev = box; + new_box->next = box->next; + box->next = new_box; + if (new_box->next) + new_box->next->prev = new_box; + else if (new_box->parent) + new_box->parent->last = new_box; +} + + +/* Exported function documented in html/box.h */ +void box_unlink_and_free(struct box *box) +{ + struct box *parent = box->parent; + struct box *next = box->next; + struct box *prev = box->prev; + + if (parent) { + if (parent->children == box) + parent->children = next; + if (parent->last == box) + parent->last = next ? next : prev; + } + + if (prev) + prev->next = next; + if (next) + next->prev = prev; + + box_free(box); +} + + +/* Exported function documented in html/box.h */ +void box_free(struct box *box) +{ + struct box *child, *next; + + /* free children first */ + for (child = box->children; child; child = next) { + next = child->next; + box_free(child); + } + + /* last this box */ + box_free_box(box); +} + + +/* Exported function documented in html/box.h */ +void box_free_box(struct box *box) +{ + if (!(box->flags & CLONE)) { + if (box->gadget) + form_free_control(box->gadget); + if (box->scroll_x != NULL) + scrollbar_destroy(box->scroll_x); + if (box->scroll_y != NULL) + scrollbar_destroy(box->scroll_y); + if (box->styles != NULL) + css_select_results_destroy(box->styles); + } + + talloc_free(box); +} + + +/* exported interface documented in html/box.h */ +nserror +box_handle_scrollbars(struct content *c, + struct box *box, + bool bottom, + bool right) +{ + struct html_scrollbar_data *data; + int visible_width, visible_height; + int full_width, full_height; + nserror res; + + if (!bottom && box->scroll_x != NULL) { + data = scrollbar_get_data(box->scroll_x); + scrollbar_destroy(box->scroll_x); + free(data); + box->scroll_x = NULL; + } + + if (!right && box->scroll_y != NULL) { + data = scrollbar_get_data(box->scroll_y); + scrollbar_destroy(box->scroll_y); + free(data); + box->scroll_y = NULL; + } + + if (!bottom && !right) { + return NSERROR_OK; + } + + visible_width = box->width + box->padding[RIGHT] + box->padding[LEFT]; + visible_height = box->height + box->padding[TOP] + box->padding[BOTTOM]; + + full_width = ((box->descendant_x1 - box->border[RIGHT].width) > + visible_width) ? + box->descendant_x1 + box->padding[RIGHT] : + visible_width; + full_height = ((box->descendant_y1 - box->border[BOTTOM].width) > + visible_height) ? + box->descendant_y1 + box->padding[BOTTOM] : + visible_height; + + if (right) { + if (box->scroll_y == NULL) { + data = malloc(sizeof(struct html_scrollbar_data)); + if (data == NULL) { + return NSERROR_NOMEM; + } + data->c = c; + data->box = box; + res = scrollbar_create(false, + visible_height, + full_height, + visible_height, + data, + html_overflow_scroll_callback, + &(box->scroll_y)); + if (res != NSERROR_OK) { + return res; + } + } else { + scrollbar_set_extents(box->scroll_y, + visible_height, + visible_height, + full_height); + } + } + if (bottom) { + if (box->scroll_x == NULL) { + data = malloc(sizeof(struct html_scrollbar_data)); + if (data == NULL) { + return NSERROR_OK; + } + data->c = c; + data->box = box; + res = scrollbar_create(true, + visible_width - (right ? SCROLLBAR_WIDTH : 0), + full_width, + visible_width, + data, + html_overflow_scroll_callback, + &box->scroll_x); + if (res != NSERROR_OK) { + return res; + } + } else { + scrollbar_set_extents(box->scroll_x, + visible_width - + (right ? SCROLLBAR_WIDTH : 0), + visible_width, full_width); + } + } + + if (right && bottom) { + scrollbar_make_pair(box->scroll_x, box->scroll_y); + } + + return NSERROR_OK; +} + + diff --git a/content/handlers/html/box_manipulate.h b/content/handlers/html/box_manipulate.h new file mode 100644 index 000000000..43a66a7d9 --- /dev/null +++ b/content/handlers/html/box_manipulate.h @@ -0,0 +1,106 @@ +/* + * Copyright 2005 James Bursa <bursa@users.sourceforge.net> + * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> + * + * 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 + * Box tree manipulation interface. + */ + +#ifndef NETSURF_HTML_BOX_MANIPULATE_H +#define NETSURF_HTML_BOX_MANIPULATE_H + + +/** + * Create a box tree node. + * + * \param styles selection results for the box, or NULL + * \param style computed style for the box (not copied), or 0 + * \param style_owned whether style is owned by this box + * \param href href for the box (copied), or 0 + * \param target target for the box (not copied), or 0 + * \param title title for the box (not copied), or 0 + * \param id id for the box (not copied), or 0 + * \param context context for allocations + * \return allocated and initialised box, or 0 on memory exhaustion + * + * styles is always owned by the box, if it is set. + * style is only owned by the box in the case of implied boxes. + */ +struct box * box_create(css_select_results *styles, css_computed_style *style, bool style_owned, struct nsurl *href, const char *target, const char *title, lwc_string *id, void *context); + + +/** + * Add a child to a box tree node. + * + * \param parent box giving birth + * \param child box to link as last child of parent + */ +void box_add_child(struct box *parent, struct box *child); + + +/** + * Insert a new box as a sibling to a box in a tree. + * + * \param box box already in tree + * \param new_box box to link into tree as next sibling + */ +void box_insert_sibling(struct box *box, struct box *new_box); + + +/** + * Unlink a box from the box tree and then free it recursively. + * + * \param box box to unlink and free recursively. + */ +void box_unlink_and_free(struct box *box); + + +/** + * Free a box tree recursively. + * + * \param box box to free recursively + * + * The box and all its children is freed. + */ +void box_free(struct box *box); + + +/** + * Free the data in a single box structure. + * + * \param box box to free + */ +void box_free_box(struct box *box); + + +/** + * Applies the given scroll setup to a box. This includes scroll + * creation/deletion as well as scroll dimension updates. + * + * \param c content in which the box is located + * \param box the box to handle the scrolls for + * \param bottom whether the horizontal scrollbar should be present + * \param right whether the vertical scrollbar should be present + * \return true on success false otherwise + */ +nserror box_handle_scrollbars(struct content *c, struct box *box, + bool bottom, bool right); + + +#endif diff --git a/content/handlers/html/box_normalise.c b/content/handlers/html/box_normalise.c index 03fe731b2..fbdb6cd05 100644 --- a/content/handlers/html/box_normalise.c +++ b/content/handlers/html/box_normalise.c @@ -33,6 +33,7 @@ #include "css/select.h" #include "html/box.h" +#include "html/box_manipulate.h" #include "html/box_normalise.h" #include "html/html_internal.h" #include "html/table.h" diff --git a/content/handlers/html/box_normalise.h b/content/handlers/html/box_normalise.h index 60f259189..591feab8d 100644 --- a/content/handlers/html/box_normalise.h +++ b/content/handlers/html/box_normalise.h @@ -19,6 +19,21 @@ /** * \file * HTML Box tree normalise interface. + * + * A box tree is "normalized" if the following is satisfied: + * \code + * parent permitted child nodes + * BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE + * INLINE_CONTAINER INLINE, INLINE_BLOCK, FLOAT_LEFT, FLOAT_RIGHT, BR, TEXT, + * INLINE_END + * INLINE none + * TABLE at least 1 TABLE_ROW_GROUP + * TABLE_ROW_GROUP at least 1 TABLE_ROW + * TABLE_ROW at least 1 TABLE_CELL + * TABLE_CELL BLOCK, INLINE_CONTAINER, TABLE (same as BLOCK) + * FLOAT_(LEFT|RIGHT) exactly 1 BLOCK or TABLE + * \endcode + * */ #ifndef NETSURF_HTML_BOX_NORMALISE_H diff --git a/content/handlers/html/box_special.c b/content/handlers/html/box_special.c new file mode 100644 index 000000000..38031ceb1 --- /dev/null +++ b/content/handlers/html/box_special.c @@ -0,0 +1,1916 @@ +/* + * 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> + * Copyright 2020 Vincent Sanders <vince@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 special element handling conversion. + */ + +#include <stdbool.h> +#include <dom/dom.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/plot_style.h" +#include "css/hints.h" +#include "desktop/frame_types.h" + +#include "html/html.h" +#include "html/html_internal.h" +#include "html/box.h" +#include "html/box_manipulate.h" +#include "html/box_construct.h" +#include "html/box_special.h" +#include "html/box_textarea.h" +#include "html/form_internal.h" + + +static const content_type image_types = CONTENT_IMAGE; + + +/** + * determine if a box is the root node + * + * \param n node to check + * \return true if node is root else false. + */ +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; +} + + +/** + * 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; +} + + +/** + * 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; +} + + +/** + * 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; +} + + +/** + * create a frameset box tree + */ +static 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); + } + + if (dom_element_has_attribute(c, corestring_dom_noresize, + &frame->no_resize) != DOM_NO_ERR) { + /* If we can't read the attribute for some reason, + * assume we didn't have it. + */ + frame->no_resize = false; + } + + 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; +} + + +/** + * Get the value of a dom node element's attribute. + * + * \param n dom 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 returning true does not imply that the attribute was found. If the + * attribute was not found, *value will be unchanged. + */ +static 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; +} + + +/** + * 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); +} + + +/** + * 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 + */ +static 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; + + if (dom_element_has_attribute(n, corestring_dom_selected, &selected) != DOM_NO_ERR) { + /* Assume not selected if we can't read the attribute presence */ + selected = false; + } + + /* 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; +} + + +/** + * \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. + * + * \{ + */ + +/** + * special element handler for Anchor [12.2]. + */ +static bool +box_a(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + 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; +} + + +/** + * Document body special element handler [7.5.1]. + */ +static bool +box_body(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + 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; +} + + +/** + * special element handler for forced line break [9.3.2]. + */ +static bool +box_br(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + box->type = BOX_BR; + return true; +} + + +/** + * special element handler for Push button [17.5]. + */ +static bool +box_button(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + 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; +} + + +/** + * Canvas element + */ +static bool +box_canvas(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + /* If scripting is not enabled display the contents of canvas */ + if (!content->enable_scripting) { + return true; + } + *convert_children = false; + + return true; +} + + +/** + * Embedded object (not in any HTML specification: + * see http://wp.netscape.com/assist/net_sites/new_html3_prop.html ) + */ +static bool +box_embed(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + 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_node_unref(attr); + dom_namednodemap_unref(attrs); + return false; + } + + if (dom_string_caseless_lwc_isequal(name, corestring_lwc_src)) { + dom_node_unref(attr); + dom_string_unref(name); + continue; + } + + err = dom_attr_get_value(attr, &value); + if (err != DOM_NO_ERR) { + dom_node_unref(attr); + dom_string_unref(name); + dom_namednodemap_unref(attrs); + return false; + } + + param = talloc(content->bctx, struct object_param); + if (param == NULL) { + dom_node_unref(attr); + 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); + dom_node_unref(attr); + + 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); +} + + +/** + * Window subdivision [16.2.1]. + */ +static bool +box_frameset(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + 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; +} + + +/** + * Inline subwindow [16.5]. + */ +static bool +box_iframe(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + 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; +} + + +/** + * Embedded image [13.2]. + */ +static bool +box_image(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + 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; +} + + +/** + * Form control [17.4]. + */ +static bool +box_input(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + struct form_control *gadget; + dom_string *type = NULL; + dom_exception err; + nsurl *url; + nserror error; + + gadget = html_forms_get_control_for_node(content->forms, n); + if (gadget == NULL) { + return false; + } + + box->gadget = gadget; + box->flags |= IS_REPLACED; + gadget->box = box; + gadget->html = content; + + /* get entry type */ + err = dom_element_get_attribute(n, corestring_dom_type, &type); + if ((err != DOM_NO_ERR) || (type == NULL)) { + /* no type so "text" is assumed */ + if (box_input_text(content, box, n) == false) { + return false; + } + *convert_children = false; + return true; + } + + if (dom_string_caseless_lwc_isequal(type, corestring_lwc_password)) { + if (box_input_text(content, box, n) == false) + goto no_memory; + + } else if (dom_string_caseless_lwc_isequal(type, corestring_lwc_file)) { + box->type = BOX_INLINE_BLOCK; + + } else if (dom_string_caseless_lwc_isequal(type, + corestring_lwc_hidden)) { + /* no box for hidden inputs */ + box->type = BOX_NONE; + + } else if ((dom_string_caseless_lwc_isequal(type, + corestring_lwc_checkbox) || + dom_string_caseless_lwc_isequal(type, + corestring_lwc_radio))) { + + } else if (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 (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 { + /* unhandled type the default is "text" */ + if (box_input_text(content, box, n) == false) + goto no_memory; + } + + dom_string_unref(type); + + *convert_children = false; + + return true; + +no_memory: + dom_string_unref(type); + + return false; +} + + +/** + * Noscript element + */ +static bool +box_noscript(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + /* If scripting is enabled, do not display the contents of noscript */ + if (content->enable_scripting) { + *convert_children = false; + } + + return true; +} + + +/** + * Generic embedded object [13.3]. + */ +static bool +box_object(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + 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_string_unref(name); + dom_node_unref(c); + break; + } + dom_string_unref(name); + + 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; +} + + +/** + * Preformatted text [9.3.4]. + */ +static bool +box_pre(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + box->flags |= PRE_STRIP; + return true; +} + + +/** + * Option selector [17.6]. + */ +static bool +box_select(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + 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); + form_free_control(gadget); + 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); + form_free_control(gadget); + 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; +} + + +/** + * Multi-line text field [17.7]. + */ +static bool box_textarea(dom_node *n, + html_content *content, + struct box *box, + bool *convert_children) +{ + /* 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; +} + + +/** + * \} + */ + + +/* exported interface documented in html/box_special.h */ +bool +convert_special_elements(dom_node *node, + html_content *content, + struct box *box, + bool *convert_children) +{ + dom_exception exc; + dom_html_element_type tag_type; + bool res; + + exc = dom_html_element_get_tag_type(node, &tag_type); + if (exc != DOM_NO_ERR) { + tag_type = DOM_HTML_ELEMENT_TYPE__UNKNOWN; + } + + switch (tag_type) { + case DOM_HTML_ELEMENT_TYPE_A: + res = box_a(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_BODY: + res = box_body(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_BR: + res = box_br(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_BUTTON: + res = box_button(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_CANVAS: + res = box_canvas(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_EMBED: + res = box_embed(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_FRAMESET: + res = box_frameset(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_IFRAME: + res = box_iframe(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_IMG: + res = box_image(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_INPUT: + res = box_input(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_NOSCRIPT: + res = box_noscript(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_OBJECT: + res = box_object(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_PRE: + res = box_pre(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_SELECT: + res = box_select(node, content, box, convert_children); + break; + + case DOM_HTML_ELEMENT_TYPE_TEXTAREA: + res = box_textarea(node, content, box, convert_children); + break; + + default: + res = true; + } + + return res; +} diff --git a/content/handlers/html/box_special.h b/content/handlers/html/box_special.h new file mode 100644 index 000000000..973ab976a --- /dev/null +++ b/content/handlers/html/box_special.h @@ -0,0 +1,35 @@ +/* + * Copyright 2020 Vincent Sanders <vince@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 + * HTML Box tree construction special element conversion interface. + */ + +#ifndef NETSURF_HTML_BOX_SPECIAL_H +#define NETSURF_HTML_BOX_SPECIAL_H + + +/** + * call an elements special conversion handler + * + * \return true if box construction should continue else false on error. + */ +bool convert_special_elements(dom_node *node, html_content *content, struct box *box, bool *convert_children); + +#endif diff --git a/content/handlers/html/box_textarea.c b/content/handlers/html/box_textarea.c index e253e168d..476773f29 100644 --- a/content/handlers/html/box_textarea.c +++ b/content/handlers/html/box_textarea.c @@ -33,6 +33,7 @@ #include "html/html_internal.h" #include "html/box.h" +#include "html/box_inspect.h" #include "html/box_textarea.h" #include "html/font.h" #include "html/form_internal.h" diff --git a/content/handlers/html/form.c b/content/handlers/html/form.c index fd0f2fa3e..86c4196ed 100644 --- a/content/handlers/html/form.c +++ b/content/handlers/html/form.c @@ -53,6 +53,7 @@ #include "desktop/gui_internal.h" #include "html/box.h" +#include "html/box_inspect.h" #include "html/font.h" #include "html/form_internal.h" #include "html/html.h" diff --git a/content/handlers/html/html.c b/content/handlers/html/html.c index 9b4785839..01244cc35 100644 --- a/content/handlers/html/html.c +++ b/content/handlers/html/html.c @@ -61,6 +61,7 @@ #include "html/interaction.h" #include "html/box.h" #include "html/box_construct.h" +#include "html/box_inspect.h" #include "html/form_internal.h" #include "html/imagemap.h" #include "html/layout.h" diff --git a/content/handlers/html/html_object.c b/content/handlers/html/html_object.c index 3a60c47f1..587c48756 100644 --- a/content/handlers/html/html_object.c +++ b/content/handlers/html/html_object.c @@ -42,6 +42,7 @@ #include "html/html.h" #include "html/box.h" +#include "html/box_inspect.h" #include "html/html_internal.h" /* break reference loop */ diff --git a/content/handlers/html/html_redraw.c b/content/handlers/html/html_redraw.c index a60f44e28..ee6fab275 100644 --- a/content/handlers/html/html_redraw.c +++ b/content/handlers/html/html_redraw.c @@ -54,6 +54,8 @@ #include "desktop/gui_internal.h" #include "html/box.h" +#include "html/box_inspect.h" +#include "html/box_manipulate.h" #include "html/font.h" #include "html/form_internal.h" #include "html/html_internal.h" diff --git a/content/handlers/html/interaction.c b/content/handlers/html/interaction.c index 7c4d54f2c..e4ef2f123 100644 --- a/content/handlers/html/interaction.c +++ b/content/handlers/html/interaction.c @@ -50,6 +50,7 @@ #include "html/box.h" #include "html/box_textarea.h" +#include "html/box_inspect.h" #include "html/font.h" #include "html/form_internal.h" #include "html/html_internal.h" diff --git a/content/handlers/html/layout.c b/content/handlers/html/layout.c index 611c77819..128b12c0b 100644 --- a/content/handlers/html/layout.c +++ b/content/handlers/html/layout.c @@ -60,6 +60,7 @@ #include "html/html_save.h" #include "html/html_internal.h" #include "html/box.h" +#include "html/box_inspect.h" #include "html/font.h" #include "html/form_internal.h" #include "html/layout.h" diff --git a/content/handlers/html/search.c b/content/handlers/html/search.c index 981c80b11..864fac6dd 100644 --- a/content/handlers/html/search.c +++ b/content/handlers/html/search.c @@ -40,6 +40,7 @@ #include "text/textplain.h" #include "html/box.h" +#include "html/box_inspect.h" #include "html/html.h" #include "html/html_internal.h" #include "html/search.h" diff --git a/desktop/frames.c b/desktop/frames.c index 1ed114540..a03f86c8f 100644 --- a/desktop/frames.c +++ b/desktop/frames.c @@ -35,6 +35,7 @@ #include "content/hlcache.h" #include "html/html.h" #include "html/box.h" +#include "html/box_inspect.h" #include "desktop/browser_private.h" #include "desktop/frames.h" diff --git a/desktop/scrollbar.h b/desktop/scrollbar.h index fa5e167f2..796520724 100644 --- a/desktop/scrollbar.h +++ b/desktop/scrollbar.h @@ -38,6 +38,7 @@ #define SCROLL_BOTTOM INT_MAX struct scrollbar; +struct redraw_context; /** * scrollbar message types diff --git a/desktop/selection.c b/desktop/selection.c index c8edda7b1..6c8f5d798 100644 --- a/desktop/selection.c +++ b/desktop/selection.c @@ -33,6 +33,7 @@ #include "utils/utils.h" #include "netsurf/form.h" #include "html/box.h" +#include "html/box_inspect.h" #include "html/html_internal.h" #include "html/font.h" #include "text/textplain.h" |