From c0ef8ce645d6077831877b5a8499b89c18df7bf9 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Tue, 28 Apr 2020 23:30:20 +0100 Subject: clean up html box, no functionality change just cosmetic split up the html box headers tidy up the documentation comments avoid forward declarations in normalisation implementation --- content/handlers/html/box.c | 199 ++------ content/handlers/html/box.h | 613 +++++++++++++++++------ content/handlers/html/box_construct.c | 62 +-- content/handlers/html/box_construct.h | 65 +++ content/handlers/html/box_normalise.c | 904 ++++++++++++++++------------------ content/handlers/html/box_normalise.h | 50 ++ content/handlers/html/html.c | 3 +- content/handlers/html/imagemap.c | 1 + 8 files changed, 1055 insertions(+), 842 deletions(-) create mode 100644 content/handlers/html/box_construct.h create mode 100644 content/handlers/html/box_normalise.h (limited to 'content/handlers/html') diff --git a/content/handlers/html/box.c b/content/handlers/html/box.c index 94c74877f..0ad10619b 100644 --- a/content/handlers/html/box.c +++ b/content/handlers/html/box.c @@ -97,26 +97,17 @@ static int box_talloc_destructor(struct box *b) return 0; } -/** - * 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, nsurl *href, const char *target, - const char *title, lwc_string *id, void *context) +/* 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; @@ -177,13 +168,8 @@ struct box * box_create(css_select_results *styles, css_computed_style *style, return box; } -/** - * Add a child to a box tree node. - * - * \param parent box giving birth - * \param child box to link as last child of parent - */ +/* Exported function documented in html/box.h */ void box_add_child(struct box *parent, struct box *child) { assert(parent); @@ -202,13 +188,7 @@ 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 - */ - +/* Exported function documented in html/box.h */ void box_insert_sibling(struct box *box, struct box *new_box) { new_box->parent = box->parent; @@ -222,12 +202,7 @@ 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. - */ - +/* Exported function documented in html/box.h */ void box_unlink_and_free(struct box *box) { struct box *parent = box->parent; @@ -250,14 +225,7 @@ 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. - */ - +/* Exported function documented in html/box.h */ void box_free(struct box *box) { struct box *child, *next; @@ -273,12 +241,7 @@ void box_free(struct box *box) } -/** - * Free the data in a single box structure. - * - * \param box box to free - */ - +/* Exported function documented in html/box.h */ void box_free_box(struct box *box) { if (!(box->flags & CLONE)) { @@ -296,14 +259,7 @@ 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 - */ - +/* Exported function documented in html/box.h */ void box_coords(struct box *box, int *x, int *y) { *x = box->x; @@ -321,13 +277,7 @@ 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 - */ - +/* Exported function documented in html/box.h */ void box_bounds(struct box *box, struct rect *r) { int width, height; @@ -359,7 +309,6 @@ 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, @@ -471,7 +420,9 @@ static bool box_contains_point( } -/** Direction to move in a box-tree walk */ +/** + * Direction to move in a box-tree walk + */ enum box_walk_dir { BOX_WALK_CHILDREN, BOX_WALK_PARENT, @@ -492,8 +443,8 @@ enum box_walk_dir { * * If no box can be found in given direction, NULL is returned. */ -static inline struct box *box_move_xy(struct box *b, enum box_walk_dir dir, - int *x, int *y) +static inline struct box * +box_move_xy(struct box *b, enum box_walk_dir dir, int *x, int *y) { struct box *rb = NULL; @@ -665,35 +616,12 @@ skip_children: } - -/** - * 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) +/* 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; @@ -742,10 +670,14 @@ struct box *box_at_point(const nscss_len_ctx *len_ctx, * updated if box is nearer than existing nearest * \return true if mouse point is inside box */ - -static bool box_nearer_text_box(struct box *box, int bx, int by, - int x, int y, int dir, struct box **nearest, int *tx, int *ty, - int *nr_xd, int *nr_yd) +static bool +box_nearer_text_box(struct box *box, + int bx, int by, + int x, int y, + int dir, + struct box **nearest, + int *tx, int *ty, + int *nr_xd, int *nr_yd) { int w = box->padding[LEFT] + box->width + box->padding[RIGHT]; int h = box->padding[TOP] + box->height + box->padding[BOTTOM]; @@ -814,7 +746,6 @@ static bool box_nearer_text_box(struct box *box, int bx, int by, * 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) @@ -885,21 +816,12 @@ static bool box_nearest_text_box(struct box *box, int bx, int by, } -/** - * 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) +/* Exported function documented in html/box.h */ +struct box * +box_pick_text_box(struct html_content *html, + int x, int y, + int dir, + int *dx, int *dy) { struct box *text_box = NULL; struct box *box; @@ -947,14 +869,7 @@ struct box *box_pick_text_box(struct html_content *html, } -/** - * 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 - */ - +/* Exported function documented in html/box.h */ struct box *box_find_by_id(struct box *box, lwc_string *id) { struct box *a, *b; @@ -974,13 +889,7 @@ 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 - */ - +/* Exported function documented in html/box.h */ bool box_visible(struct box *box) { /* visibility: hidden */ @@ -992,10 +901,7 @@ bool box_visible(struct box *box) } -/** - * Print a box tree to a file. - */ - +/* Exported function documented in html/box.h */ void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style) { unsigned int i; @@ -1218,13 +1124,8 @@ box_handle_scrollbars(struct content *c, return NSERROR_OK; } -/** - * Determine if a box has a vertical scrollbar. - * - * \param box scrolling box - * \return the box has a vertical scrollbar - */ +/* 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] + @@ -1232,13 +1133,7 @@ bool box_vscrollbar_present(const struct box * const box) } -/** - * Determine if a box has a horizontal scrollbar. - * - * \param box scrolling box - * \return the box has a horizontal scrollbar - */ - +/* 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] + diff --git a/content/handlers/html/box.h b/content/handlers/html/box.h index efad90fe4..1781d1332 100644 --- a/content/handlers/html/box.h +++ b/content/handlers/html/box.h @@ -19,7 +19,7 @@ /** * \file - * Box tree construction and manipulation (interface). + * Box tree construction and manipulation 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 @@ -97,9 +97,6 @@ struct content; struct box; struct browser_window; -struct column; -struct object_params; -struct object_param; struct html_content; struct nsurl; struct dom_node; @@ -109,20 +106,34 @@ struct rect; #define UNKNOWN_WIDTH INT_MAX #define UNKNOWN_MAX_WIDTH INT_MAX + typedef void (*box_construct_complete_cb)(struct html_content *c, bool success); -/** Type of a struct box. */ + +/** + * Type of a struct box. + */ typedef enum { - BOX_BLOCK, BOX_INLINE_CONTAINER, BOX_INLINE, - BOX_TABLE, BOX_TABLE_ROW, BOX_TABLE_CELL, + BOX_BLOCK, + BOX_INLINE_CONTAINER, + BOX_INLINE, + BOX_TABLE, + BOX_TABLE_ROW, + BOX_TABLE_CELL, BOX_TABLE_ROW_GROUP, - BOX_FLOAT_LEFT, BOX_FLOAT_RIGHT, - BOX_INLINE_BLOCK, BOX_BR, BOX_TEXT, - BOX_INLINE_END, BOX_NONE + BOX_FLOAT_LEFT, + BOX_FLOAT_RIGHT, + BOX_INLINE_BLOCK, + BOX_BR, + BOX_TEXT, + BOX_INLINE_END, + BOX_NONE } box_type; -/** Flags for a struct box. */ +/** + * Flags for a struct box. + */ typedef enum { NEW_LINE = 1 << 0, /* first inline on a new line */ STYLE_OWNED = 1 << 1, /* style is owned by this box */ @@ -139,9 +150,13 @@ typedef enum { IS_REPLACED = 1 << 12 /* box is a replaced element */ } box_flags; -/* Sides of a box */ + +/** + * Sides of a box + */ enum box_side { TOP, RIGHT, BOTTOM, LEFT }; + /** * Container for box border details */ @@ -151,31 +166,190 @@ struct box_border { int width; /**< border-width (pixels) */ }; -/** Node in box tree. All dimensions are in pixels. */ + +/** + * Table column data. + */ +struct column { + /** + * Type of column. + */ + enum { + COLUMN_WIDTH_UNKNOWN, + COLUMN_WIDTH_FIXED, + COLUMN_WIDTH_AUTO, + COLUMN_WIDTH_PERCENT, + COLUMN_WIDTH_RELATIVE + } type; + + /** + * Preferred width of column. Pixels for FIXED, percentage for + * PERCENT, relative units for RELATIVE, unused for AUTO. + */ + int width; + + /** + * Minimum width of content. + */ + int min; + + /** + * Maximum width of content. + */ + int max; + + /** + * Whether all of column's cells are css positioned. + */ + bool positioned; +}; + + +/** + * Linked list of object element parameters. + */ +struct object_param { + char *name; + char *value; + char *type; + char *valuetype; + struct object_param *next; +}; + + +/** + * Parameters for object element and similar elements. + */ +struct object_params { + struct nsurl *data; + char *type; + char *codetype; + struct nsurl *codebase; + struct nsurl *classid; + struct object_param *params; +}; + + +/** + * Node in box tree. All dimensions are in pixels. + */ struct box { - /** Type of box. */ + /** + * Type of box. + */ box_type type; - /** Box flags */ + /** + * Box flags + */ box_flags flags; - /** Computed styles for elements and their pseudo elements. NULL on - * non-element boxes. */ + /** + * DOM node that generated this box or NULL + */ + struct dom_node *node; + + /** + * Computed styles for elements and their pseudo elements. + * NULL on non-element boxes. + */ css_select_results *styles; - /** Style for this box. 0 for INLINE_CONTAINER and FLOAT_*. Pointer into - * a box's 'styles' select results, except for implied boxes, where it - * is a pointer to an owned computed style. */ + /** + * Style for this box. 0 for INLINE_CONTAINER and + * FLOAT_*. Pointer into a box's 'styles' select results, + * except for implied boxes, where it is a pointer to an + * owned computed style. + */ css_computed_style *style; - /** Coordinate of left padding edge relative to parent box, or relative - * to ancestor that contains this box in float_children for FLOAT_. */ + /** + * value of id attribute (or name for anchors) + */ + lwc_string *id; + + + /** + * Next sibling box, or NULL. + */ + struct box *next; + + /** + * Previous sibling box, or NULL. + */ + struct box *prev; + + /** + * First child box, or NULL. + */ + struct box *children; + + /** + * Last child box, or NULL. + */ + struct box *last; + + /** + * Parent box, or NULL. + */ + struct box *parent; + + /** + * INLINE_END box corresponding to this INLINE box, or INLINE + * box corresponding to this INLINE_END box. + */ + struct box *inline_end; + + + /** + * First float child box, or NULL. Float boxes are in the tree + * twice, in this list for the block box which defines the + * area for floats, and also in the standard tree given by + * children, next, prev, etc. + */ + struct box *float_children; + + /** + * Next sibling float box. + */ + struct box *next_float; + + /** + * If box is a float, points to box's containing block + */ + struct box *float_container; + + /** + * Level below which subsequent floats must be cleared. This + * is used only for boxes with float_children + */ + int clear_level; + + /** + * Level below which floats have been placed. + */ + int cached_place_below_level; + + + /** + * Coordinate of left padding edge relative to parent box, or + * relative to ancestor that contains this box in + * float_children for FLOAT_. + */ int x; - /** Coordinate of top padding edge, relative as for x. */ + /** + * Coordinate of top padding edge, relative as for x. + */ int y; - int width; /**< Width of content box (excluding padding etc.). */ - int height; /**< Height of content box (excluding padding etc.). */ + /** + * Width of content box (excluding padding etc.). + */ + int width; + /** + * Height of content box (excluding padding etc.). + */ + int height; /* These four variables determine the maximum extent of a box's * descendants. They are relative to the x,y coordinates of the box. @@ -196,123 +370,146 @@ struct box { int descendant_x1; /**< right edge of descendants */ int descendant_y1; /**< bottom edge of descendants */ - int margin[4]; /**< Margin: TOP, RIGHT, BOTTOM, LEFT. */ - int padding[4]; /**< Padding: TOP, RIGHT, BOTTOM, LEFT. */ - struct box_border border[4]; /**< Border: TOP, RIGHT, BOTTOM, LEFT. */ + /** + * Margin: TOP, RIGHT, BOTTOM, LEFT. + */ + int margin[4]; + + /** + * Padding: TOP, RIGHT, BOTTOM, LEFT. + */ + int padding[4]; + + /** + * Border: TOP, RIGHT, BOTTOM, LEFT. + */ + struct box_border border[4]; + + /** + * Horizontal scroll. + */ + struct scrollbar *scroll_x; - struct scrollbar *scroll_x; /**< Horizontal scroll. */ - struct scrollbar *scroll_y; /**< Vertical scroll. */ + /** + * Vertical scroll. + */ + struct scrollbar *scroll_y; - /** Width of box taking all line breaks (including margins etc). Must - * be non-negative. */ + /** + * Width of box taking all line breaks (including margins + * etc). Must be non-negative. + */ int min_width; - /** Width that would be taken with no line breaks. Must be - * non-negative. */ + + /** + * Width that would be taken with no line breaks. Must be + * non-negative. + */ int max_width; - /**< Byte offset within a textual representation of this content. */ - size_t byte_offset; - char *text; /**< Text, or 0 if none. Unterminated. */ - size_t length; /**< Length of text. */ + /** + * Text, or NULL if none. Unterminated. + */ + char *text; + + /** + * Length of text. + */ + size_t length; - /** Width of space after current text (depends on font and size). */ + /** + * Width of space after current text (depends on font and size). + */ int space; - struct nsurl *href; /**< Link, or 0. */ - const char *target; /**< Link target, or 0. */ - const char *title; /**< Title, or 0. */ - - unsigned int columns; /**< Number of columns for TABLE / TABLE_CELL. */ - unsigned int rows; /**< Number of rows for TABLE only. */ - unsigned int start_column; /**< Start column for TABLE_CELL only. */ - - struct box *next; /**< Next sibling box, or 0. */ - struct box *prev; /**< Previous sibling box, or 0. */ - struct box *children; /**< First child box, or 0. */ - struct box *last; /**< Last child box, or 0. */ - struct box *parent; /**< Parent box, or 0. */ - /** INLINE_END box corresponding to this INLINE box, or INLINE box - * corresponding to this INLINE_END box. */ - struct box *inline_end; + /** + * Byte offset within a textual representation of this content. + */ + size_t byte_offset; - /** First float child box, or 0. Float boxes are in the tree twice, in - * this list for the block box which defines the area for floats, and - * also in the standard tree given by children, next, prev, etc. */ - struct box *float_children; - /** Next sibling float box. */ - struct box *next_float; - /** If box is a float, points to box's containing block */ - struct box *float_container; - /** Level below which subsequent floats must be cleared. - * This is used only for boxes with float_children */ - int clear_level; - /* Level below which floats have been placed. */ - int cached_place_below_level; + /** + * Link, or NULL. + */ + struct nsurl *href; + + /** + * Link target, or NULL. + */ + const char *target; - /** List marker box if this is a list-item, or 0. */ + /** + * Title, or NULL. + */ + const char *title; + + + /** + * Number of columns for TABLE / TABLE_CELL. + */ + unsigned int columns; + + /** + * Number of rows for TABLE only. + */ + unsigned int rows; + + /** + * Start column for TABLE_CELL only. + */ + unsigned int start_column; + + /** + * Array of table column data for TABLE only. + */ + struct column *col; + + + /** + * List marker box if this is a list-item, or NULL. + */ struct box *list_marker; - struct column *col; /**< Array of table column data for TABLE only. */ - /** Form control data, or 0 if not a form control. */ + /** + * Form control data, or NULL if not a form control. + */ struct form_control* gadget; - char *usemap; /** (Image)map to use with this object, or 0 if none */ - lwc_string *id; /**< value of id attribute (or name for anchors) */ - /** Background image for this box, or 0 if none */ + /** + * (Image)map to use with this object, or NULL if none + */ + char *usemap; + + + /** + * Background image for this box, or NULL if none + */ struct hlcache_handle *background; - /** Object in this box (usually an image), or 0 if none. */ + + /** + * Object in this box (usually an image), or NULL if none. + */ struct hlcache_handle* object; - /** Parameters for the object, or 0. */ - struct object_params *object_params; - /** Iframe's browser_window, or NULL if none */ - struct browser_window *iframe; + /** + * Parameters for the object, or NULL. + */ + struct object_params *object_params; - struct dom_node *node; /**< DOM node that generated this box or NULL */ -}; -/** Table column data. */ -struct column { - /** Type of column. */ - enum { COLUMN_WIDTH_UNKNOWN, COLUMN_WIDTH_FIXED, - COLUMN_WIDTH_AUTO, COLUMN_WIDTH_PERCENT, - COLUMN_WIDTH_RELATIVE } type; - /** Preferred width of column. Pixels for FIXED, percentage for PERCENT, - * relative units for RELATIVE, unused for AUTO. */ - int width; - /** Minimum width of content. */ - int min; - /** Maximum width of content. */ - int max; - /** Whether all of column's cells are css positioned. */ - bool positioned; -}; + /** + * Iframe's browser_window, or NULL if none + */ + struct browser_window *iframe; -/** Parameters for object element and similar elements. */ -struct object_params { - struct nsurl *data; - char *type; - char *codetype; - struct nsurl *codebase; - struct nsurl *classid; - struct object_param *params; }; -/** Linked list of object element parameters. */ -struct object_param { - char *name; - char *value; - char *type; - char *valuetype; - struct object_param *next; -}; -/** Frame target names (constant pointers to save duplicating the strings many +/* Frame target names (constant pointers to save duplicating the strings many * times). We convert _blank to _top for user-friendliness. */ extern const char *TARGET_SELF; extern const char *TARGET_PARENT; @@ -320,38 +517,154 @@ 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); + -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); -void box_bounds(struct box *box, struct rect *r); + + +/** + * 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); -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); -struct box *box_pick_text_box(struct html_content *html, - int x, int y, int dir, int *dx, int *dy); + + +/** + * 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); -void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style); + /** - * Extract a URL from a relative link, handling junk like whitespace and - * attempting to read a real URL from "javascript:" links. - * - * \param content html content - * \param dsrel relative URL text taken from page - * \param base base for relative URLs - * \param result updated to target URL on heap, unchanged if extract failed - * \return true on success, false on memory exhaustion + * Print a box tree to a file. */ -bool box_extract_link(const struct html_content *content, const struct dom_string *dsrel, struct nsurl *base, struct nsurl **result); +void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style); + /** * Applies the given scroll setup to a box. This includes scroll @@ -366,29 +679,24 @@ bool box_extract_link(const struct html_content *content, const struct dom_strin nserror box_handle_scrollbars(struct content *c, struct box *box, bool bottom, bool right); -bool box_vscrollbar_present(const struct box *box); -bool box_hscrollbar_present(const struct box *box); /** - * Construct a box tree from an xml tree and stylesheets. + * Determine if a box has a vertical scrollbar. * - * \param n xml tree - * \param c content of type CONTENT_HTML to construct box tree in - * \param cb callback to report conversion completion - * \return netsurf error code indicating status of call + * \param box scrolling box + * \return the box has a vertical scrollbar */ -nserror dom_to_box(struct dom_node *n, struct html_content *c, - box_construct_complete_cb cb, void **box_conversion_context); +bool box_vscrollbar_present(const struct box *box); + /** - * aborts any ongoing box construction + * Determine if a box has a horizontal scrollbar. + * + * \param box scrolling box + * \return the box has a horizontal scrollbar */ -nserror cancel_dom_to_box(void *box_conversion_context); +bool box_hscrollbar_present(const struct box *box); -bool box_normalise_block( - struct box *block, - const struct box *root, - struct html_content *c); /** * Check if layout box is a first child. @@ -401,12 +709,5 @@ static inline bool box_is_first_child(struct box *b) return (b->parent == NULL || b == b->parent->children); } -/** - * Retrieve the box for a dom node, if there is one - * - * \param node The DOM node - * \return The box if there is one - */ -struct box *box_for_node(struct dom_node *node); #endif diff --git a/content/handlers/html/box_construct.c b/content/handlers/html/box_construct.c index 16154cbd4..039da7478 100644 --- a/content/handlers/html/box_construct.c +++ b/content/handlers/html/box_construct.c @@ -51,6 +51,8 @@ #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" @@ -3139,7 +3141,7 @@ static void convert_xml_to_box(struct box_construct_ctx *ctx) } -/* Exported function, documented in box.h */ +/* exported function documented in html/box_construct.h */ nserror dom_to_box(dom_node *n, html_content *c, @@ -3175,7 +3177,7 @@ dom_to_box(dom_node *n, } -/* Exported function, see box.h */ +/* exported function documented in html/box_construct.h */ nserror cancel_dom_to_box(void *box_conversion_context) { struct box_construct_ctx *ctx = box_conversion_context; @@ -3193,7 +3195,7 @@ nserror cancel_dom_to_box(void *box_conversion_context) } -/* Exported function, see box.h */ +/* exported function documented in html/box_construct.h */ struct box *box_for_node(dom_node *n) { struct box *box = NULL; @@ -3208,59 +3210,7 @@ struct box *box_for_node(dom_node *n) } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -/* exported function documented in html/box.h */ +/* exported function documented in html/box_construct.h */ bool box_extract_link(const html_content *content, const dom_string *dsrel, diff --git a/content/handlers/html/box_construct.h b/content/handlers/html/box_construct.h new file mode 100644 index 000000000..e93f515e4 --- /dev/null +++ b/content/handlers/html/box_construct.h @@ -0,0 +1,65 @@ +/* + * 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 interface. + */ + +#ifndef NETSURF_HTML_BOX_CONSTRUCT_H +#define NETSURF_HTML_BOX_CONSTRUCT_H + +/** + * Construct a box tree from a dom and html content + * + * \param n dom document + * \param c content of type CONTENT_HTML to construct box tree in + * \param cb callback to report conversion completion + * \param box_conversion_context pointer that recives the conversion context + * \return netsurf error code indicating status of call + */ +nserror dom_to_box(struct dom_node *n, struct html_content *c, box_construct_complete_cb cb, void **box_conversion_context); + + +/** + * aborts any ongoing box construction + */ +nserror cancel_dom_to_box(void *box_conversion_context); + + +/** + * Retrieve the box for a dom node, if there is one + * + * \param node The DOM node + * \return The box if there is one + */ +struct box *box_for_node(struct dom_node *node); + +/** + * Extract a URL from a relative link, handling junk like whitespace and + * attempting to read a real URL from "javascript:" links. + * + * \param content html content + * \param dsrel relative URL text taken from page + * \param base base for relative URLs + * \param result updated to target URL on heap, unchanged if extract failed + * \return true on success, false on memory exhaustion + */ +bool box_extract_link(const struct html_content *content, const struct dom_string *dsrel, struct nsurl *base, struct nsurl **result); + +#endif diff --git a/content/handlers/html/box_normalise.c b/content/handlers/html/box_normalise.c index 7155cb722..03fe731b2 100644 --- a/content/handlers/html/box_normalise.c +++ b/content/handlers/html/box_normalise.c @@ -21,7 +21,7 @@ /** * \file - * Box tree normalisation (implementation). + * Box tree normalisation implementation. */ #include @@ -33,6 +33,7 @@ #include "css/select.h" #include "html/box.h" +#include "html/box_normalise.h" #include "html/html_internal.h" #include "html/table.h" @@ -66,279 +67,310 @@ struct columns { }; -static bool box_normalise_table( - struct box *table, - const struct box *root, - html_content *c); -static bool box_normalise_table_spans( - struct box *table, - const struct box *root, - struct span_info *spans, - html_content *c); -static bool box_normalise_table_row_group( - struct box *row_group, - const struct box *root, - struct columns *col_info, - html_content *c); -static bool box_normalise_table_row( - struct box *row, - const struct box *root, - struct columns *col_info, - html_content *c); -static bool calculate_table_row(struct columns *col_info, - unsigned int col_span, unsigned int row_span, - unsigned int *start_column, struct box *cell); -static bool box_normalise_inline_container( - struct box *cont, - const struct box *root, - html_content *c); - /** - * Ensure the box tree is correctly nested by adding and removing nodes. - * - * \param block box of type BLOCK, INLINE_BLOCK, or TABLE_CELL - * \param root root box of document - * \param c content of boxes - * \return true on success, false on memory exhaustion + * Compute the column index at which the current cell begins. + * Additionally, update the column record to reflect row spanning. * - * The tree is modified to satisfy the following: - * \code - * parent permitted child nodes - * BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE - * INLINE_CONTAINER INLINE, INLINE_BLOCK, FLOAT_LEFT, FLOAT_RIGHT, BR, TEXT - * INLINE, TEXT 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 + * \param col_info Column record + * \param col_span Number of columns that current cell spans + * \param row_span Number of rows that current cell spans + * \param start_column Pointer to location to receive column index + * \param cell Box for current table cell + * \return true on success, false on memory exhaustion */ +static bool +calculate_table_row(struct columns *col_info, + unsigned int col_span, + unsigned int row_span, + unsigned int *start_column, + struct box *cell) +{ + unsigned int cell_start_col = col_info->current_column; + unsigned int cell_end_col; + unsigned int i; + struct span_info *spans; + struct box *rg = cell->parent->parent; /* Cell's row group */ + + /* Skip columns with cells spanning from above */ + /* TODO: Need to ignore cells spanning from above that belong to + * different row group. We don't have that info here. */ + while (col_info->spans[cell_start_col].row_span != 0 && + col_info->spans[cell_start_col].rg == rg) { + cell_start_col++; + } + + /* Update current column with calculated start */ + col_info->current_column = cell_start_col; + + /* If this cell has a colspan of 0, then assume 1. + * No other browser supports colspan=0, anyway. */ + if (col_span == 0) + col_span = 1; + + cell_end_col = cell_start_col + col_span; + + if (col_info->num_columns < cell_end_col) { + /* It appears that this row has more columns than + * the maximum recorded for the table so far. + * Allocate more span records. */ + spans = realloc(col_info->spans, + sizeof *spans * (cell_end_col + 1)); + if (spans == NULL) + return false; + + col_info->spans = spans; + col_info->num_columns = cell_end_col; + + /* Mark new final column as sentinel */ + col_info->spans[cell_end_col].row_span = 0; + col_info->spans[cell_end_col].auto_row = false; + } + + /* This cell may span multiple columns. If it also wants to span + * multiple rows, temporarily assume it spans 1 row only. This will + * be fixed up in box_normalise_table_spans() */ + for (i = cell_start_col; i < cell_end_col; i++) { + col_info->spans[i].row_span = (row_span == 0) ? 1 : row_span; + col_info->spans[i].auto_row = (row_span == 0); + col_info->spans[i].rg = rg; + } + + /* Update current column with calculated end. */ + col_info->current_column = cell_end_col; + + *start_column = cell_start_col; -bool box_normalise_block( - struct box *block, - const struct box *root, - html_content *c) + return true; +} + + +static bool +box_normalise_table_row(struct box *row, + const struct box *root, + struct columns *col_info, + html_content * c) { struct box *child; struct box *next_child; - struct box *table; + struct box *cell = NULL; css_computed_style *style; + unsigned int i; nscss_select_ctx ctx; - assert(block != NULL); - assert(root != NULL); + assert(row != NULL); + assert(row->type == BOX_TABLE_ROW); ctx.root_style = root->style; #ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "block %p, block->type %u", block, block->type); -#endif - - assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK || - block->type == BOX_TABLE_CELL); - - for (child = block->children; child != NULL; child = next_child) { -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "child %p, child->type = %d", child, - child->type); + NSLOG(netsurf, INFO, "row %p", row); #endif - next_child = child->next; /* child may be destroyed */ + for (child = row->children; child != NULL; child = next_child) { + next_child = child->next; switch (child->type) { - case BOX_BLOCK: + case BOX_TABLE_CELL: /* ok */ if (box_normalise_block(child, root, c) == false) return false; + cell = child; break; + case BOX_BLOCK: case BOX_INLINE_CONTAINER: - if (box_normalise_inline_container(child, root, c) == false) - return false; - break; case BOX_TABLE: - if (box_normalise_table(child, root, c) == false) - return false; - break; - case BOX_INLINE: - case BOX_INLINE_END: - case BOX_INLINE_BLOCK: - case BOX_FLOAT_LEFT: - case BOX_FLOAT_RIGHT: - case BOX_BR: - case BOX_TEXT: - /* should have been wrapped in inline - container by convert_xml_to_box() */ - assert(0); - break; case BOX_TABLE_ROW_GROUP: case BOX_TABLE_ROW: - case BOX_TABLE_CELL: - /* insert implied table */ - assert(block->style != NULL); + /* insert implied table cell */ + assert(row->style != NULL); ctx.ctx = c->select_ctx; ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); ctx.base_url = c->base_url; ctx.universal = c->universal; - style = nscss_get_blank_style(&ctx, block->style); + style = nscss_get_blank_style(&ctx, row->style); if (style == NULL) return false; - table = box_create(NULL, style, true, block->href, - block->target, NULL, NULL, c->bctx); - if (table == NULL) { + cell = box_create(NULL, style, true, row->href, + row->target, NULL, NULL, c->bctx); + if (cell == NULL) { css_computed_style_destroy(style); return false; } - table->type = BOX_TABLE; + cell->type = BOX_TABLE_CELL; if (child->prev == NULL) - block->children = table; + row->children = cell; else - child->prev->next = table; + child->prev->next = cell; - table->prev = child->prev; + cell->prev = child->prev; while (child != NULL && ( + child->type == BOX_BLOCK || + child->type == BOX_INLINE_CONTAINER || + child->type == BOX_TABLE || child->type == BOX_TABLE_ROW_GROUP || - child->type == BOX_TABLE_ROW || - child->type == BOX_TABLE_CELL)) { - box_add_child(table, child); + child->type == BOX_TABLE_ROW)) { + box_add_child(cell, child); next_child = child->next; child->next = NULL; child = next_child; } - table->last->next = NULL; - table->next = next_child = child; - if (table->next != NULL) - table->next->prev = table; + assert(cell->last != NULL); + + cell->last->next = NULL; + cell->next = next_child = child; + if (cell->next != NULL) + cell->next->prev = cell; else - block->last = table; - table->parent = block; + row->last = cell; + cell->parent = row; - if (box_normalise_table(table, root, c) == false) + if (box_normalise_block(cell, root, c) == false) return false; break; + case BOX_INLINE: + case BOX_INLINE_END: + case BOX_INLINE_BLOCK: + case BOX_FLOAT_LEFT: + case BOX_FLOAT_RIGHT: + case BOX_BR: + case BOX_TEXT: + /* should have been wrapped in inline + container by convert_xml_to_box() */ + assert(0); + break; default: assert(0); } + + if (calculate_table_row(col_info, cell->columns, cell->rows, + &cell->start_column, cell) == false) + return false; + } + + + /* Update row spanning details for all columns */ + for (i = 0; i < col_info->num_columns; i++) { + if (col_info->spans[i].row_span != 0 && + col_info->spans[i].auto_row == false) { + /* This cell spans rows, and is not an auto row. + * Reduce number of rows left to span */ + col_info->spans[i].row_span--; + } } + /* Reset current column for next row */ + col_info->current_column = 0; + + /* Increment row counter */ + col_info->num_rows++; + +#ifdef BOX_NORMALISE_DEBUG + NSLOG(netsurf, INFO, "row %p done", row); +#endif + return true; } -bool box_normalise_table( - struct box *table, - const struct box *root, - html_content * c) +static bool +box_normalise_table_row_group(struct box *row_group, + const struct box *root, + struct columns *col_info, + html_content * c) { struct box *child; struct box *next_child; - struct box *row_group; + struct box *row; css_computed_style *style; - struct columns col_info; nscss_select_ctx ctx; + unsigned int group_row_count = 0; - assert(table != NULL); - assert(table->type == BOX_TABLE); + assert(row_group != 0); + assert(row_group->type == BOX_TABLE_ROW_GROUP); ctx.root_style = root->style; #ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "table %p", table); + NSLOG(netsurf, INFO, "row_group %p", row_group); #endif - col_info.num_columns = 1; - col_info.current_column = 0; - col_info.spans = malloc(2 * sizeof *col_info.spans); - if (col_info.spans == NULL) - return false; - - col_info.spans[0].row_span = col_info.spans[1].row_span = 0; - col_info.spans[0].auto_row = false; - col_info.spans[1].auto_row = false; - col_info.num_rows = 0; - - for (child = table->children; child != NULL; child = next_child) { + for (child = row_group->children; child != NULL; child = next_child) { next_child = child->next; + switch (child->type) { - case BOX_TABLE_ROW_GROUP: + case BOX_TABLE_ROW: /* ok */ - if (box_normalise_table_row_group(child, root, - &col_info, c) == false) { - free(col_info.spans); + group_row_count++; + if (box_normalise_table_row(child, root, col_info, + c) == false) return false; - } break; case BOX_BLOCK: case BOX_INLINE_CONTAINER: case BOX_TABLE: - case BOX_TABLE_ROW: + case BOX_TABLE_ROW_GROUP: case BOX_TABLE_CELL: - /* insert implied table row group */ - assert(table->style != NULL); + /* insert implied table row */ + assert(row_group->style != NULL); ctx.ctx = c->select_ctx; ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); ctx.base_url = c->base_url; ctx.universal = c->universal; - style = nscss_get_blank_style(&ctx, table->style); - if (style == NULL) { - free(col_info.spans); + style = nscss_get_blank_style(&ctx, row_group->style); + if (style == NULL) return false; - } - row_group = box_create(NULL, style, true, table->href, - table->target, NULL, NULL, c->bctx); - if (row_group == NULL) { + row = box_create(NULL, style, true, row_group->href, + row_group->target, NULL, NULL, c->bctx); + if (row == NULL) { css_computed_style_destroy(style); - free(col_info.spans); return false; } - - row_group->type = BOX_TABLE_ROW_GROUP; + row->type = BOX_TABLE_ROW; if (child->prev == NULL) - table->children = row_group; + row_group->children = row; else - child->prev->next = row_group; + child->prev->next = row; - row_group->prev = child->prev; + row->prev = child->prev; while (child != NULL && ( child->type == BOX_BLOCK || child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || - child->type == BOX_TABLE_ROW || + child->type == BOX_TABLE_ROW_GROUP || child->type == BOX_TABLE_CELL)) { - box_add_child(row_group, child); + box_add_child(row, child); next_child = child->next; child->next = NULL; child = next_child; } - assert(row_group->last != NULL); + assert(row->last != NULL); - row_group->last->next = NULL; - row_group->next = next_child = child; - if (row_group->next != NULL) - row_group->next->prev = row_group; + row->last->next = NULL; + row->next = next_child = child; + if (row->next != NULL) + row->next->prev = row; else - table->last = row_group; - row_group->parent = table; + row_group->last = row; + row->parent = row_group; - if (box_normalise_table_row_group(row_group, root, - &col_info, c) == false) { - free(col_info.spans); + group_row_count++; + if (box_normalise_table_row(row, root, col_info, + c) == false) return false; - } break; case BOX_INLINE: case BOX_INLINE_END: @@ -352,48 +384,25 @@ bool box_normalise_table( assert(0); break; default: - fprintf(stderr, "%i\n", child->type); assert(0); } } - table->columns = col_info.num_columns; - table->rows = col_info.num_rows; - - if (table->children == NULL) { - struct box *row; - + if (row_group->children == NULL) { #ifdef BOX_NORMALISE_DEBUG NSLOG(netsurf, INFO, - "table->children == 0, creating implied row"); + "row_group->children == 0, inserting implied row"); #endif - assert(table->style != NULL); + assert(row_group->style != NULL); ctx.ctx = c->select_ctx; ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); ctx.base_url = c->base_url; ctx.universal = c->universal; - style = nscss_get_blank_style(&ctx, table->style); - if (style == NULL) { - free(col_info.spans); - return false; - } - - row_group = box_create(NULL, style, true, table->href, - table->target, NULL, NULL, c->bctx); - if (row_group == NULL) { - css_computed_style_destroy(style); - free(col_info.spans); - return false; - } - row_group->type = BOX_TABLE_ROW_GROUP; - style = nscss_get_blank_style(&ctx, row_group->style); if (style == NULL) { - box_free(row_group); - free(col_info.spans); return false; } @@ -401,8 +410,6 @@ bool box_normalise_table( row_group->target, NULL, NULL, c->bctx); if (row == NULL) { css_computed_style_destroy(style); - box_free(row_group); - free(col_info.spans); return false; } row->type = BOX_TABLE_ROW; @@ -410,21 +417,16 @@ bool box_normalise_table( row->parent = row_group; row_group->children = row_group->last = row; - row_group->parent = table; - table->children = table->last = row_group; - - table->rows = 1; - } + group_row_count = 1; - if (box_normalise_table_spans(table, root, col_info.spans, c) == false) { - free(col_info.spans); - return false; + /* Keep table's row count in sync */ + col_info->num_rows++; } - free(col_info.spans); + row_group->rows = group_row_count; #ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "table %p done", table); + NSLOG(netsurf, INFO, "row_group %p done", row_group); #endif return true; @@ -441,12 +443,11 @@ bool box_normalise_table( * \param c Content containing table * \return True on success, false on memory exhaustion. */ - -bool box_normalise_table_spans( - struct box *table, - const struct box *root, - struct span_info *spans, - html_content *c) +static bool +box_normalise_table_spans(struct box *table, + const struct box *root, + struct span_info *spans, + html_content *c) { struct box *table_row_group; struct box *table_row; @@ -601,249 +602,111 @@ bool box_normalise_table_spans( } -bool box_normalise_table_row_group( - struct box *row_group, - const struct box *root, - struct columns *col_info, - html_content * c) +static bool +box_normalise_table(struct box *table, const struct box *root, html_content * c) { struct box *child; struct box *next_child; - struct box *row; + struct box *row_group; css_computed_style *style; + struct columns col_info; nscss_select_ctx ctx; - unsigned int group_row_count = 0; - assert(row_group != 0); - assert(row_group->type == BOX_TABLE_ROW_GROUP); + assert(table != NULL); + assert(table->type == BOX_TABLE); ctx.root_style = root->style; #ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "row_group %p", row_group); -#endif - - for (child = row_group->children; child != NULL; child = next_child) { - next_child = child->next; - - switch (child->type) { - case BOX_TABLE_ROW: - /* ok */ - group_row_count++; - if (box_normalise_table_row(child, root, col_info, - c) == false) - return false; - break; - case BOX_BLOCK: - case BOX_INLINE_CONTAINER: - case BOX_TABLE: - case BOX_TABLE_ROW_GROUP: - case BOX_TABLE_CELL: - /* insert implied table row */ - assert(row_group->style != NULL); - - ctx.ctx = c->select_ctx; - ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); - ctx.base_url = c->base_url; - ctx.universal = c->universal; - - style = nscss_get_blank_style(&ctx, row_group->style); - if (style == NULL) - return false; - - row = box_create(NULL, style, true, row_group->href, - row_group->target, NULL, NULL, c->bctx); - if (row == NULL) { - css_computed_style_destroy(style); - return false; - } - row->type = BOX_TABLE_ROW; - - if (child->prev == NULL) - row_group->children = row; - else - child->prev->next = row; - - row->prev = child->prev; - - while (child != NULL && ( - child->type == BOX_BLOCK || - child->type == BOX_INLINE_CONTAINER || - child->type == BOX_TABLE || - child->type == BOX_TABLE_ROW_GROUP || - child->type == BOX_TABLE_CELL)) { - box_add_child(row, child); - - next_child = child->next; - child->next = NULL; - child = next_child; - } - - assert(row->last != NULL); - - row->last->next = NULL; - row->next = next_child = child; - if (row->next != NULL) - row->next->prev = row; - else - row_group->last = row; - row->parent = row_group; - - group_row_count++; - if (box_normalise_table_row(row, root, col_info, - c) == false) - return false; - break; - case BOX_INLINE: - case BOX_INLINE_END: - case BOX_INLINE_BLOCK: - case BOX_FLOAT_LEFT: - case BOX_FLOAT_RIGHT: - case BOX_BR: - case BOX_TEXT: - /* should have been wrapped in inline - container by convert_xml_to_box() */ - assert(0); - break; - default: - assert(0); - } - } - - if (row_group->children == NULL) { -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, - "row_group->children == 0, inserting implied row"); -#endif - - assert(row_group->style != NULL); - - ctx.ctx = c->select_ctx; - ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); - ctx.base_url = c->base_url; - ctx.universal = c->universal; - - style = nscss_get_blank_style(&ctx, row_group->style); - if (style == NULL) { - return false; - } - - row = box_create(NULL, style, true, row_group->href, - row_group->target, NULL, NULL, c->bctx); - if (row == NULL) { - css_computed_style_destroy(style); - return false; - } - row->type = BOX_TABLE_ROW; - - row->parent = row_group; - row_group->children = row_group->last = row; - - group_row_count = 1; - - /* Keep table's row count in sync */ - col_info->num_rows++; - } - - row_group->rows = group_row_count; - -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "row_group %p done", row_group); + NSLOG(netsurf, INFO, "table %p", table); #endif - return true; -} - - -bool box_normalise_table_row( - struct box *row, - const struct box *root, - struct columns *col_info, - html_content * c) -{ - struct box *child; - struct box *next_child; - struct box *cell = NULL; - css_computed_style *style; - unsigned int i; - nscss_select_ctx ctx; - - assert(row != NULL); - assert(row->type == BOX_TABLE_ROW); - - ctx.root_style = root->style; + col_info.num_columns = 1; + col_info.current_column = 0; + col_info.spans = malloc(2 * sizeof *col_info.spans); + if (col_info.spans == NULL) + return false; -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "row %p", row); -#endif + col_info.spans[0].row_span = col_info.spans[1].row_span = 0; + col_info.spans[0].auto_row = false; + col_info.spans[1].auto_row = false; + col_info.num_rows = 0; - for (child = row->children; child != NULL; child = next_child) { + for (child = table->children; child != NULL; child = next_child) { next_child = child->next; - switch (child->type) { - case BOX_TABLE_CELL: + case BOX_TABLE_ROW_GROUP: /* ok */ - if (box_normalise_block(child, root, c) == false) + if (box_normalise_table_row_group(child, root, + &col_info, c) == false) { + free(col_info.spans); return false; - cell = child; + } break; case BOX_BLOCK: case BOX_INLINE_CONTAINER: case BOX_TABLE: - case BOX_TABLE_ROW_GROUP: case BOX_TABLE_ROW: - /* insert implied table cell */ - assert(row->style != NULL); + case BOX_TABLE_CELL: + /* insert implied table row group */ + assert(table->style != NULL); ctx.ctx = c->select_ctx; ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); ctx.base_url = c->base_url; ctx.universal = c->universal; - style = nscss_get_blank_style(&ctx, row->style); - if (style == NULL) + style = nscss_get_blank_style(&ctx, table->style); + if (style == NULL) { + free(col_info.spans); return false; + } - cell = box_create(NULL, style, true, row->href, - row->target, NULL, NULL, c->bctx); - if (cell == NULL) { + row_group = box_create(NULL, style, true, table->href, + table->target, NULL, NULL, c->bctx); + if (row_group == NULL) { css_computed_style_destroy(style); + free(col_info.spans); return false; } - cell->type = BOX_TABLE_CELL; + + row_group->type = BOX_TABLE_ROW_GROUP; if (child->prev == NULL) - row->children = cell; + table->children = row_group; else - child->prev->next = cell; + child->prev->next = row_group; - cell->prev = child->prev; + row_group->prev = child->prev; while (child != NULL && ( child->type == BOX_BLOCK || child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || - child->type == BOX_TABLE_ROW_GROUP || - child->type == BOX_TABLE_ROW)) { - box_add_child(cell, child); + child->type == BOX_TABLE_ROW || + child->type == BOX_TABLE_CELL)) { + box_add_child(row_group, child); next_child = child->next; child->next = NULL; child = next_child; } - assert(cell->last != NULL); + assert(row_group->last != NULL); - cell->last->next = NULL; - cell->next = next_child = child; - if (cell->next != NULL) - cell->next->prev = cell; + row_group->last->next = NULL; + row_group->next = next_child = child; + if (row_group->next != NULL) + row_group->next->prev = row_group; else - row->last = cell; - cell->parent = row; + table->last = row_group; + row_group->parent = table; - if (box_normalise_block(cell, root, c) == false) + if (box_normalise_table_row_group(row_group, root, + &col_info, c) == false) { + free(col_info.spans); return false; + } break; case BOX_INLINE: case BOX_INLINE_END: @@ -857,118 +720,89 @@ bool box_normalise_table_row( assert(0); break; default: + fprintf(stderr, "%i\n", child->type); assert(0); } - - if (calculate_table_row(col_info, cell->columns, cell->rows, - &cell->start_column, cell) == false) - return false; - } - - - /* Update row spanning details for all columns */ - for (i = 0; i < col_info->num_columns; i++) { - if (col_info->spans[i].row_span != 0 && - col_info->spans[i].auto_row == false) { - /* This cell spans rows, and is not an auto row. - * Reduce number of rows left to span */ - col_info->spans[i].row_span--; - } } - /* Reset current column for next row */ - col_info->current_column = 0; + table->columns = col_info.num_columns; + table->rows = col_info.num_rows; - /* Increment row counter */ - col_info->num_rows++; + if (table->children == NULL) { + struct box *row; #ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "row %p done", row); + NSLOG(netsurf, INFO, + "table->children == 0, creating implied row"); #endif - return true; -} - - -/** - * Compute the column index at which the current cell begins. - * Additionally, update the column record to reflect row spanning. - * - * \param col_info Column record - * \param col_span Number of columns that current cell spans - * \param row_span Number of rows that current cell spans - * \param start_column Pointer to location to receive column index - * \param cell Box for current table cell - * \return true on success, false on memory exhaustion - */ - -bool calculate_table_row(struct columns *col_info, - unsigned int col_span, unsigned int row_span, - unsigned int *start_column, struct box *cell) -{ - unsigned int cell_start_col = col_info->current_column; - unsigned int cell_end_col; - unsigned int i; - struct span_info *spans; - struct box *rg = cell->parent->parent; /* Cell's row group */ + assert(table->style != NULL); - /* Skip columns with cells spanning from above */ - /* TODO: Need to ignore cells spanning from above that belong to - * different row group. We don't have that info here. */ - while (col_info->spans[cell_start_col].row_span != 0 && - col_info->spans[cell_start_col].rg == rg) { - cell_start_col++; - } + ctx.ctx = c->select_ctx; + ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); + ctx.base_url = c->base_url; + ctx.universal = c->universal; - /* Update current column with calculated start */ - col_info->current_column = cell_start_col; + style = nscss_get_blank_style(&ctx, table->style); + if (style == NULL) { + free(col_info.spans); + return false; + } - /* If this cell has a colspan of 0, then assume 1. - * No other browser supports colspan=0, anyway. */ - if (col_span == 0) - col_span = 1; + row_group = box_create(NULL, style, true, table->href, + table->target, NULL, NULL, c->bctx); + if (row_group == NULL) { + css_computed_style_destroy(style); + free(col_info.spans); + return false; + } + row_group->type = BOX_TABLE_ROW_GROUP; - cell_end_col = cell_start_col + col_span; + style = nscss_get_blank_style(&ctx, row_group->style); + if (style == NULL) { + box_free(row_group); + free(col_info.spans); + return false; + } - if (col_info->num_columns < cell_end_col) { - /* It appears that this row has more columns than - * the maximum recorded for the table so far. - * Allocate more span records. */ - spans = realloc(col_info->spans, - sizeof *spans * (cell_end_col + 1)); - if (spans == NULL) + row = box_create(NULL, style, true, row_group->href, + row_group->target, NULL, NULL, c->bctx); + if (row == NULL) { + css_computed_style_destroy(style); + box_free(row_group); + free(col_info.spans); return false; + } + row->type = BOX_TABLE_ROW; - col_info->spans = spans; - col_info->num_columns = cell_end_col; + row->parent = row_group; + row_group->children = row_group->last = row; - /* Mark new final column as sentinel */ - col_info->spans[cell_end_col].row_span = 0; - col_info->spans[cell_end_col].auto_row = false; + row_group->parent = table; + table->children = table->last = row_group; + + table->rows = 1; } - /* This cell may span multiple columns. If it also wants to span - * multiple rows, temporarily assume it spans 1 row only. This will - * be fixed up in box_normalise_table_spans() */ - for (i = cell_start_col; i < cell_end_col; i++) { - col_info->spans[i].row_span = (row_span == 0) ? 1 : row_span; - col_info->spans[i].auto_row = (row_span == 0); - col_info->spans[i].rg = rg; + if (box_normalise_table_spans(table, root, col_info.spans, c) == false) { + free(col_info.spans); + return false; } - /* Update current column with calculated end. */ - col_info->current_column = cell_end_col; + free(col_info.spans); - *start_column = cell_start_col; +#ifdef BOX_NORMALISE_DEBUG + NSLOG(netsurf, INFO, "table %p done", table); +#endif return true; } -bool box_normalise_inline_container( - struct box *cont, - const struct box *root, - html_content * c) +static bool +box_normalise_inline_container(struct box *cont, + const struct box *root, + html_content * c) { struct box *child; struct box *next_child; @@ -1045,3 +879,119 @@ bool box_normalise_inline_container( return true; } + + +/* Exported function documented in html/box_normalise.h */ +bool +box_normalise_block(struct box *block, const struct box *root, html_content *c) +{ + struct box *child; + struct box *next_child; + struct box *table; + css_computed_style *style; + nscss_select_ctx ctx; + + assert(block != NULL); + assert(root != NULL); + + ctx.root_style = root->style; + +#ifdef BOX_NORMALISE_DEBUG + NSLOG(netsurf, INFO, "block %p, block->type %u", block, block->type); +#endif + + assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK || + block->type == BOX_TABLE_CELL); + + for (child = block->children; child != NULL; child = next_child) { +#ifdef BOX_NORMALISE_DEBUG + NSLOG(netsurf, INFO, "child %p, child->type = %d", child, + child->type); +#endif + + next_child = child->next; /* child may be destroyed */ + + switch (child->type) { + case BOX_BLOCK: + /* ok */ + if (box_normalise_block(child, root, c) == false) + return false; + break; + case BOX_INLINE_CONTAINER: + if (box_normalise_inline_container(child, root, c) == false) + return false; + break; + case BOX_TABLE: + if (box_normalise_table(child, root, c) == false) + return false; + break; + case BOX_INLINE: + case BOX_INLINE_END: + case BOX_INLINE_BLOCK: + case BOX_FLOAT_LEFT: + case BOX_FLOAT_RIGHT: + case BOX_BR: + case BOX_TEXT: + /* should have been wrapped in inline + container by convert_xml_to_box() */ + assert(0); + break; + case BOX_TABLE_ROW_GROUP: + case BOX_TABLE_ROW: + case BOX_TABLE_CELL: + /* insert implied table */ + assert(block->style != NULL); + + ctx.ctx = c->select_ctx; + ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); + ctx.base_url = c->base_url; + ctx.universal = c->universal; + + style = nscss_get_blank_style(&ctx, block->style); + if (style == NULL) + return false; + + table = box_create(NULL, style, true, block->href, + block->target, NULL, NULL, c->bctx); + if (table == NULL) { + css_computed_style_destroy(style); + return false; + } + table->type = BOX_TABLE; + + if (child->prev == NULL) + block->children = table; + else + child->prev->next = table; + + table->prev = child->prev; + + while (child != NULL && ( + child->type == BOX_TABLE_ROW_GROUP || + child->type == BOX_TABLE_ROW || + child->type == BOX_TABLE_CELL)) { + box_add_child(table, child); + + next_child = child->next; + child->next = NULL; + child = next_child; + } + + table->last->next = NULL; + table->next = next_child = child; + if (table->next != NULL) + table->next->prev = table; + else + block->last = table; + table->parent = block; + + if (box_normalise_table(table, root, c) == false) + return false; + break; + default: + assert(0); + } + } + + return true; +} diff --git a/content/handlers/html/box_normalise.h b/content/handlers/html/box_normalise.h new file mode 100644 index 000000000..60f259189 --- /dev/null +++ b/content/handlers/html/box_normalise.h @@ -0,0 +1,50 @@ +/* + * 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 normalise interface. + */ + +#ifndef NETSURF_HTML_BOX_NORMALISE_H +#define NETSURF_HTML_BOX_NORMALISE_H + +/** + * Ensure the box tree is correctly nested by adding and removing nodes. + * + * \param block box of type BLOCK, INLINE_BLOCK, or TABLE_CELL + * \param root root box of document + * \param c content of boxes + * \return true on success, false on memory exhaustion + * + * The tree is modified to satisfy the following: + * \code + * parent permitted child nodes + * BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE + * INLINE_CONTAINER INLINE, INLINE_BLOCK, FLOAT_LEFT, FLOAT_RIGHT, BR, TEXT + * INLINE, TEXT 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 + */ +bool box_normalise_block(struct box *block, const struct box *root, struct html_content *c); + +#endif diff --git a/content/handlers/html/html.c b/content/handlers/html/html.c index e445457ca..9b4785839 100644 --- a/content/handlers/html/html.c +++ b/content/handlers/html/html.c @@ -60,6 +60,7 @@ #include "html/html_internal.h" #include "html/interaction.h" #include "html/box.h" +#include "html/box_construct.h" #include "html/form_internal.h" #include "html/imagemap.h" #include "html/layout.h" @@ -212,7 +213,7 @@ static void html_box_convert_done(html_content *c, bool success) dom_exception exc; /* returned by libdom functions */ dom_node *html; - NSLOG(netsurf, INFO, "Done XML to box (%p)", c); + NSLOG(netsurf, INFO, "DOM to box conversion complete (content %p)", c); c->box_conversion_context = NULL; diff --git a/content/handlers/html/imagemap.c b/content/handlers/html/imagemap.c index 0c3576842..37095e21d 100644 --- a/content/handlers/html/imagemap.c +++ b/content/handlers/html/imagemap.c @@ -36,6 +36,7 @@ #include "content/hlcache.h" #include "html/box.h" +#include "html/box_construct.h" #include "html/html_internal.h" #include "html/imagemap.h" -- cgit v1.2.3