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 * Copyright 2003 Phil Mellor + * Copyright 2020 Vincent Sanders * * 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 - *

Example Heading

- *

Example paragraph with emphasised text etc.

\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 #include -#include #include #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 -#include -#include -#include -#include -#include +#include -#include "utils/config.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/box.h" -#include "html/box_construct.h" -#include "html/box_normalise.h" -#include "html/box_textarea.h" -#include "html/form_internal.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/form_internal.h" /** * Context for box tree construction @@ -158,1752 +146,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|" 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 - * , 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 * @@ -2244,93 +486,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. * 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 + *

Example Heading

+ *

Example paragraph with emphasised text etc.

\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 similarity index 56% rename from content/handlers/html/box.c rename to 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 - * Copyright 2003 Phil Mellor - * Copyright 2005 John M Bell * Copyright 2008 Michael Drake + * Copyright 2020 Vincent Sanders * * 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 -#include #include -#include #include -#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; } @@ -420,26 +182,13 @@ 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; @@ -816,6 +540,298 @@ static bool box_nearest_text_box(struct box *box, int bx, int by, } +/* 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; +} + + +/* 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; +} + + +/* Exported function documented in html/box.h */ +struct box *box_find_by_id(struct box *box, lwc_string *id) +{ + struct box *a, *b; + bool m; + + if (box->id != NULL && + 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) { + return b; + } + } + + return NULL; +} + + +/* Exported function documented in html/box.h */ +bool box_visible(struct box *box) +{ + /* visibility: hidden */ + if (box->style && + css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN) { + return false; + } + + return true; +} + + +/* Exported function documented in html/box.h */ +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++) { + 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, "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); + + fprintf(stream, "m(%i %i %i %i) ", + 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 "); + } + + if (box->text) + fprintf(stream, "%li '%.*s' ", (unsigned long) box->byte_offset, + (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))); + } + if (box->iframe) { + fprintf(stream, "(iframe) "); + } + if (box->gadget) + fprintf(stream, "(gadget) "); + if (style && box->style) + nscss_dump_computed_style(stream, box->style); + if (box->href) + fprintf(stream, " -> '%s'", nsurl_access(box->href)); + if (box->target) + fprintf(stream, " |%s|", box->target); + if (box->title) + fprintf(stream, " [%s]", box->title); + if (box->id) + fprintf(stream, " ID:%s", lwc_string_data(box->id)); + if (box->type == BOX_INLINE || box->type == BOX_INLINE_END) + fprintf(stream, " inline_end %p", box->inline_end); + if (box->float_children) + fprintf(stream, " float_children %p", box->float_children); + if (box->next_float) + fprintf(stream, " next_float %p", box->next_float); + if (box->float_container) + fprintf(stream, " float_container %p", box->float_container); + if (box->col) { + fprintf(stream, " (columns"); + 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", + "positioned"}) + [box->col[i].positioned], + box->col[i].width, + box->col[i].min, box->col[i].max); + } + fprintf(stream, ")"); + } + if (box->node != NULL) { + dom_string *name; + if (dom_node_get_node_name(box->node, &name) == DOM_NO_ERR) { + fprintf(stream, " <%s>", dom_string_data(name)); + dom_string_unref(name); + } + } + fprintf(stream, "\n"); + + if (box->list_marker) { + for (i = 0; i != depth; i++) + fprintf(stream, " "); + fprintf(stream, "list_marker:\n"); + box_dump(stream, box->list_marker, depth + 1, style); + } + + for (c = box->children; c && c->next; c = c->next) + ; + if (box->last != c) + fprintf(stream, "warning: box->last %p (should be %p) " + "(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); + if (c->prev != prev) + fprintf(stream, "warning: box->prev %p (should be " + "%p) (box on next line)\n", + c->prev, prev); + box_dump(stream, c, depth + 1, style); + } +} + + +/* exported interface documented in html/box.h */ +bool box_vscrollbar_present(const struct box * const box) +{ + 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_hscrollbar_present(const struct box * const box) +{ + return box->padding[LEFT] + + box->width + + box->padding[RIGHT] + + box->border[RIGHT].width < box->descendant_x1; +} + + /* Exported function documented in html/box.h */ struct box * box_pick_text_box(struct html_content *html, @@ -840,14 +856,14 @@ box_pick_text_box(struct html_content *html, fy = by; if (!box_nearest_text_box(box, bx, by, fx, fy, x, y, - dir, &text_box, &tx, &ty, &nr_xd, &nr_yd)) { + 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]); + text_box->width + + text_box->padding[RIGHT]); int h = (text_box->padding[TOP] + - text_box->height + - text_box->padding[BOTTOM]); + text_box->height + + text_box->padding[BOTTOM]); int x1, y1; y1 = ty + h; @@ -867,275 +883,3 @@ box_pick_text_box(struct html_content *html, return text_box; } - - -/* Exported function documented in html/box.h */ -struct box *box_find_by_id(struct box *box, lwc_string *id) -{ - struct box *a, *b; - bool m; - - if (box->id != NULL && - 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) - return b; - } - - return NULL; -} - - -/* Exported function documented in html/box.h */ -bool box_visible(struct box *box) -{ - /* visibility: hidden */ - if (box->style && css_computed_visibility(box->style) == - CSS_VISIBILITY_HIDDEN) - return false; - - return true; -} - - -/* Exported function documented in html/box.h */ -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++) - 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, "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); - - fprintf(stream, "m(%i %i %i %i) ", - 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 "); - } - - if (box->text) - fprintf(stream, "%li '%.*s' ", (unsigned long) box->byte_offset, - (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))); - } - if (box->iframe) { - fprintf(stream, "(iframe) "); - } - if (box->gadget) - fprintf(stream, "(gadget) "); - if (style && box->style) - nscss_dump_computed_style(stream, box->style); - if (box->href) - fprintf(stream, " -> '%s'", nsurl_access(box->href)); - if (box->target) - fprintf(stream, " |%s|", box->target); - if (box->title) - fprintf(stream, " [%s]", box->title); - if (box->id) - fprintf(stream, " ID:%s", lwc_string_data(box->id)); - if (box->type == BOX_INLINE || box->type == BOX_INLINE_END) - fprintf(stream, " inline_end %p", box->inline_end); - if (box->float_children) - fprintf(stream, " float_children %p", box->float_children); - if (box->next_float) - fprintf(stream, " next_float %p", box->next_float); - if (box->float_container) - fprintf(stream, " float_container %p", box->float_container); - if (box->col) { - fprintf(stream, " (columns"); - 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", - "positioned"}) - [box->col[i].positioned], - box->col[i].width, - box->col[i].min, box->col[i].max); - fprintf(stream, ")"); - } - if (box->node != NULL) { - dom_string *name; - if (dom_node_get_node_name(box->node, &name) == DOM_NO_ERR) { - fprintf(stream, " <%s>", dom_string_data(name)); - dom_string_unref(name); - } - } - fprintf(stream, "\n"); - - if (box->list_marker) { - for (i = 0; i != depth; i++) - fprintf(stream, " "); - fprintf(stream, "list_marker:\n"); - box_dump(stream, box->list_marker, depth + 1, style); - } - - for (c = box->children; c && c->next; c = c->next) - ; - if (box->last != c) - fprintf(stream, "warning: box->last %p (should be %p) " - "(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); - if (c->prev != prev) - fprintf(stream, "warning: box->prev %p (should be " - "%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) -{ - 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; -} - - -/* exported interface documented in html/box.h */ -bool box_vscrollbar_present(const struct box * const box) -{ - 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_hscrollbar_present(const struct box * const box) -{ - return box->padding[LEFT] + box->width + box->padding[RIGHT] + - box->border[RIGHT].width < box->descendant_x1; -} 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 + * + * 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 . + */ + +/** + * \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 + * Copyright 2003 Phil Mellor + * Copyright 2005 John M Bell + * Copyright 2008 Michael Drake + * Copyright 2020 Vincent Sanders + * + * 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 . + */ + +/** + * \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 + * Copyright 2003 Phil Mellor + * + * 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 . + */ + +/** + * \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 + * Copyright 2003 Phil Mellor + * Copyright 2005 John M Bell + * Copyright 2006 Richard Wilson + * Copyright 2008 Michael Drake + * Copyright 2020 Vincent Sanders + * + * 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 . + */ + +/** + * \file + * Implementation of special element handling conversion. + */ + +#include +#include + +#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|" 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 + * , 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 + * + * 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 . + */ + +/** + * \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"