diff options
Diffstat (limited to 'content/handlers/html')
-rw-r--r-- | content/handlers/html/Makefile | 1 | ||||
-rw-r--r-- | content/handlers/html/box.h | 4 | ||||
-rw-r--r-- | content/handlers/html/box_construct.c | 121 | ||||
-rw-r--r-- | content/handlers/html/box_inspect.c | 13 | ||||
-rw-r--r-- | content/handlers/html/box_inspect.h | 12 | ||||
-rw-r--r-- | content/handlers/html/box_normalise.c | 202 | ||||
-rw-r--r-- | content/handlers/html/box_normalise.h | 9 | ||||
-rw-r--r-- | content/handlers/html/box_special.c | 37 | ||||
-rw-r--r-- | content/handlers/html/css.c | 11 | ||||
-rw-r--r-- | content/handlers/html/dom_event.c | 12 | ||||
-rw-r--r-- | content/handlers/html/form.c | 31 | ||||
-rw-r--r-- | content/handlers/html/html.c | 24 | ||||
-rw-r--r-- | content/handlers/html/interaction.c | 10 | ||||
-rw-r--r-- | content/handlers/html/layout.c | 770 | ||||
-rw-r--r-- | content/handlers/html/layout_flex.c | 1117 | ||||
-rw-r--r-- | content/handlers/html/layout_internal.h | 738 | ||||
-rw-r--r-- | content/handlers/html/redraw_border.c | 9 | ||||
-rw-r--r-- | content/handlers/html/script.c | 1 | ||||
-rw-r--r-- | content/handlers/html/table.c | 49 |
19 files changed, 2474 insertions, 697 deletions
diff --git a/content/handlers/html/Makefile b/content/handlers/html/Makefile index 8bb329b76..e41cc1d22 100644 --- a/content/handlers/html/Makefile +++ b/content/handlers/html/Makefile @@ -16,6 +16,7 @@ S_HTML := box_construct.c \ imagemap.c \ interaction.c \ layout.c \ + layout_flex.c \ object.c \ redraw.c \ redraw_border.c \ diff --git a/content/handlers/html/box.h b/content/handlers/html/box.h index 1059556e6..df2b99d87 100644 --- a/content/handlers/html/box.h +++ b/content/handlers/html/box.h @@ -66,7 +66,9 @@ typedef enum { BOX_BR, BOX_TEXT, BOX_INLINE_END, - BOX_NONE + BOX_NONE, + BOX_FLEX, + BOX_INLINE_FLEX, } box_type; diff --git a/content/handlers/html/box_construct.c b/content/handlers/html/box_construct.c index 12d9df87c..8519c2b1d 100644 --- a/content/handlers/html/box_construct.c +++ b/content/handlers/html/box_construct.c @@ -86,28 +86,30 @@ struct box_construct_props { static const content_type image_types = CONTENT_IMAGE; -/** - * mapping from CSS display to box type this table must be in sync - * with libcss' css_display enum - */ +/* mapping from CSS display to box type + * this table must be in sync with libcss' css_display enum */ static const box_type box_map[] = { - 0, /* CSS_DISPLAY_INHERIT, */ - BOX_INLINE, /* CSS_DISPLAY_INLINE, */ - BOX_BLOCK, /* CSS_DISPLAY_BLOCK, */ - BOX_BLOCK, /* CSS_DISPLAY_LIST_ITEM, */ - BOX_INLINE, /* CSS_DISPLAY_RUN_IN, */ - BOX_INLINE_BLOCK, /* CSS_DISPLAY_INLINE_BLOCK, */ - BOX_TABLE, /* CSS_DISPLAY_TABLE, */ - BOX_TABLE, /* CSS_DISPLAY_INLINE_TABLE, */ - BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_ROW_GROUP, */ - BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_HEADER_GROUP, */ - BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_FOOTER_GROUP, */ - BOX_TABLE_ROW, /* CSS_DISPLAY_TABLE_ROW, */ - BOX_NONE, /* CSS_DISPLAY_TABLE_COLUMN_GROUP, */ - BOX_NONE, /* CSS_DISPLAY_TABLE_COLUMN, */ - BOX_TABLE_CELL, /* CSS_DISPLAY_TABLE_CELL, */ - BOX_INLINE, /* CSS_DISPLAY_TABLE_CAPTION, */ - BOX_NONE /* CSS_DISPLAY_NONE */ + BOX_BLOCK, /* CSS_DISPLAY_INHERIT */ + BOX_INLINE, /* CSS_DISPLAY_INLINE */ + BOX_BLOCK, /* CSS_DISPLAY_BLOCK */ + BOX_BLOCK, /* CSS_DISPLAY_LIST_ITEM */ + BOX_INLINE, /* CSS_DISPLAY_RUN_IN */ + BOX_INLINE_BLOCK, /* CSS_DISPLAY_INLINE_BLOCK */ + BOX_TABLE, /* CSS_DISPLAY_TABLE */ + BOX_TABLE, /* CSS_DISPLAY_INLINE_TABLE */ + BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_ROW_GROUP */ + BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_HEADER_GROUP */ + BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_FOOTER_GROUP */ + BOX_TABLE_ROW, /* CSS_DISPLAY_TABLE_ROW */ + BOX_NONE, /* CSS_DISPLAY_TABLE_COLUMN_GROUP */ + BOX_NONE, /* CSS_DISPLAY_TABLE_COLUMN */ + BOX_TABLE_CELL, /* CSS_DISPLAY_TABLE_CELL */ + BOX_INLINE, /* CSS_DISPLAY_TABLE_CAPTION */ + BOX_NONE, /* CSS_DISPLAY_NONE */ + BOX_FLEX, /* CSS_DISPLAY_FLEX */ + BOX_INLINE_FLEX, /* CSS_DISPLAY_INLINE_FLEX */ + BOX_BLOCK, /* CSS_DISPLAY_GRID */ + BOX_INLINE_BLOCK, /* CSS_DISPLAY_INLINE_GRID */ }; @@ -142,7 +144,6 @@ static inline bool box_is_root(dom_node *n) return true; } - /** * Extract transient construction properties * @@ -248,16 +249,19 @@ box_get_style(html_content *c, const css_computed_style *root_style, dom_node *n) { - dom_string *s; - dom_exception err; + dom_string *s = NULL; css_stylesheet *inline_style = NULL; css_select_results *styles; nscss_select_ctx ctx; /* Firstly, construct inline stylesheet, if any */ - err = dom_element_get_attribute(n, corestring_dom_style, &s); - if (err != DOM_NO_ERR) - return NULL; + if (nsoption_bool(author_level_css)) { + dom_exception err; + err = dom_element_get_attribute(n, corestring_dom_style, &s); + if (err != DOM_NO_ERR) { + return NULL; + } + } if (s != NULL) { inline_style = nscss_create_inline_style( @@ -438,6 +442,23 @@ box_construct_marker(struct box *box, return true; } +static inline bool box__style_is_float(const struct box *box) +{ + return css_computed_float(box->style) == CSS_FLOAT_LEFT || + css_computed_float(box->style) == CSS_FLOAT_RIGHT; +} + +static inline bool box__is_flex(const struct box *box) +{ + return box->type == BOX_FLEX || box->type == BOX_INLINE_FLEX; +} + +static inline bool box__containing_block_is_flex( + const struct box_construct_props *props) +{ + return props->containing_block != NULL && + box__is_flex(props->containing_block); +} /** * Construct the box tree for an XML element. @@ -451,6 +472,7 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children) { dom_string *title0, *s; lwc_string *id = NULL; + enum css_display_e css_display; struct box *box = NULL, *old_box; css_select_results *styles = NULL; lwc_string *bgimage_uri; @@ -549,16 +571,15 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children) dom_string_unref(s); } + css_display = ns_computed_display_static(box->style); + /* Set box type from computed display */ if ((css_computed_position(box->style) == CSS_POSITION_ABSOLUTE || - css_computed_position(box->style) == - CSS_POSITION_FIXED) && - (ns_computed_display_static(box->style) == - CSS_DISPLAY_INLINE || - ns_computed_display_static(box->style) == - CSS_DISPLAY_INLINE_BLOCK || - ns_computed_display_static(box->style) == - CSS_DISPLAY_INLINE_TABLE)) { + css_computed_position(box->style) == CSS_POSITION_FIXED) && + (css_display == CSS_DISPLAY_INLINE || + css_display == CSS_DISPLAY_INLINE_BLOCK || + css_display == CSS_DISPLAY_INLINE_TABLE || + css_display == CSS_DISPLAY_INLINE_FLEX)) { /* Special case for absolute positioning: make absolute inlines * into inline block so that the boxes are constructed in an * inline container as if they were not absolutely positioned. @@ -572,6 +593,21 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children) /* Normal mapping */ box->type = box_map[ns_computed_display(box->style, props.node_is_root)]; + + if (props.containing_block->type == BOX_FLEX || + props.containing_block->type == BOX_INLINE_FLEX) { + /* Blockification */ + switch (box->type) { + case BOX_INLINE_FLEX: + box->type = BOX_FLEX; + break; + case BOX_INLINE_BLOCK: + box->type = BOX_BLOCK; + break; + default: + break; + } + } } if (convert_special_elements(ctx->n, @@ -587,10 +623,9 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children) box->styles->styles[CSS_PSEUDO_ELEMENT_BEFORE]); } - if (box->type == BOX_NONE || - (ns_computed_display(box->style, - props.node_is_root) == CSS_DISPLAY_NONE && - props.node_is_root == false)) { + if (box->type == BOX_NONE || (ns_computed_display(box->style, + props.node_is_root) == CSS_DISPLAY_NONE && + props.node_is_root == false)) { css_select_results_destroy(styles); box->styles = NULL; box->style = NULL; @@ -625,8 +660,9 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children) (box->type == BOX_INLINE || box->type == BOX_BR || box->type == BOX_INLINE_BLOCK || - css_computed_float(box->style) == CSS_FLOAT_LEFT || - css_computed_float(box->style) == CSS_FLOAT_RIGHT) && + box->type == BOX_INLINE_FLEX || + (box__style_is_float(box) && + !box__containing_block_is_flex(&props))) && props.node_is_root == false) { /* Found an inline child of a block without a current container * (i.e. this box is the first child of its parent, or was @@ -674,6 +710,7 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children) box->flags |= CONVERT_CHILDREN; if (box->type == BOX_INLINE || box->type == BOX_BR || + box->type == BOX_INLINE_FLEX || box->type == BOX_INLINE_BLOCK) { /* Inline container must exist, as we'll have * created it above if it didn't */ @@ -690,6 +727,7 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children) } if (props.node_is_root == false && + box__containing_block_is_flex(&props) == false && (css_computed_float(box->style) == CSS_FLOAT_LEFT || css_computed_float(box->style) == @@ -1342,7 +1380,6 @@ struct box *box_for_node(dom_node *n) return box; } - /* exported function documented in html/box_construct.h */ bool box_extract_link(const html_content *content, diff --git a/content/handlers/html/box_inspect.c b/content/handlers/html/box_inspect.c index b4b13940d..6591b6446 100644 --- a/content/handlers/html/box_inspect.c +++ b/content/handlers/html/box_inspect.c @@ -25,6 +25,7 @@ #include <stdio.h> #include <dom/dom.h> +#include "utils/utils.h" #include "utils/nsurl.h" #include "utils/errors.h" #include "netsurf/types.h" @@ -212,7 +213,7 @@ box_move_xy(struct box *b, enum box_walk_dir dir, int *x, int *y) rb = b; break; } - /* fall through */ + fallthrough; case BOX_WALK_NEXT_SIBLING: do { @@ -660,7 +661,7 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style) 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) ", + fprintf(stream, "desc(%i %i %i %i) ", box->descendant_x0, box->descendant_y0, box->descendant_x1, box->descendant_y1); @@ -724,6 +725,14 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style) fprintf(stream, "TEXT "); break; + case BOX_FLEX: + fprintf(stream, "FLEX "); + break; + + case BOX_INLINE_FLEX: + fprintf(stream, "INLINE_FLEX "); + break; + default: fprintf(stream, "Unknown box type "); } diff --git a/content/handlers/html/box_inspect.h b/content/handlers/html/box_inspect.h index b9161f148..a218326d8 100644 --- a/content/handlers/html/box_inspect.h +++ b/content/handlers/html/box_inspect.h @@ -139,5 +139,17 @@ static inline bool box_is_first_child(struct box *b) return (b->parent == NULL || b == b->parent->children); } +static inline unsigned box_count_children(const struct box *b) +{ + const struct box *c = b->children; + unsigned count = 0; + + while (c != NULL) { + count++; + c = c->next; + } + + return count; +} #endif diff --git a/content/handlers/html/box_normalise.c b/content/handlers/html/box_normalise.c index 1b6a345d5..8f25b031f 100644 --- a/content/handlers/html/box_normalise.c +++ b/content/handlers/html/box_normalise.c @@ -177,6 +177,7 @@ box_normalise_table_row(struct box *row, return false; cell = child; break; + case BOX_FLEX: case BOX_BLOCK: case BOX_INLINE_CONTAINER: case BOX_TABLE: @@ -211,6 +212,7 @@ box_normalise_table_row(struct box *row, cell->prev = child->prev; while (child != NULL && ( + child->type == BOX_FLEX || child->type == BOX_BLOCK || child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || @@ -238,6 +240,7 @@ box_normalise_table_row(struct box *row, break; case BOX_INLINE: case BOX_INLINE_END: + case BOX_INLINE_FLEX: case BOX_INLINE_BLOCK: case BOX_FLOAT_LEFT: case BOX_FLOAT_RIGHT: @@ -314,6 +317,7 @@ box_normalise_table_row_group(struct box *row_group, c) == false) return false; break; + case BOX_FLEX: case BOX_BLOCK: case BOX_INLINE_CONTAINER: case BOX_TABLE: @@ -348,6 +352,7 @@ box_normalise_table_row_group(struct box *row_group, row->prev = child->prev; while (child != NULL && ( + child->type == BOX_FLEX || child->type == BOX_BLOCK || child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || @@ -377,6 +382,7 @@ box_normalise_table_row_group(struct box *row_group, break; case BOX_INLINE: case BOX_INLINE_END: + case BOX_INLINE_FLEX: case BOX_INLINE_BLOCK: case BOX_FLOAT_LEFT: case BOX_FLOAT_RIGHT: @@ -648,6 +654,7 @@ box_normalise_table(struct box *table, const struct box *root, html_content * c) return false; } break; + case BOX_FLEX: case BOX_BLOCK: case BOX_INLINE_CONTAINER: case BOX_TABLE: @@ -686,6 +693,7 @@ box_normalise_table(struct box *table, const struct box *root, html_content * c) row_group->prev = child->prev; while (child != NULL && ( + child->type == BOX_FLEX || child->type == BOX_BLOCK || child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || @@ -716,6 +724,7 @@ box_normalise_table(struct box *table, const struct box *root, html_content * c) break; case BOX_INLINE: case BOX_INLINE_END: + case BOX_INLINE_FLEX: case BOX_INLINE_BLOCK: case BOX_FLOAT_LEFT: case BOX_FLOAT_RIGHT: @@ -806,6 +815,181 @@ box_normalise_table(struct box *table, const struct box *root, html_content * c) return true; } +static bool box_normalise_flex( + struct box *flex_container, + const struct box *root, + html_content *c) +{ + struct box *child; + struct box *next_child; + struct box *implied_flex_item; + css_computed_style *style; + nscss_select_ctx ctx; + + assert(flex_container != NULL); + assert(root != NULL); + + ctx.root_style = root->style; + +#ifdef BOX_NORMALISE_DEBUG + NSLOG(netsurf, INFO, "flex_container %p, flex_container->type %u", + flex_container, flex_container->type); +#endif + + assert(flex_container->type == BOX_FLEX || + flex_container->type == BOX_INLINE_FLEX); + + for (child = flex_container->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_FLEX: + /* ok */ + if (box_normalise_flex(child, root, c) == false) + return false; + break; + case BOX_BLOCK: + /* ok */ + if (box_normalise_block(child, root, c) == false) + return false; + break; + case BOX_INLINE_CONTAINER: + /* insert implied flex item */ + assert(flex_container->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, &c->unit_len_ctx, + flex_container->style); + if (style == NULL) + return false; + + implied_flex_item = box_create(NULL, style, true, + flex_container->href, + flex_container->target, + NULL, NULL, c->bctx); + if (implied_flex_item == NULL) { + css_computed_style_destroy(style); + return false; + } + implied_flex_item->type = BOX_BLOCK; + + if (child->prev == NULL) + flex_container->children = implied_flex_item; + else + child->prev->next = implied_flex_item; + + implied_flex_item->prev = child->prev; + + while (child != NULL && + child->type == BOX_INLINE_CONTAINER) { + box_add_child(implied_flex_item, child); + + next_child = child->next; + child->next = NULL; + child = next_child; + } + + implied_flex_item->last->next = NULL; + implied_flex_item->next = next_child = child; + if (implied_flex_item->next != NULL) + implied_flex_item->next->prev = implied_flex_item; + else + flex_container->last = implied_flex_item; + implied_flex_item->parent = flex_container; + + if (box_normalise_block(implied_flex_item, + 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_FLEX: + 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(flex_container->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, &c->unit_len_ctx, + flex_container->style); + if (style == NULL) + return false; + + implied_flex_item = box_create(NULL, style, true, + flex_container->href, + flex_container->target, + NULL, NULL, c->bctx); + if (implied_flex_item == NULL) { + css_computed_style_destroy(style); + return false; + } + implied_flex_item->type = BOX_TABLE; + + if (child->prev == NULL) + flex_container->children = implied_flex_item; + else + child->prev->next = implied_flex_item; + + implied_flex_item->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(implied_flex_item, child); + + next_child = child->next; + child->next = NULL; + child = next_child; + } + + implied_flex_item->last->next = NULL; + implied_flex_item->next = next_child = child; + if (implied_flex_item->next != NULL) + implied_flex_item->next->prev = implied_flex_item; + else + flex_container->last = implied_flex_item; + implied_flex_item->parent = flex_container; + + if (box_normalise_table(implied_flex_item, + root, c) == false) + return false; + break; + default: + assert(0); + } + } + + return true; +} static bool box_normalise_inline_container(struct box *cont, @@ -836,6 +1020,11 @@ box_normalise_inline_container(struct box *cont, if (box_normalise_block(child, root, c) == false) return false; break; + case BOX_INLINE_FLEX: + /* ok */ + if (box_normalise_flex(child, root, c) == false) + return false; + break; case BOX_FLOAT_LEFT: case BOX_FLOAT_RIGHT: /* ok */ @@ -852,6 +1041,11 @@ box_normalise_inline_container(struct box *cont, c) == false) return false; break; + case BOX_FLEX: + if (box_normalise_flex(child->children, root, + c) == false) + return false; + break; default: assert(0); } @@ -870,6 +1064,7 @@ box_normalise_inline_container(struct box *cont, box_free(child); } break; + case BOX_FLEX: case BOX_BLOCK: case BOX_INLINE_CONTAINER: case BOX_TABLE: @@ -888,7 +1083,6 @@ box_normalise_inline_container(struct box *cont, return true; } - /* Exported function documented in html/box_normalise.h */ bool box_normalise_block(struct box *block, const struct box *root, html_content *c) @@ -920,6 +1114,11 @@ box_normalise_block(struct box *block, const struct box *root, html_content *c) next_child = child->next; /* child may be destroyed */ switch (child->type) { + case BOX_FLEX: + /* ok */ + if (box_normalise_flex(child, root, c) == false) + return false; + break; case BOX_BLOCK: /* ok */ if (box_normalise_block(child, root, c) == false) @@ -935,6 +1134,7 @@ box_normalise_block(struct box *block, const struct box *root, html_content *c) break; case BOX_INLINE: case BOX_INLINE_END: + case BOX_INLINE_FLEX: case BOX_INLINE_BLOCK: case BOX_FLOAT_LEFT: case BOX_FLOAT_RIGHT: diff --git a/content/handlers/html/box_normalise.h b/content/handlers/html/box_normalise.h index 591feab8d..377cd9019 100644 --- a/content/handlers/html/box_normalise.h +++ b/content/handlers/html/box_normalise.h @@ -50,14 +50,15 @@ * 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 + * BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE, FLEX + * FLEX, INLINE_FLEX BLOCK, INLINE_CONTAINER, TABLE, FLEX + * INLINE_CONTAINER INLINE, INLINE_BLOCK, FLOAT_LEFT, FLOAT_RIGHT, BR, TEXT, INLINE_FLEX * 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 + * TABLE_CELL BLOCK, INLINE_CONTAINER, TABLE, FLEX (same as BLOCK) + * FLOAT_(LEFT|RIGHT) exactly 1 BLOCK, TABLE or FLEX * \endcode */ bool box_normalise_block(struct box *block, const struct box *root, struct html_content *c); diff --git a/content/handlers/html/box_special.c b/content/handlers/html/box_special.c index f761557e0..db3c4126d 100644 --- a/content/handlers/html/box_special.c +++ b/content/handlers/html/box_special.c @@ -560,8 +560,18 @@ static bool box_input_text(html_content *html, struct box *box, struct dom_node *node) { struct box *inline_container, *inline_box; + uint8_t display = css_computed_display_static(box->style); - box->type = BOX_INLINE_BLOCK; + switch (display) { + case CSS_DISPLAY_GRID: + case CSS_DISPLAY_FLEX: + case CSS_DISPLAY_BLOCK: + box->type = BOX_BLOCK; + break; + default: + box->type = BOX_INLINE_BLOCK; + break; + } inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, html->bctx); if (!inline_container) @@ -825,8 +835,8 @@ box_canvas(dom_node *n, } *convert_children = false; - if (box->style && - ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE) + if (box->style && ns_computed_display(box->style, + box_is_root(n)) == CSS_DISPLAY_NONE) return true; /* This is replaced content */ @@ -854,8 +864,8 @@ box_embed(dom_node *n, dom_string *src; dom_exception err; - if (box->style && - ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE) + if (box->style && ns_computed_display(box->style, + box_is_root(n)) == CSS_DISPLAY_NONE) return true; params = talloc(content->bctx, struct object_params); @@ -1025,8 +1035,8 @@ box_iframe(dom_node *n, struct content_html_iframe *iframe; int i; - if (box->style && - ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE) + if (box->style && ns_computed_display(box->style, + box_is_root(n)) == CSS_DISPLAY_NONE) return true; if (box->style && @@ -1154,8 +1164,8 @@ box_image(dom_node *n, 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) + if (box->style && ns_computed_display(box->style, + box_is_root(n)) == CSS_DISPLAY_NONE) return true; /* handle alt text */ @@ -1322,10 +1332,9 @@ box_input(dom_node *n, corestring_lwc_image)) { gadget->type = GADGET_IMAGE; - if (box->style && - ns_computed_display(box->style, + if (box->style && ns_computed_display(box->style, box_is_root(n)) != CSS_DISPLAY_NONE && - nsoption_bool(foreground_images) == true) { + nsoption_bool(foreground_images) == true) { dom_string *s; err = dom_element_get_attribute(n, corestring_dom_src, &s); @@ -1405,8 +1414,8 @@ box_object(dom_node *n, dom_node *c; dom_exception err; - if (box->style && - ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE) + 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) == diff --git a/content/handlers/html/css.c b/content/handlers/html/css.c index 2434b1783..0bc38844f 100644 --- a/content/handlers/html/css.c +++ b/content/handlers/html/css.c @@ -33,6 +33,7 @@ #include "utils/nsoption.h" #include "utils/corestrings.h" #include "utils/log.h" +#include "netsurf/inttypes.h" #include "netsurf/misc.h" #include "netsurf/content.h" #include "content/hlcache.h" @@ -173,7 +174,7 @@ html_stylesheet_from_domnode(html_content *c, dom_string_unref(style); - snprintf(urlbuf, sizeof(urlbuf), "x-ns-css:%u", key); + snprintf(urlbuf, sizeof(urlbuf), "x-ns-css:%"PRIu32"", key); error = nsurl_create(urlbuf, &url); if (error != NSERROR_OK) { @@ -395,16 +396,20 @@ bool html_css_process_link(html_content *htmlc, dom_node *node) if (exc != DOM_NO_ERR || rel == NULL) return true; - if (strcasestr(dom_string_data(rel), "stylesheet") == 0) { + if (strcasestr(dom_string_data(rel), "stylesheet") == NULL) { dom_string_unref(rel); return true; - } else if (strcasestr(dom_string_data(rel), "alternate") != 0) { + } else if (strcasestr(dom_string_data(rel), "alternate") != NULL) { /* Ignore alternate stylesheets */ dom_string_unref(rel); return true; } dom_string_unref(rel); + if (nsoption_bool(author_level_css) == false) { + return true; + } + /* type='text/css' or not present */ exc = dom_element_get_attribute(node, corestring_dom_type, &type_attr); if (exc == DOM_NO_ERR && type_attr != NULL) { diff --git a/content/handlers/html/dom_event.c b/content/handlers/html/dom_event.c index 36a020b6d..d42882515 100644 --- a/content/handlers/html/dom_event.c +++ b/content/handlers/html/dom_event.c @@ -25,6 +25,7 @@ #include <string.h> #include "utils/config.h" +#include "utils/utils.h" #include "utils/corestrings.h" #include "utils/nsoption.h" #include "utils/log.h" @@ -622,7 +623,9 @@ dom_default_action_DOMNodeInserted_cb(struct dom_event *evt, void *pw) break; case DOM_HTML_ELEMENT_TYPE_STYLE: - html_css_process_style(htmlc, (dom_node *)node); + if (nsoption_bool(author_level_css)) { + html_css_process_style(htmlc, (dom_node *)node); + } break; case DOM_HTML_ELEMENT_TYPE_SCRIPT: @@ -689,6 +692,7 @@ dom_default_action_DOMNodeInsertedIntoDocument_cb(struct dom_event *evt, switch (tag_type) { case DOM_HTML_ELEMENT_TYPE_SCRIPT: dom_SCRIPT_showed_up(htmlc, (dom_html_script_element *) node); + fallthrough; default: break; } @@ -730,11 +734,15 @@ dom_default_action_DOMSubtreeModified_cb(struct dom_event *evt, void *pw) switch (tag_type) { case DOM_HTML_ELEMENT_TYPE_STYLE: - html_css_update_style(htmlc, (dom_node *)node); + if (nsoption_bool(author_level_css)) { + html_css_update_style(htmlc, + (dom_node *)node); + } break; case DOM_HTML_ELEMENT_TYPE_TEXTAREA: case DOM_HTML_ELEMENT_TYPE_INPUT: html_texty_element_update(htmlc, (dom_node *)node); + fallthrough; default: break; } diff --git a/content/handlers/html/form.c b/content/handlers/html/form.c index 97ec19518..9b6768a56 100644 --- a/content/handlers/html/form.c +++ b/content/handlers/html/form.c @@ -41,6 +41,7 @@ #include "utils/utf8.h" #include "utils/ascii.h" #include "netsurf/browser_window.h" +#include "netsurf/inttypes.h" #include "netsurf/mouse.h" #include "netsurf/plotters.h" #include "netsurf/misc.h" @@ -486,7 +487,7 @@ form_dom_to_data_select(dom_html_select_element *select_element, &option_element); if (exp != DOM_NO_ERR) { NSLOG(netsurf, INFO, - "Could not get options item %d", option_index); + "Could not get options item %"PRId32, option_index); res = NSERROR_DOM; } else { res = form_dom_to_data_select_option( @@ -1100,7 +1101,7 @@ form_dom_to_data(struct form *form, exp = dom_html_collection_item(elements, element_idx, &element); if (exp != DOM_NO_ERR) { NSLOG(netsurf, INFO, - "retrieving form element %d failed with %d", + "retrieving form element %"PRIu32" failed with %d", element_idx, exp); res = NSERROR_DOM; goto form_dom_to_data_error; @@ -1110,7 +1111,7 @@ form_dom_to_data(struct form *form, exp = dom_node_get_node_name(element, &nodename); if (exp != DOM_NO_ERR) { NSLOG(netsurf, INFO, - "getting element node name %d failed with %d", + "getting element node name %"PRIu32" failed with %d", element_idx, exp); dom_node_unref(element); res = NSERROR_DOM; @@ -2001,15 +2002,33 @@ void form_radio_set(struct form_control *radio) if (radio->selected) return; - for (control = radio->form->controls; control; - control = control->next) { + /* Clear selected state for other controls in + * the same radio button group */ + for (control = radio->form->controls; + control != NULL; + control = control->next) { + /* Only interested in radio inputs */ if (control->type != GADGET_RADIO) continue; + + /* Ignore ourself */ if (control == radio) continue; - if (strcmp(control->name, radio->name) != 0) + + /* Ignore inputs where: + * a) this or the other control have no name attribute + * b) this or the other control have an empty name attribute + * c) the control names do not match + */ + if ((control->name == NULL) || + (radio->name == NULL) || + (control->name[0] == '\0') || + (radio->name[0] == '\0') || + strcmp(control->name, radio->name) != 0) continue; + /* Other control is in the same radio button group: clear its + * selected state */ if (control->selected) { control->selected = false; dom_html_input_element_set_checked(control->node, false); diff --git a/content/handlers/html/html.c b/content/handlers/html/html.c index c6a28724c..82f5f1388 100644 --- a/content/handlers/html/html.c +++ b/content/handlers/html/html.c @@ -123,7 +123,7 @@ bool fire_generic_dom_event(dom_string *type, dom_node *target, return false; } NSLOG(netsurf, INFO, "Dispatching '%*s' against %p", - dom_string_length(type), dom_string_data(type), target); + (int)dom_string_length(type), dom_string_data(type), target); result = fire_dom_event(evt, target); dom_event_unref(evt); return result; @@ -200,7 +200,7 @@ bool fire_dom_keyboard_event(dom_string *type, dom_node *target, } NSLOG(netsurf, INFO, "Dispatching '%*s' against %p", - dom_string_length(type), dom_string_data(type), target); + (int)dom_string_length(type), dom_string_data(type), target); result = fire_dom_event((dom_event *) evt, target); dom_event_unref(evt); @@ -458,6 +458,8 @@ html_create_html_data(html_content *c, const http_parameter *params) dom_hubbub_error error; dom_exception err; void *old_node_data; + const char *prefer_color_mode = (nsoption_bool(prefer_dark_mode)) ? + "dark" : "light"; c->parser = NULL; c->parse_completed = false; @@ -505,6 +507,13 @@ html_create_html_data(html_content *c, const http_parameter *params) return NSERROR_NOMEM; } + if (lwc_intern_string(prefer_color_mode, strlen(prefer_color_mode), + &c->media.prefers_color_scheme) != lwc_error_ok) { + lwc_string_unref(c->universal); + c->universal = NULL; + return NSERROR_NOMEM; + } + c->sel = selection_create((struct content *)c); nerror = http_parameter_list_find_item(params, corestring_lwc_charset, &charset); @@ -516,6 +525,8 @@ html_create_html_data(html_content *c, const http_parameter *params) if (c->encoding == NULL) { lwc_string_unref(c->universal); c->universal = NULL; + lwc_string_unref(c->media.prefers_color_scheme); + c->media.prefers_color_scheme = NULL; return NSERROR_NOMEM; } @@ -552,6 +563,8 @@ html_create_html_data(html_content *c, const http_parameter *params) lwc_string_unref(c->universal); c->universal = NULL; + lwc_string_unref(c->media.prefers_color_scheme); + c->media.prefers_color_scheme = NULL; return libdom_hubbub_error_to_nserror(error); } @@ -568,6 +581,8 @@ html_create_html_data(html_content *c, const http_parameter *params) lwc_string_unref(c->universal); c->universal = NULL; + lwc_string_unref(c->media.prefers_color_scheme); + c->media.prefers_color_scheme = NULL; NSLOG(netsurf, INFO, "Unable to set user data."); return NSERROR_DOM; @@ -1274,6 +1289,11 @@ static void html_destroy(struct content *c) html->universal = NULL; } + if (html->media.prefers_color_scheme != NULL) { + lwc_string_unref(html->media.prefers_color_scheme); + html->media.prefers_color_scheme = NULL; + } + /* Free stylesheets */ html_css_free_stylesheets(html); diff --git a/content/handlers/html/interaction.c b/content/handlers/html/interaction.c index 026ef1ee7..0a843e026 100644 --- a/content/handlers/html/interaction.c +++ b/content/handlers/html/interaction.c @@ -899,7 +899,7 @@ gadget_mouse_action(html_content *html, } free(oldcoords); } - /* Fall through */ + fallthrough; case GADGET_SUBMIT: if (mas->gadget.control->form) { @@ -1465,7 +1465,7 @@ html_mouse_action(struct content *c, int x, int y) { html_content *html = (html_content *)c; - nserror res; + nserror res = NSERROR_OK; /* handle open select menu */ if (html->visible_select_menu != NULL) { @@ -1493,7 +1493,7 @@ html_mouse_action(struct content *c, break; case HTML_DRAG_NONE: - res = mouse_action_drag_none(html, bw, mouse, x, y); + res = mouse_action_drag_none(html, bw, mouse, x, y); break; default: @@ -1669,7 +1669,7 @@ void html_set_drag_type(html_content *html, html_drag_type drag_type, case HTML_DRAG_SELECTION: assert(drag_owner.no_owner == true); - /* Fall through */ + fallthrough; case HTML_DRAG_TEXTAREA_SELECTION: case HTML_DRAG_CONTENT_SELECTION: msg_data.drag.type = CONTENT_DRAG_SELECTION; @@ -1800,7 +1800,7 @@ void html_set_selection(html_content *html, html_selection_type selection_type, break; case HTML_SELECTION_SELF: assert(selection_owner.none == false); - /* fall through */ + fallthrough; case HTML_SELECTION_TEXTAREA: case HTML_SELECTION_CONTENT: msg_data.selection.selection = true; diff --git a/content/handlers/html/layout.c b/content/handlers/html/layout.c index c06fdf690..76ce24df5 100644 --- a/content/handlers/html/layout.c +++ b/content/handlers/html/layout.c @@ -67,24 +67,11 @@ #include "html/font.h" #include "html/form_internal.h" #include "html/layout.h" +#include "html/layout_internal.h" #include "html/table.h" -#define AUTO INT_MIN - -/* Fixed point percentage (a) of an integer (b), to an integer */ -#define FPCT_OF_INT_TOINT(a, b) (FIXTOINT(FDIV((a * b), F_100))) - -typedef uint8_t (*css_len_func)( - const css_computed_style *style, - css_fixed *length, css_unit *unit); -typedef uint8_t (*css_border_style_func)( - const css_computed_style *style); -typedef uint8_t (*css_border_color_func)( - const css_computed_style *style, - css_color *color); - /** Array of per-side access functions for computed style margins. */ -static const css_len_func margin_funcs[4] = { +const css_len_func margin_funcs[4] = { [TOP] = css_computed_margin_top, [RIGHT] = css_computed_margin_right, [BOTTOM] = css_computed_margin_bottom, @@ -92,7 +79,7 @@ static const css_len_func margin_funcs[4] = { }; /** Array of per-side access functions for computed style paddings. */ -static const css_len_func padding_funcs[4] = { +const css_len_func padding_funcs[4] = { [TOP] = css_computed_padding_top, [RIGHT] = css_computed_padding_right, [BOTTOM] = css_computed_padding_bottom, @@ -100,7 +87,7 @@ static const css_len_func padding_funcs[4] = { }; /** Array of per-side access functions for computed style border_widths. */ -static const css_len_func border_width_funcs[4] = { +const css_len_func border_width_funcs[4] = { [TOP] = css_computed_border_top_width, [RIGHT] = css_computed_border_right_width, [BOTTOM] = css_computed_border_bottom_width, @@ -108,7 +95,7 @@ static const css_len_func border_width_funcs[4] = { }; /** Array of per-side access functions for computed style border styles. */ -static const css_border_style_func border_style_funcs[4] = { +const css_border_style_func border_style_funcs[4] = { [TOP] = css_computed_border_top_style, [RIGHT] = css_computed_border_right_style, [BOTTOM] = css_computed_border_bottom_style, @@ -116,7 +103,7 @@ static const css_border_style_func border_style_funcs[4] = { }; /** Array of per-side access functions for computed style border colors. */ -static const css_border_color_func border_color_funcs[4] = { +const css_border_color_func border_color_funcs[4] = { [TOP] = css_computed_border_top_color, [RIGHT] = css_computed_border_right_color, [BOTTOM] = css_computed_border_bottom_color, @@ -124,10 +111,6 @@ static const css_border_color_func border_color_funcs[4] = { }; /* forward declaration to break cycles */ -static bool layout_block_context( - struct box *block, - int viewport_height, - html_content *content); static void layout_minmax_block( struct box *block, const struct gui_layout_table *font_func, @@ -263,75 +246,6 @@ static int layout_text_indent( /** - * Determine width of margin, borders, and padding on one side of a box. - * - * \param unit_len_ctx CSS length conversion context for document - * \param style style to measure - * \param side side of box to measure - * \param margin whether margin width is required - * \param border whether border width is required - * \param padding whether padding width is required - * \param fixed increased by sum of fixed margin, border, and padding - * \param frac increased by sum of fractional margin and padding - */ -static void -calculate_mbp_width(const css_unit_ctx *unit_len_ctx, - const css_computed_style *style, - unsigned int side, - bool margin, - bool border, - bool padding, - int *fixed, - float *frac) -{ - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - assert(style); - - /* margin */ - if (margin) { - enum css_margin_e type; - - type = margin_funcs[side](style, &value, &unit); - if (type == CSS_MARGIN_SET) { - if (unit == CSS_UNIT_PCT) { - *frac += FIXTOINT(FDIV(value, F_100)); - } else { - *fixed += FIXTOINT(css_unit_len2device_px( - style, unit_len_ctx, - value, unit)); - } - } - } - - /* border */ - if (border) { - if (border_style_funcs[side](style) != - CSS_BORDER_STYLE_NONE) { - border_width_funcs[side](style, &value, &unit); - - *fixed += FIXTOINT(css_unit_len2device_px( - style, unit_len_ctx, - value, unit)); - } - } - - /* padding */ - if (padding) { - padding_funcs[side](style, &value, &unit); - if (unit == CSS_UNIT_PCT) { - *frac += FIXTOINT(FDIV(value, F_100)); - } else { - *fixed += FIXTOINT(css_unit_len2device_px( - style, unit_len_ctx, - value, unit)); - } - } -} - - -/** * Calculate minimum and maximum width of a table. * * \param table box of type TABLE @@ -360,7 +274,7 @@ static void layout_minmax_table(struct box *table, return; if (table_calculate_column_types(&content->unit_len_ctx, table) == false) { - NSLOG(netsurf, WARNING, + NSLOG(netsurf, ERROR, "Could not establish table column types."); return; } @@ -588,11 +502,7 @@ layout_minmax_line(struct box *first, css_fixed value = 0; css_unit unit = CSS_UNIT_PX; - assert(b->type == BOX_INLINE || b->type == BOX_INLINE_BLOCK || - b->type == BOX_FLOAT_LEFT || - b->type == BOX_FLOAT_RIGHT || - b->type == BOX_BR || b->type == BOX_TEXT || - b->type == BOX_INLINE_END); + assert(lh__box_is_inline_content(b)); NSLOG(layout, DEBUG, "%p: min %i, max %i", b, min, max); @@ -601,13 +511,13 @@ layout_minmax_line(struct box *first, break; } - if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT) { + if (lh__box_is_float_box(b)) { assert(b->children); - if (b->children->type == BOX_BLOCK) - layout_minmax_block(b->children, font_func, + if (b->children->type == BOX_TABLE) + layout_minmax_table(b->children, font_func, content); else - layout_minmax_table(b->children, font_func, + layout_minmax_block(b->children, font_func, content); b->min_width = b->children->min_width; b->max_width = b->children->max_width; @@ -617,7 +527,7 @@ layout_minmax_line(struct box *first, continue; } - if (b->type == BOX_INLINE_BLOCK) { + if (b->type == BOX_INLINE_BLOCK || b->type == BOX_INLINE_FLEX) { layout_minmax_block(b, font_func, content); if (min < b->min_width) min = b->min_width; @@ -668,8 +578,7 @@ layout_minmax_line(struct box *first, continue; } - if (!b->object && !(b->flags & IFRAME) && !b->gadget && - !(b->flags & REPLACE_DIM)) { + if (lh__box_is_replace(b) == false) { /* inline non-replaced, 10.3.1 and 10.6.1 */ bool no_wrap_box; if (!b->text) @@ -951,7 +860,6 @@ layout_minmax_inline_container(struct box *inline_container, inline_container->max_width); } - /** * Calculate minimum and maximum width of a block. * @@ -977,9 +885,13 @@ static void layout_minmax_block( css_fixed height = 0; css_unit hunit = CSS_UNIT_PX; enum css_box_sizing_e bs = CSS_BOX_SIZING_CONTENT_BOX; + bool using_min_border_box = false; + bool using_max_border_box = false; bool child_has_height = false; assert(block->type == BOX_BLOCK || + block->type == BOX_FLEX || + block->type == BOX_INLINE_FLEX || block->type == BOX_INLINE_BLOCK || block->type == BOX_TABLE_CELL); @@ -994,9 +906,9 @@ static void layout_minmax_block( } /* set whether the minimum width is of any interest for this box */ - if (((block->parent && (block->parent->type == BOX_FLOAT_LEFT || - block->parent->type == BOX_FLOAT_RIGHT)) || - block->type == BOX_INLINE_BLOCK) && + if (((block->parent && lh__box_is_float_box(block->parent)) || + block->type == BOX_INLINE_BLOCK || + block->type == BOX_INLINE_FLEX) && wtype != CSS_WIDTH_SET) { /* box shrinks to fit; need minimum width */ block->flags |= NEED_MIN; @@ -1007,6 +919,9 @@ static void layout_minmax_block( wtype != CSS_WIDTH_SET) { /* box inside shrink-to-fit context; need minimum width */ block->flags |= NEED_MIN; + } else if (block->parent && (block->parent->type == BOX_FLEX)) { + /* box is flex item */ + block->flags |= NEED_MIN; } if (block->gadget && (block->gadget->type == GADGET_TEXTBOX || @@ -1056,6 +971,7 @@ static void layout_minmax_block( /* recurse through children */ for (child = block->children; child; child = child->next) { switch (child->type) { + case BOX_FLEX: case BOX_BLOCK: layout_minmax_block(child, font_func, content); @@ -1097,10 +1013,24 @@ static void layout_minmax_block( continue; } - if (min < child->min_width) - min = child->min_width; - if (max < child->max_width) - max = child->max_width; + if (lh__box_is_flex_container(block) && + lh__flex_main_is_horizontal(block)) { + if (block->style != NULL && + css_computed_flex_wrap(block->style) == + CSS_FLEX_WRAP_NOWRAP) { + min += child->min_width; + } else { + if (min < child->min_width) + min = child->min_width; + } + max += child->max_width; + + } else { + if (min < child->min_width) + min = child->min_width; + if (max < child->max_width) + max = child->max_width; + } if (child_has_height) block->flags |= HAS_HEIGHT; @@ -1113,23 +1043,40 @@ static void layout_minmax_block( } /* fixed width takes priority */ - if (block->type != BOX_TABLE_CELL && wtype == CSS_WIDTH_SET && - wunit != CSS_UNIT_PCT) { - min = max = FIXTOINT(css_unit_len2device_px(block->style, - &content->unit_len_ctx, width, wunit)); - if (bs == CSS_BOX_SIZING_BORDER_BOX) { - int border_box_fixed = 0; - float border_box_frac = 0; - calculate_mbp_width(&content->unit_len_ctx, - block->style, LEFT, - false, true, true, - &border_box_fixed, &border_box_frac); - calculate_mbp_width(&content->unit_len_ctx, - block->style, RIGHT, - false, true, true, - &border_box_fixed, &border_box_frac); - if (min < border_box_fixed) { - min = max = border_box_fixed; + if (block->type != BOX_TABLE_CELL && !lh__box_is_flex_item(block)) { + bool border_box = bs == CSS_BOX_SIZING_BORDER_BOX; + enum css_max_width_e max_type; + enum css_min_width_e min_type; + css_unit unit = CSS_UNIT_PX; + css_fixed value = 0; + + if (wtype == CSS_WIDTH_SET && wunit != CSS_UNIT_PCT) { + min = max = FIXTOINT( + css_unit_len2device_px(block->style, + &content->unit_len_ctx, width, wunit)); + using_max_border_box = border_box; + using_min_border_box = border_box; + } + + min_type = css_computed_min_width(block->style, &value, &unit); + if (min_type == CSS_MIN_WIDTH_SET && unit != CSS_UNIT_PCT) { + int val = FIXTOINT(css_unit_len2device_px(block->style, + &content->unit_len_ctx, value, unit)); + + if (min < val) { + min = val; + using_min_border_box = border_box; + } + } + + max_type = css_computed_max_width(block->style, &value, &unit); + if (max_type == CSS_MAX_WIDTH_SET && unit != CSS_UNIT_PCT) { + int val = FIXTOINT(css_unit_len2device_px(block->style, + &content->unit_len_ctx, value, unit)); + + if (val >= 0 && max > val) { + max = val; + using_max_border_box = border_box; } } } @@ -1143,22 +1090,30 @@ static void layout_minmax_block( /* add margins, border, padding to min, max widths */ /* Note: we don't know available width here so percentage margin * and paddings are wrong. */ - if (bs == CSS_BOX_SIZING_BORDER_BOX && wtype == CSS_WIDTH_SET) { - /* Border and padding included in width, so just get margin */ - calculate_mbp_width(&content->unit_len_ctx, - block->style, LEFT, true, false, false, - &extra_fixed, &extra_frac); - calculate_mbp_width(&content->unit_len_ctx, - block->style, RIGHT, true, false, false, - &extra_fixed, &extra_frac); - } else { - calculate_mbp_width(&content->unit_len_ctx, - block->style, LEFT, true, true, true, - &extra_fixed, &extra_frac); - calculate_mbp_width(&content->unit_len_ctx, - block->style, RIGHT, true, true, true, - &extra_fixed, &extra_frac); + calculate_mbp_width(&content->unit_len_ctx, block->style, LEFT, + false, true, true, &extra_fixed, &extra_frac); + calculate_mbp_width(&content->unit_len_ctx, block->style, RIGHT, + false, true, true, &extra_fixed, &extra_frac); + + if (using_max_border_box) { + max -= extra_fixed; + max = max(max, 0); + } + + if (using_min_border_box) { + min -= extra_fixed; + min = max(min, 0); + } + + if (max < min) { + min = max; } + + calculate_mbp_width(&content->unit_len_ctx, block->style, LEFT, + true, false, false, &extra_fixed, &extra_frac); + calculate_mbp_width(&content->unit_len_ctx, block->style, RIGHT, + true, false, false, &extra_fixed, &extra_frac); + if (extra_fixed < 0) extra_fixed = 0; if (extra_frac < 0) @@ -1177,376 +1132,8 @@ static void layout_minmax_block( block->max_width = (max + extra_fixed) / (1.0 - extra_frac); } - assert(0 <= block->min_width && block->min_width <= block->max_width); -} - - -/** - * Adjust a specified width or height for the box-sizing property. - * - * This turns the specified dimension into a content-box dimension. - * - * \param unit_len_ctx Length conversion context - * \param box gadget to adjust dimensions of - * \param available_width width of containing block - * \param setwidth set true if the dimension to be tweaked is a width, - * else set false for a height - * \param dimension current value for given width/height dimension. - * updated to new value after consideration of - * gadget properties. - */ -static void layout_handle_box_sizing( - const css_unit_ctx *unit_len_ctx, - struct box *box, - int available_width, - bool setwidth, - int *dimension) -{ - enum css_box_sizing_e bs; - - assert(box && box->style); - - bs = css_computed_box_sizing(box->style); - - if (bs == CSS_BOX_SIZING_BORDER_BOX) { - int orig = *dimension; - int fixed = 0; - float frac = 0; - - calculate_mbp_width(unit_len_ctx, box->style, - setwidth ? LEFT : TOP, - false, true, true, &fixed, &frac); - calculate_mbp_width(unit_len_ctx, box->style, - setwidth ? RIGHT : BOTTOM, - false, true, true, &fixed, &frac); - orig -= frac * available_width + fixed; - *dimension = orig > 0 ? orig : 0; - } -} - - -/** - * Calculate width, height, and thickness of margins, paddings, and borders. - * - * \param unit_len_ctx Length conversion context - * \param available_width width of containing block - * \param viewport_height height of viewport in pixels or -ve if unknown - * \param box current box - * \param style style giving width, height, margins, paddings, - * and borders - * \param width updated to width, may be NULL - * \param height updated to height, may be NULL - * \param max_width updated to max-width, may be NULL - * \param min_width updated to min-width, may be NULL - * \param max_height updated to max-height, may be NULL - * \param min_height updated to min-height, may be NULL - * \param margin filled with margins, may be NULL - * \param padding filled with paddings, may be NULL - * \param border filled with border widths, may be NULL - */ -static void -layout_find_dimensions(const css_unit_ctx *unit_len_ctx, - int available_width, - int viewport_height, - struct box *box, - const css_computed_style *style, - int *width, - int *height, - int *max_width, - int *min_width, - int *max_height, - int *min_height, - int margin[4], - int padding[4], - struct box_border border[4]) -{ - struct box *containing_block = NULL; - unsigned int i; - - if (width) { - enum css_width_e wtype; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - wtype = css_computed_width(style, &value, &unit); - - if (wtype == CSS_WIDTH_SET) { - if (unit == CSS_UNIT_PCT) { - *width = FPCT_OF_INT_TOINT( - value, available_width); - } else { - *width = FIXTOINT(css_unit_len2device_px( - style, unit_len_ctx, - value, unit)); - } - } else { - *width = AUTO; - } - - if (*width != AUTO) { - layout_handle_box_sizing(unit_len_ctx, box, available_width, - true, width); - } - } - - if (height) { - enum css_height_e htype; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - htype = css_computed_height(style, &value, &unit); - - if (htype == CSS_HEIGHT_SET) { - if (unit == CSS_UNIT_PCT) { - enum css_height_e cbhtype; - - if (css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE && - box->parent) { - /* Box is absolutely positioned */ - assert(box->float_container); - containing_block = box->float_container; - } else if (box->float_container && - css_computed_position(box->style) != - CSS_POSITION_ABSOLUTE && - (css_computed_float(box->style) == - CSS_FLOAT_LEFT || - css_computed_float(box->style) == - CSS_FLOAT_RIGHT)) { - /* Box is a float */ - assert(box->parent && - box->parent->parent && - box->parent->parent->parent); - - containing_block = - box->parent->parent->parent; - } else if (box->parent && box->parent->type != - BOX_INLINE_CONTAINER) { - /* Box is a block level element */ - containing_block = box->parent; - } else if (box->parent && box->parent->type == - BOX_INLINE_CONTAINER) { - /* Box is an inline block */ - assert(box->parent->parent); - containing_block = box->parent->parent; - } - - if (containing_block) { - css_fixed f = 0; - css_unit u = CSS_UNIT_PX; - - cbhtype = css_computed_height( - containing_block->style, - &f, &u); - } - - if (containing_block && - containing_block->height != AUTO && - (css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE || - cbhtype == CSS_HEIGHT_SET)) { - /* Box is absolutely positioned or its - * containing block has a valid - * specified height. - * (CSS 2.1 Section 10.5) */ - *height = FPCT_OF_INT_TOINT(value, - containing_block->height); - } else if ((!box->parent || - !box->parent->parent) && - viewport_height >= 0) { - /* If root element or it's child - * (HTML or BODY) */ - *height = FPCT_OF_INT_TOINT(value, - viewport_height); - } else { - /* precentage height not permissible - * treat height as auto */ - *height = AUTO; - } - } else { - *height = FIXTOINT(css_unit_len2device_px( - style, unit_len_ctx, - value, unit)); - } - } else { - *height = AUTO; - } - - if (*height != AUTO) { - layout_handle_box_sizing(unit_len_ctx, box, available_width, - false, height); - } - } - - if (max_width) { - enum css_max_width_e type; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - type = css_computed_max_width(style, &value, &unit); - - if (type == CSS_MAX_WIDTH_SET) { - if (unit == CSS_UNIT_PCT) { - *max_width = FPCT_OF_INT_TOINT(value, - available_width); - } else { - *max_width = FIXTOINT(css_unit_len2device_px( - style, unit_len_ctx, - value, unit)); - } - } else { - /* Inadmissible */ - *max_width = -1; - } - - if (*max_width != -1) { - layout_handle_box_sizing(unit_len_ctx, box, available_width, - true, max_width); - } - } - - if (min_width) { - enum css_min_width_e type; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - type = ns_computed_min_width(style, &value, &unit); - - if (type == CSS_MIN_WIDTH_SET) { - if (unit == CSS_UNIT_PCT) { - *min_width = FPCT_OF_INT_TOINT(value, - available_width); - } else { - *min_width = FIXTOINT(css_unit_len2device_px( - style, unit_len_ctx, - value, unit)); - } - } else { - /* Inadmissible */ - *min_width = 0; - } - - if (*min_width != 0) { - layout_handle_box_sizing(unit_len_ctx, box, available_width, - true, min_width); - } - } - - if (max_height) { - enum css_max_height_e type; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - type = css_computed_max_height(style, &value, &unit); - - if (type == CSS_MAX_HEIGHT_SET) { - if (unit == CSS_UNIT_PCT) { - /* TODO: handle percentage */ - *max_height = -1; - } else { - *max_height = FIXTOINT(css_unit_len2device_px( - style, unit_len_ctx, - value, unit)); - } - } else { - /* Inadmissible */ - *max_height = -1; - } - } - - if (min_height) { - enum css_min_height_e type; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - type = ns_computed_min_height(style, &value, &unit); - - if (type == CSS_MIN_HEIGHT_SET) { - if (unit == CSS_UNIT_PCT) { - /* TODO: handle percentage */ - *min_height = 0; - } else { - *min_height = FIXTOINT(css_unit_len2device_px( - style, unit_len_ctx, - value, unit)); - } - } else { - /* Inadmissible */ - *min_height = 0; - } - } - - for (i = 0; i != 4; i++) { - if (margin) { - enum css_margin_e type = CSS_MARGIN_AUTO; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - type = margin_funcs[i](style, &value, &unit); - - if (type == CSS_MARGIN_SET) { - if (unit == CSS_UNIT_PCT) { - margin[i] = FPCT_OF_INT_TOINT(value, - available_width); - } else { - margin[i] = FIXTOINT(css_unit_len2device_px( - style, unit_len_ctx, - value, unit)); - } - } else { - margin[i] = AUTO; - } - } - - if (padding) { - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - padding_funcs[i](style, &value, &unit); - - if (unit == CSS_UNIT_PCT) { - padding[i] = FPCT_OF_INT_TOINT(value, - available_width); - } else { - padding[i] = FIXTOINT(css_unit_len2device_px( - style, unit_len_ctx, - value, unit)); - } - } - - /* Table cell borders are populated in table.c */ - if (border && box->type != BOX_TABLE_CELL) { - enum css_border_style_e bstyle = CSS_BORDER_STYLE_NONE; - css_color color = 0; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - border_width_funcs[i](style, &value, &unit); - bstyle = border_style_funcs[i](style); - border_color_funcs[i](style, &color); - - border[i].style = bstyle; - border[i].c = color; - - if (bstyle == CSS_BORDER_STYLE_HIDDEN || - bstyle == CSS_BORDER_STYLE_NONE) - /* spec unclear: following Mozilla */ - border[i].width = 0; - else - border[i].width = FIXTOINT(css_unit_len2device_px( - style, unit_len_ctx, - value, unit)); - - /* Special case for border-collapse: make all borders - * on table/table-row-group/table-row zero width. */ - if (css_computed_border_collapse(style) == - CSS_BORDER_COLLAPSE_COLLAPSE && - (box->type == BOX_TABLE || - box->type == BOX_TABLE_ROW_GROUP || - box->type == BOX_TABLE_ROW)) - border[i].width = 0; - } - } + assert(0 <= block->min_width); + assert(block->min_width <= block->max_width); } @@ -2013,15 +1600,10 @@ static void layout_move_children(struct box *box, int x, int y) } -/** - * Layout a table. - * - * \param table table to layout - * \param available_width width of containing block - * \param content memory pool for any new boxes - * \return true on success, false on memory exhaustion - */ -static bool layout_table(struct box *table, int available_width, +/* Documented in layout_internal.h */ +bool layout_table( + struct box *table, + int available_width, html_content *content) { unsigned int columns = table->columns; /* total columns */ @@ -2713,7 +2295,9 @@ static bool layout_block_object(struct box *block) { assert(block); assert(block->type == BOX_BLOCK || + block->type == BOX_FLEX || block->type == BOX_INLINE_BLOCK || + block->type == BOX_INLINE_FLEX || block->type == BOX_TABLE || block->type == BOX_TABLE_CELL); assert(block->object); @@ -2721,7 +2305,7 @@ static bool layout_block_object(struct box *block) NSLOG(layout, DEBUG, "block %p, object %p, width %i", block, hlcache_handle_get_url(block->object), block->width); - if (content_get_type(block->object) == CONTENT_HTML) { + if (content_can_reformat(block->object)) { content_reformat(block->object, false, block->width, 1); } else { /* Non-HTML objects */ @@ -3007,12 +2591,20 @@ layout_float_find_dimensions( */ static bool layout_float(struct box *b, int width, html_content *content) { - assert(b->type == BOX_TABLE || b->type == BOX_BLOCK || - b->type == BOX_INLINE_BLOCK); + assert(b->type == BOX_TABLE || + b->type == BOX_BLOCK || + b->type == BOX_INLINE_BLOCK || + b->type == BOX_FLEX || + b->type == BOX_INLINE_FLEX); layout_float_find_dimensions(&content->unit_len_ctx, width, b->style, b); - if (b->type == BOX_TABLE) { - if (!layout_table(b, width, content)) - return false; + if (b->type == BOX_TABLE || b->type == BOX_INLINE_FLEX) { + if (b->type == BOX_TABLE) { + if (!layout_table(b, width, content)) + return false; + } else { + if (!layout_flex(b, width, content)) + return false; + } if (b->margin[LEFT] == AUTO) b->margin[LEFT] = 0; if (b->margin[RIGHT] == AUTO) @@ -3021,8 +2613,9 @@ static bool layout_float(struct box *b, int width, html_content *content) b->margin[TOP] = 0; if (b->margin[BOTTOM] == AUTO) b->margin[BOTTOM] = 0; - } else + } else { return layout_block_context(b, -1, content); + } return true; } @@ -3209,20 +2802,14 @@ layout_line(struct box *first, for (x = 0, b = first; x <= x1 - x0 && b != 0; b = b->next) { int min_width, max_width, min_height, max_height; - assert(b->type == BOX_INLINE || b->type == BOX_INLINE_BLOCK || - b->type == BOX_FLOAT_LEFT || - b->type == BOX_FLOAT_RIGHT || - b->type == BOX_BR || b->type == BOX_TEXT || - b->type == BOX_INLINE_END); - + assert(lh__box_is_inline_content(b)); NSLOG(layout, DEBUG, "pass 1: b %p, x %i", b, x); - if (b->type == BOX_BR) break; - if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT) + if (lh__box_is_float_box(b)) continue; if (b->type == BOX_INLINE_BLOCK && (css_computed_position(b->style) == @@ -3236,7 +2823,8 @@ layout_line(struct box *first, x += space_after; - if (b->type == BOX_INLINE_BLOCK) { + if (b->type == BOX_INLINE_BLOCK || + b->type == BOX_INLINE_FLEX) { if (b->max_width != UNKNOWN_WIDTH) if (!layout_float(b, *width, content)) return false; @@ -3288,8 +2876,7 @@ layout_line(struct box *first, continue; } - if (!b->object && !(b->flags & IFRAME) && !b->gadget && - !(b->flags & REPLACE_DIM)) { + if (lh__box_is_replace(b) == false) { /* inline non-replaced, 10.3.1 and 10.6.1 */ b->height = line_height(&content->unit_len_ctx, b->style ? b->style : @@ -3397,7 +2984,7 @@ layout_line(struct box *first, } /* Reformat object to new box size */ - if (b->object && content_get_type(b->object) == CONTENT_HTML && + if (b->object && content_can_reformat(b->object) && b->width != content_get_available_width(b->object)) { css_fixed value = 0; @@ -3449,10 +3036,7 @@ layout_line(struct box *first, CSS_POSITION_FIXED)) { b->x = x + space_after; - } else if (b->type == BOX_INLINE || - b->type == BOX_INLINE_BLOCK || - b->type == BOX_TEXT || - b->type == BOX_INLINE_END) { + } else if (lh__box_is_inline_flow(b)) { assert(b->width != UNKNOWN_WIDTH); x_previous = x; @@ -3460,7 +3044,8 @@ layout_line(struct box *first, b->x = x; if ((b->type == BOX_INLINE && !b->inline_end) || - b->type == BOX_INLINE_BLOCK) { + b->type == BOX_INLINE_BLOCK || + b->type == BOX_INLINE_FLEX) { b->x += b->margin[LEFT] + b->border[LEFT].width; x = b->x + b->padding[LEFT] + b->width + b->padding[RIGHT] + @@ -3791,9 +3376,7 @@ layout_line(struct box *first, d->y = *y; continue; } else if ((d->type == BOX_INLINE && - ((d->object || d->gadget) == false) && - !(d->flags & IFRAME) && - !(d->flags & REPLACE_DIM)) || + lh__box_is_replace(d) == false) || d->type == BOX_BR || d->type == BOX_TEXT || d->type == BOX_INLINE_END) { @@ -3914,8 +3497,7 @@ static bool layout_inline_container(struct box *inline_container, int width, whitespace == CSS_WHITE_SPACE_PRE_WRAP); } - if ((!c->object && !(c->flags & REPLACE_DIM) && - !(c->flags & IFRAME) && + if ((lh__box_is_object(c) == false && c->text && (c->length || is_pre)) || c->type == BOX_BR) has_text_children = true; @@ -3945,21 +3527,11 @@ static bool layout_inline_container(struct box *inline_container, int width, } -/** - * Layout a block formatting context. - * - * \param block BLOCK, INLINE_BLOCK, or TABLE_CELL to layout - * \param viewport_height Height of viewport in pixels or -ve if unknown - * \param content Memory pool for any new boxes - * \return true on success, false on memory exhaustion - * - * This function carries out layout of a block and its children, as described - * in CSS 2.1 9.4.1. - */ -static bool -layout_block_context(struct box *block, - int viewport_height, - html_content *content) +/* Documented in layout_intertnal.h */ +bool layout_block_context( + struct box *block, + int viewport_height, + html_content *content) { struct box *box; int cx, cy; /**< current coordinates */ @@ -3974,7 +3546,9 @@ layout_block_context(struct box *block, assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK || - block->type == BOX_TABLE_CELL); + block->type == BOX_TABLE_CELL || + block->type == BOX_FLEX || + block->type == BOX_INLINE_FLEX); assert(block->width != UNKNOWN_WIDTH); assert(block->width != AUTO); @@ -4043,7 +3617,9 @@ layout_block_context(struct box *block, enum css_overflow_e overflow_x = CSS_OVERFLOW_VISIBLE; enum css_overflow_e overflow_y = CSS_OVERFLOW_VISIBLE; - assert(box->type == BOX_BLOCK || box->type == BOX_TABLE || + assert(box->type == BOX_BLOCK || + box->type == BOX_FLEX || + box->type == BOX_TABLE || box->type == BOX_INLINE_CONTAINER); /* Tables are laid out before being positioned, because the @@ -4094,9 +3670,10 @@ layout_block_context(struct box *block, * left and right margins to avoid any floats. */ lm = rm = 0; - if (box->type == BOX_BLOCK || box->flags & IFRAME) { - if (!box->object && !(box->flags & IFRAME) && - !(box->flags & REPLACE_DIM) && + if (box->type == BOX_FLEX || + box->type == BOX_BLOCK || + box->flags & IFRAME) { + if (lh__box_is_object(box) == false && box->style && (overflow_x != CSS_OVERFLOW_VISIBLE || overflow_y != CSS_OVERFLOW_VISIBLE)) { @@ -4181,6 +3758,7 @@ layout_block_context(struct box *block, /* Vertical margin */ if (((box->type == BOX_BLOCK && (box->flags & HAS_HEIGHT)) || + box->type == BOX_FLEX || box->type == BOX_TABLE || (box->type == BOX_INLINE_CONTAINER && !box_is_first_child(box)) || @@ -4205,11 +3783,19 @@ layout_block_context(struct box *block, /* Unless the box has an overflow style of visible, the box * establishes a new block context. */ - if (box->type == BOX_BLOCK && box->style && - (overflow_x != CSS_OVERFLOW_VISIBLE || - overflow_y != CSS_OVERFLOW_VISIBLE)) { + if (box->type == BOX_FLEX || + (box->type == BOX_BLOCK && box->style && + (overflow_x != CSS_OVERFLOW_VISIBLE || + overflow_y != CSS_OVERFLOW_VISIBLE))) { - layout_block_context(box, viewport_height, content); + if (box->type == BOX_FLEX) { + if (!layout_flex(box, box->width, content)) { + return false; + } + } else { + layout_block_context(box, + viewport_height, content); + } cy += box->padding[TOP]; @@ -4230,7 +3816,8 @@ layout_block_context(struct box *block, goto advance_to_next_box; } - NSLOG(layout, DEBUG, "box %p, cx %i, cy %i", box, cx, cy); + NSLOG(layout, DEBUG, "box %p, cx %i, cy %i, width %i", + box, cx, cy, box->width); /* Layout (except tables). */ if (box->object) { @@ -4607,7 +4194,7 @@ layout__get_ol_reversed(dom_node *ol_node) */ static bool layout__get_list_item_count( - dom_node *list_owner, int *count_out) + dom_node *list_owner, dom_long *count_out) { dom_html_element_type tag_type; dom_exception exc; @@ -4679,7 +4266,7 @@ layout__ordered_list_count( dom_exception exc; dom_node *child; int step = 1; - int next; + dom_long next; if (box->node == NULL) { return; @@ -4914,9 +4501,9 @@ layout_compute_offsets(const css_unit_ctx *unit_len_ctx, css_fixed value = 0; css_unit unit = CSS_UNIT_PX; - assert(containing_block->width != UNKNOWN_WIDTH && - containing_block->width != AUTO && - containing_block->height != AUTO); + assert(containing_block->width != UNKNOWN_WIDTH); + assert(containing_block->width != AUTO); + assert(containing_block->height != AUTO); /* left */ type = css_computed_left(box->style, &value, &unit); @@ -5006,7 +4593,9 @@ layout_absolute(struct box *box, int space; assert(box->type == BOX_BLOCK || box->type == BOX_TABLE || - box->type == BOX_INLINE_BLOCK); + box->type == BOX_INLINE_BLOCK || + box->type == BOX_FLEX || + box->type == BOX_INLINE_FLEX); /* The static position is where the box would be if it was not * absolutely positioned. The x and y are filled in by @@ -5024,8 +4613,6 @@ layout_absolute(struct box *box, containing_block->padding[RIGHT]; containing_block->height += containing_block->padding[TOP] + containing_block->padding[BOTTOM]; - } else { - /** \todo inline containers */ } layout_compute_offsets(&content->unit_len_ctx, box, containing_block, @@ -5243,6 +4830,13 @@ layout_absolute(struct box *box, box->float_container = NULL; layout_solve_width(box, box->parent->width, box->width, 0, 0, -1, -1); + } else if (box->type == BOX_FLEX || box->type == BOX_INLINE_FLEX) { + /* layout_table also expects the containing block to be + * stored in the float_container field */ + box->float_container = containing_block; + if (!layout_flex(box, width, content)) + return false; + box->float_container = NULL; } /* 10.6.4 */ @@ -5379,7 +4973,9 @@ layout_position_absolute(struct box *box, for (c = box->children; c; c = c->next) { if ((c->type == BOX_BLOCK || c->type == BOX_TABLE || - c->type == BOX_INLINE_BLOCK) && + c->type == BOX_INLINE_BLOCK || + c->type == BOX_FLEX || + c->type == BOX_INLINE_FLEX) && (css_computed_position(c->style) == CSS_POSITION_ABSOLUTE || css_computed_position(c->style) == diff --git a/content/handlers/html/layout_flex.c b/content/handlers/html/layout_flex.c new file mode 100644 index 000000000..bde3c5bd1 --- /dev/null +++ b/content/handlers/html/layout_flex.c @@ -0,0 +1,1117 @@ +/* + * Copyright 2022 Michael Drake <tlsa@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file + * HTML layout implementation: display: flex. + * + * Layout is carried out in two stages: + * + * 1. + calculation of minimum / maximum box widths, and + * + determination of whether block level boxes will have >zero height + * + * 2. + layout (position and dimensions) + * + * In most cases the functions for the two stages are a corresponding pair + * layout_minmax_X() and layout_X(). + */ + +#include <string.h> + +#include "utils/log.h" +#include "utils/utils.h" + +#include "html/box.h" +#include "html/html.h" +#include "html/private.h" +#include "html/box_inspect.h" +#include "html/layout_internal.h" + +/** + * Flex item data + */ +struct flex_item_data { + enum css_flex_basis_e basis; + css_fixed basis_length; + css_unit basis_unit; + struct box *box; + + css_fixed shrink; + css_fixed grow; + + int min_main; + int max_main; + int min_cross; + int max_cross; + + int target_main_size; + int base_size; + int main_size; + size_t line; + + bool freeze; + bool min_violation; + bool max_violation; +}; + +/** + * Flex line data + */ +struct flex_line_data { + int main_size; + int cross_size; + + int used_main_size; + int main_auto_margin_count; + + int pos; + + size_t first; + size_t count; + size_t frozen; +}; + +/** + * Flex layout context + */ +struct flex_ctx { + html_content *content; + const struct box *flex; + const css_unit_ctx *unit_len_ctx; + + int main_size; + int cross_size; + + int available_main; + int available_cross; + + bool horizontal; + bool main_reversed; + enum css_flex_wrap_e wrap; + + struct flex_items { + size_t count; + struct flex_item_data *data; + } item; + + struct flex_lines { + size_t count; + size_t alloc; + struct flex_line_data *data; + } line; +}; + +/** + * Destroy a flex layout context + * + * \param[in] ctx Flex layout context + */ +static void layout_flex_ctx__destroy(struct flex_ctx *ctx) +{ + if (ctx != NULL) { + free(ctx->item.data); + free(ctx->line.data); + free(ctx); + } +} + +/** + * Create a flex layout context + * + * \param[in] content HTML content containing flex box + * \param[in] flex Box to create layout context for + * \return flex layout context or NULL on error + */ +static struct flex_ctx *layout_flex_ctx__create( + html_content *content, + const struct box *flex) +{ + struct flex_ctx *ctx; + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + return NULL; + } + ctx->line.alloc = 1; + + ctx->item.count = box_count_children(flex); + ctx->item.data = calloc(ctx->item.count, sizeof(*ctx->item.data)); + if (ctx->item.data == NULL) { + layout_flex_ctx__destroy(ctx); + return NULL; + } + + ctx->line.alloc = 1; + ctx->line.data = calloc(ctx->line.alloc, sizeof(*ctx->line.data)); + if (ctx->line.data == NULL) { + layout_flex_ctx__destroy(ctx); + return NULL; + } + + ctx->flex = flex; + ctx->content = content; + ctx->unit_len_ctx = &content->unit_len_ctx; + + ctx->wrap = css_computed_flex_wrap(flex->style); + ctx->horizontal = lh__flex_main_is_horizontal(flex); + ctx->main_reversed = lh__flex_direction_reversed(flex); + + return ctx; +} + +/** + * Find box side representing the start of flex container in main direction. + * + * \param[in] ctx Flex layout context. + * \return the start side. + */ +static enum box_side layout_flex__main_start_side( + const struct flex_ctx *ctx) +{ + if (ctx->horizontal) { + return (ctx->main_reversed) ? RIGHT : LEFT; + } else { + return (ctx->main_reversed) ? BOTTOM : TOP; + } +} + +/** + * Find box side representing the end of flex container in main direction. + * + * \param[in] ctx Flex layout context. + * \return the end side. + */ +static enum box_side layout_flex__main_end_side( + const struct flex_ctx *ctx) +{ + if (ctx->horizontal) { + return (ctx->main_reversed) ? LEFT : RIGHT; + } else { + return (ctx->main_reversed) ? TOP : BOTTOM; + } +} + +/** + * Perform layout on a flex item + * + * \param[in] ctx Flex layout context + * \param[in] item Item to lay out + * \param[in] available_width Available width for item in pixels + * \return true on success false on failure + */ +static bool layout_flex_item( + const struct flex_ctx *ctx, + const struct flex_item_data *item, + int available_width) +{ + bool success; + struct box *b = item->box; + + switch (b->type) { + case BOX_BLOCK: + success = layout_block_context(b, -1, ctx->content); + break; + case BOX_TABLE: + b->float_container = b->parent; + success = layout_table(b, available_width, ctx->content); + b->float_container = NULL; + break; + case BOX_FLEX: + b->float_container = b->parent; + success = layout_flex(b, available_width, ctx->content); + b->float_container = NULL; + break; + default: + assert(0 && "Bad flex item back type"); + success = false; + break; + } + + if (!success) { + NSLOG(flex, ERROR, "box %p: layout failed", b); + } + + return success; +} + +/** + * Calculate an item's base and target main sizes. + * + * \param[in] ctx Flex layout context + * \param[in] item Item to get sizes of + * \param[in] available_width Available width in pixels + * \return true on success false on failure + */ +static inline bool layout_flex__base_and_main_sizes( + const struct flex_ctx *ctx, + struct flex_item_data *item, + int available_width) +{ + struct box *b = item->box; + int content_min_width = b->min_width; + int content_max_width = b->max_width; + int delta_outer_main = lh__delta_outer_main(ctx->flex, b); + + NSLOG(flex, DEEPDEBUG, "box %p: delta_outer_main: %i", + b, delta_outer_main); + + if (item->basis == CSS_FLEX_BASIS_SET) { + if (item->basis_unit == CSS_UNIT_PCT) { + item->base_size = FPCT_OF_INT_TOINT( + item->basis_length, + available_width); + } else { + item->base_size = FIXTOINT(css_unit_len2device_px( + b->style, ctx->unit_len_ctx, + item->basis_length, + item->basis_unit)); + } + + } else if (item->basis == CSS_FLEX_BASIS_AUTO) { + item->base_size = ctx->horizontal ? b->width : b->height; + } else { + item->base_size = AUTO; + } + + if (ctx->horizontal == false) { + if (b->width == AUTO) { + b->width = min(max(content_min_width, available_width), + content_max_width); + b->width -= lh__delta_outer_width(b); + } + + if (!layout_flex_item(ctx, item, b->width)) { + return false; + } + } + + if (item->base_size == AUTO) { + if (ctx->horizontal == false) { + item->base_size = b->height; + } else { + item->base_size = content_max_width - delta_outer_main; + } + } + + item->base_size += delta_outer_main; + + if (ctx->horizontal) { + item->base_size = min(item->base_size, available_width); + item->base_size = max(item->base_size, content_min_width); + } + + item->target_main_size = item->base_size; + item->main_size = item->base_size; + + if (item->max_main > 0 && + item->main_size > item->max_main + delta_outer_main) { + item->main_size = item->max_main + delta_outer_main; + } + + if (item->main_size < item->min_main + delta_outer_main) { + item->main_size = item->min_main + delta_outer_main; + } + + NSLOG(flex, DEEPDEBUG, "flex-item box: %p: base_size: %i, main_size %i", + b, item->base_size, item->main_size); + + return true; +} + +/** + * Fill out all item's data in a flex container. + * + * \param[in] ctx Flex layout context + * \param[in] flex Flex box + * \param[in] available_width Available width in pixels + */ +static void layout_flex_ctx__populate_item_data( + const struct flex_ctx *ctx, + const struct box *flex, + int available_width) +{ + size_t i = 0; + bool horizontal = ctx->horizontal; + + for (struct box *b = flex->children; b != NULL; b = b->next) { + struct flex_item_data *item = &ctx->item.data[i++]; + + b->float_container = b->parent; + layout_find_dimensions(ctx->unit_len_ctx, available_width, -1, + b, b->style, &b->width, &b->height, + horizontal ? &item->max_main : &item->max_cross, + horizontal ? &item->min_main : &item->min_cross, + horizontal ? &item->max_cross : &item->max_main, + horizontal ? &item->min_cross : &item->min_main, + b->margin, b->padding, b->border); + b->float_container = NULL; + + NSLOG(flex, DEEPDEBUG, "flex-item box: %p: width: %i", + b, b->width); + + item->box = b; + item->basis = css_computed_flex_basis(b->style, + &item->basis_length, &item->basis_unit); + + css_computed_flex_shrink(b->style, &item->shrink); + css_computed_flex_grow(b->style, &item->grow); + + layout_flex__base_and_main_sizes(ctx, item, available_width); + } +} + +/** + * Ensure context's lines array has a free space + * + * \param[in] ctx Flex layout context + * \return true on success false on out of memory + */ +static bool layout_flex_ctx__ensure_line(struct flex_ctx *ctx) +{ + struct flex_line_data *temp; + size_t line_alloc = ctx->line.alloc * 2; + + if (ctx->line.alloc > ctx->line.count) { + return true; + } + + temp = realloc(ctx->line.data, sizeof(*ctx->line.data) * line_alloc); + if (temp == NULL) { + return false; + } + ctx->line.data = temp; + + memset(ctx->line.data + ctx->line.alloc, 0, + sizeof(*ctx->line.data) * (line_alloc - ctx->line.alloc)); + ctx->line.alloc = line_alloc; + + return true; +} + +/** + * Assigns flex items to the line and returns the line + * + * \param[in] ctx Flex layout context + * \param[in] item_index Index to first item to assign to this line + * \return Pointer to the new line, or NULL on error. + */ +static struct flex_line_data *layout_flex__build_line(struct flex_ctx *ctx, + size_t item_index) +{ + enum box_side start_side = layout_flex__main_start_side(ctx); + enum box_side end_side = layout_flex__main_end_side(ctx); + struct flex_line_data *line; + int used_main = 0; + + if (!layout_flex_ctx__ensure_line(ctx)) { + return NULL; + } + + line = &ctx->line.data[ctx->line.count]; + line->first = item_index; + + NSLOG(flex, DEEPDEBUG, "flex container %p: available main: %i", + ctx->flex, ctx->available_main); + + while (item_index < ctx->item.count) { + struct flex_item_data *item = &ctx->item.data[item_index]; + struct box *b = item->box; + int pos_main; + + pos_main = ctx->horizontal ? + item->main_size : + b->height + lh__delta_outer_main(ctx->flex, b); + + if (ctx->wrap == CSS_FLEX_WRAP_NOWRAP || + pos_main + used_main <= ctx->available_main || + lh__box_is_absolute(item->box) || + ctx->available_main == AUTO || + line->count == 0 || + pos_main == 0) { + if (lh__box_is_absolute(item->box) == false) { + line->main_size += item->main_size; + used_main += pos_main; + + if (b->margin[start_side] == AUTO) { + line->main_auto_margin_count++; + } + if (b->margin[end_side] == AUTO) { + line->main_auto_margin_count++; + } + } + item->line = ctx->line.count; + line->count++; + item_index++; + } else { + break; + } + } + + if (line->count > 0) { + ctx->line.count++; + } else { + NSLOG(layout, ERROR, "Failed to fit any flex items"); + } + + return line; +} + +/** + * Freeze an item on a line + * + * \param[in] line Line to containing item + * \param[in] item Item to freeze + */ +static inline void layout_flex__item_freeze( + struct flex_line_data *line, + struct flex_item_data *item) +{ + item->freeze = true; + line->frozen++; + + if (!lh__box_is_absolute(item->box)){ + line->used_main_size += item->target_main_size; + } + + NSLOG(flex, DEEPDEBUG, "flex-item box: %p: " + "Frozen at target_main_size: %i", + item->box, item->target_main_size); +} + +/** + * Calculate remaining free space and unfrozen item factor sum + * + * \param[in] ctx Flex layout context + * \param[in] line Line to calculate free space on + * \param[out] unfrozen_factor_sum Returns sum of unfrozen item's flex factors + * \param[in] initial_free_main Initial free space in main direction + * \param[in] available_main Available space in main direction + * \param[in] grow Whether to grow or shrink + * return remaining free space on line + */ +static inline int layout_flex__remaining_free_main( + struct flex_ctx *ctx, + struct flex_line_data *line, + css_fixed *unfrozen_factor_sum, + int initial_free_main, + int available_main, + bool grow) +{ + int remaining_free_main = available_main; + size_t item_count = line->first + line->count; + + *unfrozen_factor_sum = 0; + + for (size_t i = line->first; i < item_count; i++) { + struct flex_item_data *item = &ctx->item.data[i]; + + if (item->freeze) { + remaining_free_main -= item->target_main_size; + } else { + remaining_free_main -= item->base_size; + + *unfrozen_factor_sum += grow ? + item->grow : item->shrink; + } + } + + if (*unfrozen_factor_sum < F_1) { + int free_space = FIXTOINT(FMUL(INTTOFIX(initial_free_main), + *unfrozen_factor_sum)); + + if (free_space < remaining_free_main) { + remaining_free_main = free_space; + } + } + + NSLOG(flex, DEEPDEBUG, "Remaining free space: %i", + remaining_free_main); + + return remaining_free_main; +} + +/** + * Clamp flex item target main size and get min/max violations + * + * \param[in] ctx Flex layout context + * \param[in] line Line to align items on + * return total violation in pixels + */ +static inline int layout_flex__get_min_max_violations( + struct flex_ctx *ctx, + struct flex_line_data *line) +{ + + int total_violation = 0; + size_t item_count = line->first + line->count; + + for (size_t i = line->first; i < item_count; i++) { + struct flex_item_data *item = &ctx->item.data[i]; + int target_main_size = item->target_main_size; + + NSLOG(flex, DEEPDEBUG, "item %p: target_main_size: %i", + item->box, target_main_size); + + if (item->freeze) { + continue; + } + + if (item->max_main > 0 && + target_main_size > item->max_main) { + target_main_size = item->max_main; + item->max_violation = true; + NSLOG(flex, DEEPDEBUG, "Violation: max_main: %i", + item->max_main); + } + + if (target_main_size < item->min_main) { + target_main_size = item->min_main; + item->min_violation = true; + NSLOG(flex, DEEPDEBUG, "Violation: min_main: %i", + item->min_main); + } + + if (target_main_size < item->box->min_width) { + target_main_size = item->box->min_width; + item->min_violation = true; + NSLOG(flex, DEEPDEBUG, "Violation: box min_width: %i", + item->box->min_width); + } + + if (target_main_size < 0) { + target_main_size = 0; + item->min_violation = true; + NSLOG(flex, DEEPDEBUG, "Violation: less than 0"); + } + + total_violation += target_main_size - item->target_main_size; + item->target_main_size = target_main_size; + } + + NSLOG(flex, DEEPDEBUG, "Total violation: %i", total_violation); + + return total_violation; +} + +/** + * Distribute remaining free space proportional to the flex factors. + * + * Remaining free space may be negative. + * + * \param[in] ctx Flex layout context + * \param[in] line Line to distribute free space on + * \param[in] unfrozen_factor_sum Sum of unfrozen item's flex factors + * \param[in] remaining_free_main Remaining free space in main direction + * \param[in] grow Whether to grow or shrink + */ +static inline void layout_flex__distribute_free_main( + struct flex_ctx *ctx, + struct flex_line_data *line, + css_fixed unfrozen_factor_sum, + int remaining_free_main, + bool grow) +{ + size_t item_count = line->first + line->count; + + if (grow) { + css_fixed remainder = 0; + for (size_t i = line->first; i < item_count; i++) { + struct flex_item_data *item = &ctx->item.data[i]; + css_fixed result; + css_fixed ratio; + + if (item->freeze) { + continue; + } + + ratio = FDIV(item->grow, unfrozen_factor_sum); + result = FMUL(INTTOFIX(remaining_free_main), ratio) + + remainder; + + item->target_main_size = item->base_size + + FIXTOINT(result); + remainder = FIXFRAC(result); + } + } else { + css_fixed scaled_shrink_factor_sum = 0; + css_fixed remainder = 0; + + for (size_t i = line->first; i < item_count; i++) { + struct flex_item_data *item = &ctx->item.data[i]; + css_fixed scaled_shrink_factor; + + if (item->freeze) { + continue; + } + + scaled_shrink_factor = FMUL( + item->shrink, + INTTOFIX(item->base_size)); + scaled_shrink_factor_sum += scaled_shrink_factor; + } + + for (size_t i = line->first; i < item_count; i++) { + struct flex_item_data *item = &ctx->item.data[i]; + css_fixed scaled_shrink_factor; + css_fixed result; + css_fixed ratio; + + if (item->freeze) { + continue; + } else if (scaled_shrink_factor_sum == 0) { + item->target_main_size = item->main_size; + layout_flex__item_freeze(line, item); + continue; + } + + scaled_shrink_factor = FMUL( + item->shrink, + INTTOFIX(item->base_size)); + ratio = FDIV(scaled_shrink_factor, + scaled_shrink_factor_sum); + result = FMUL(INTTOFIX(abs(remaining_free_main)), + ratio) + remainder; + + item->target_main_size = item->base_size - + FIXTOINT(result); + remainder = FIXFRAC(result); + } + } +} + +/** + * Resolve flexible item lengths along a line. + * + * See 9.7 of Tests CSS Flexible Box Layout Module Level 1. + * + * \param[in] ctx Flex layout context + * \param[in] line Line to resolve + * \return true on success, false on failure. + */ +static bool layout_flex__resolve_line( + struct flex_ctx *ctx, + struct flex_line_data *line) +{ + size_t item_count = line->first + line->count; + int available_main = ctx->available_main; + int initial_free_main; + bool grow; + + if (available_main == AUTO) { + available_main = INT_MAX; + } + + grow = (line->main_size < available_main); + initial_free_main = available_main; + + NSLOG(flex, DEEPDEBUG, "box %p: line %zu: first: %zu, count: %zu", + ctx->flex, line - ctx->line.data, + line->first, line->count); + NSLOG(flex, DEEPDEBUG, "Line main_size: %i, available_main: %i", + line->main_size, available_main); + + for (size_t i = line->first; i < item_count; i++) { + struct flex_item_data *item = &ctx->item.data[i]; + + /* 3. Size inflexible items */ + if (grow) { + if (item->grow == 0 || + item->base_size > item->main_size) { + item->target_main_size = item->main_size; + layout_flex__item_freeze(line, item); + } + } else { + if (item->shrink == 0 || + item->base_size < item->main_size) { + item->target_main_size = item->main_size; + layout_flex__item_freeze(line, item); + } + } + + /* 4. Calculate initial free space */ + if (item->freeze) { + initial_free_main -= item->target_main_size; + } else { + initial_free_main -= item->base_size; + } + } + + /* 5. Loop */ + while (line->frozen < line->count) { + css_fixed unfrozen_factor_sum; + int remaining_free_main; + int total_violation; + + NSLOG(flex, DEEPDEBUG, "flex-container: %p: Resolver pass", + ctx->flex); + + /* b */ + remaining_free_main = layout_flex__remaining_free_main(ctx, + line, &unfrozen_factor_sum, initial_free_main, + available_main, grow); + + /* c */ + if (remaining_free_main != 0) { + layout_flex__distribute_free_main(ctx, + line, unfrozen_factor_sum, + remaining_free_main, grow); + } + + /* d */ + total_violation = layout_flex__get_min_max_violations( + ctx, line); + + /* e */ + for (size_t i = line->first; i < item_count; i++) { + struct flex_item_data *item = &ctx->item.data[i]; + + if (item->freeze) { + continue; + } + + if (total_violation == 0 || + (total_violation > 0 && item->min_violation) || + (total_violation < 0 && item->max_violation)) { + layout_flex__item_freeze(line, item); + } + } + } + + return true; +} + +/** + * Position items along a line + * + * \param[in] ctx Flex layout context + * \param[in] line Line to resolve + * \return true on success, false on failure. + */ +static bool layout_flex__place_line_items_main( + struct flex_ctx *ctx, + struct flex_line_data *line) +{ + int main_pos = ctx->flex->padding[layout_flex__main_start_side(ctx)]; + int post_multiplier = ctx->main_reversed ? 0 : 1; + int pre_multiplier = ctx->main_reversed ? -1 : 0; + size_t item_count = line->first + line->count; + int extra_remainder = 0; + int extra = 0; + + if (ctx->main_reversed) { + main_pos = lh__box_size_main(ctx->horizontal, ctx->flex) - + main_pos; + } + + if (ctx->available_main != AUTO && + ctx->available_main != UNKNOWN_WIDTH && + ctx->available_main > line->used_main_size) { + if (line->main_auto_margin_count > 0) { + extra = ctx->available_main - line->used_main_size; + + extra_remainder = extra % line->main_auto_margin_count; + extra /= line->main_auto_margin_count; + } + } + + for (size_t i = line->first; i < item_count; i++) { + enum box_side main_end = ctx->horizontal ? RIGHT : BOTTOM; + enum box_side main_start = ctx->horizontal ? LEFT : TOP; + struct flex_item_data *item = &ctx->item.data[i]; + struct box *b = item->box; + int extra_total = 0; + int extra_post = 0; + int extra_pre = 0; + int box_size_main; + int *box_pos_main; + + if (ctx->horizontal) { + b->width = item->target_main_size - + lh__delta_outer_width(b); + + if (!layout_flex_item(ctx, item, b->width)) { + return false; + } + } + + box_size_main = lh__box_size_main(ctx->horizontal, b); + box_pos_main = ctx->horizontal ? &b->x : &b->y; + + if (!lh__box_is_absolute(b)) { + if (b->margin[main_start] == AUTO) { + extra_pre = extra + extra_remainder; + } + if (b->margin[main_end] == AUTO) { + extra_post = extra + extra_remainder; + } + extra_total = extra_pre + extra_post; + + main_pos += pre_multiplier * + (extra_total + box_size_main + + lh__delta_outer_main(ctx->flex, b)); + } + + *box_pos_main = main_pos + lh__non_auto_margin(b, main_start) + + extra_pre + b->border[main_start].width; + + if (!lh__box_is_absolute(b)) { + int cross_size; + int box_size_cross = lh__box_size_cross( + ctx->horizontal, b); + + main_pos += post_multiplier * + (extra_total + box_size_main + + lh__delta_outer_main(ctx->flex, b)); + + cross_size = box_size_cross + lh__delta_outer_cross( + ctx->flex, b); + if (line->cross_size < cross_size) { + line->cross_size = cross_size; + } + } + } + + return true; +} + +/** + * Collect items onto lines and place items along the lines + * + * \param[in] ctx Flex layout context + * \return true on success, false on failure. + */ +static bool layout_flex__collect_items_into_lines( + struct flex_ctx *ctx) +{ + size_t pos = 0; + + while (pos < ctx->item.count) { + struct flex_line_data *line; + + line = layout_flex__build_line(ctx, pos); + if (line == NULL) { + return false; + } + + pos += line->count; + + NSLOG(flex, DEEPDEBUG, "flex-container: %p: " + "fitted: %zu (total: %zu/%zu)", + ctx->flex, line->count, + pos, ctx->item.count); + + if (!layout_flex__resolve_line(ctx, line)) { + return false; + } + + if (!layout_flex__place_line_items_main(ctx, line)) { + return false; + } + + ctx->cross_size += line->cross_size; + if (ctx->main_size < line->main_size) { + ctx->main_size = line->main_size; + } + } + + return true; +} + +/** + * Align items on a line. + * + * \param[in] ctx Flex layout context + * \param[in] line Line to align items on + * \param[in] extra Extra line width in pixels + */ +static void layout_flex__place_line_items_cross(struct flex_ctx *ctx, + struct flex_line_data *line, int extra) +{ + enum box_side cross_start = ctx->horizontal ? TOP : LEFT; + size_t item_count = line->first + line->count; + + for (size_t i = line->first; i < item_count; i++) { + struct flex_item_data *item = &ctx->item.data[i]; + struct box *b = item->box; + int cross_free_space; + int *box_size_cross; + int *box_pos_cross; + + box_pos_cross = ctx->horizontal ? &b->y : &b->x; + box_size_cross = lh__box_size_cross_ptr(ctx->horizontal, b); + + cross_free_space = line->cross_size + extra - *box_size_cross - + lh__delta_outer_cross(ctx->flex, b); + + switch (lh__box_align_self(ctx->flex, b)) { + default: + case CSS_ALIGN_SELF_STRETCH: + if (lh__box_size_cross_is_auto(ctx->horizontal, b)) { + *box_size_cross += cross_free_space; + + /* Relayout children for stretch. */ + if (!layout_flex_item(ctx, item, b->width)) { + return; + } + } + fallthrough; + case CSS_ALIGN_SELF_FLEX_START: + *box_pos_cross = ctx->flex->padding[cross_start] + + line->pos + + lh__non_auto_margin(b, cross_start) + + b->border[cross_start].width; + break; + + case CSS_ALIGN_SELF_FLEX_END: + *box_pos_cross = ctx->flex->padding[cross_start] + + line->pos + cross_free_space + + lh__non_auto_margin(b, cross_start) + + b->border[cross_start].width; + break; + + case CSS_ALIGN_SELF_BASELINE: + case CSS_ALIGN_SELF_CENTER: + *box_pos_cross = ctx->flex->padding[cross_start] + + line->pos + cross_free_space / 2 + + lh__non_auto_margin(b, cross_start) + + b->border[cross_start].width; + break; + } + } +} + +/** + * Place the lines and align the items on the line. + * + * \param[in] ctx Flex layout context + */ +static void layout_flex__place_lines(struct flex_ctx *ctx) +{ + bool reversed = ctx->wrap == CSS_FLEX_WRAP_WRAP_REVERSE; + int line_pos = reversed ? ctx->cross_size : 0; + int post_multiplier = reversed ? 0 : 1; + int pre_multiplier = reversed ? -1 : 0; + int extra_remainder = 0; + int extra = 0; + + if (ctx->available_cross != AUTO && + ctx->available_cross > ctx->cross_size && + ctx->line.count > 0) { + extra = ctx->available_cross - ctx->cross_size; + + extra_remainder = extra % ctx->line.count; + extra /= ctx->line.count; + } + + for (size_t i = 0; i < ctx->line.count; i++) { + struct flex_line_data *line = &ctx->line.data[i]; + + line_pos += pre_multiplier * line->cross_size; + line->pos = line_pos; + line_pos += post_multiplier * line->cross_size + + extra + extra_remainder; + + layout_flex__place_line_items_cross(ctx, line, + extra + extra_remainder); + + if (extra_remainder > 0) { + extra_remainder--; + } + } +} + +/** + * Layout a flex container. + * + * \param[in] flex table to layout + * \param[in] available_width width of containing block + * \param[in] content memory pool for any new boxes + * \return true on success, false on memory exhaustion + */ +bool layout_flex(struct box *flex, int available_width, + html_content *content) +{ + int max_height, min_height; + struct flex_ctx *ctx; + bool success = false; + + ctx = layout_flex_ctx__create(content, flex); + if (ctx == NULL) { + return false; + } + + NSLOG(flex, DEEPDEBUG, "box %p: %s, available_width %i, width: %i", + flex, ctx->horizontal ? "horizontal" : "vertical", + available_width, flex->width); + + layout_find_dimensions( + ctx->unit_len_ctx, available_width, -1, + flex, flex->style, NULL, &flex->height, + NULL, NULL, &max_height, &min_height, + flex->margin, flex->padding, flex->border); + + available_width = min(available_width, flex->width); + + if (ctx->horizontal) { + ctx->available_main = available_width; + ctx->available_cross = ctx->flex->height; + } else { + ctx->available_main = ctx->flex->height; + ctx->available_cross = available_width; + } + + NSLOG(flex, DEEPDEBUG, "box %p: available_main: %i", + flex, ctx->available_main); + NSLOG(flex, DEEPDEBUG, "box %p: available_cross: %i", + flex, ctx->available_cross); + + layout_flex_ctx__populate_item_data(ctx, flex, available_width); + + /* Place items onto lines. */ + success = layout_flex__collect_items_into_lines(ctx); + if (!success) { + goto cleanup; + } + + layout_flex__place_lines(ctx); + + if (flex->height == AUTO) { + flex->height = ctx->horizontal ? + ctx->cross_size : + ctx->main_size; + } + + if (flex->height != AUTO) { + if (max_height >= 0 && flex->height > max_height) { + flex->height = max_height; + } + if (min_height > 0 && flex->height < min_height) { + flex->height = min_height; + } + } + + success = true; + +cleanup: + layout_flex_ctx__destroy(ctx); + + NSLOG(flex, DEEPDEBUG, "box %p: %s: w: %i, h: %i", flex, + success ? "success" : "failure", + flex->width, flex->height); + return success; +} diff --git a/content/handlers/html/layout_internal.h b/content/handlers/html/layout_internal.h new file mode 100644 index 000000000..d094462ec --- /dev/null +++ b/content/handlers/html/layout_internal.h @@ -0,0 +1,738 @@ +/* + * Copyright 2003 James Bursa <bursa@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file + * HTML layout private interface. + */ + +#ifndef NETSURF_HTML_LAYOUT_INTERNAL_H +#define NETSURF_HTML_LAYOUT_INTERNAL_H + +#define AUTO INT_MIN + +/* Fixed point percentage (a) of an integer (b), to an integer */ +#define FPCT_OF_INT_TOINT(a, b) (FIXTOINT(FDIV((a * b), F_100))) + +/** + * Layout a block formatting context. + * + * \param block BLOCK, INLINE_BLOCK, or TABLE_CELL to layout + * \param viewport_height Height of viewport in pixels or -ve if unknown + * \param content Memory pool for any new boxes + * \return true on success, false on memory exhaustion + * + * This function carries out layout of a block and its children, as described + * in CSS 2.1 9.4.1. + */ +bool layout_block_context( + struct box *block, + int viewport_height, + html_content *content); + +/** + * Layout a table. + * + * \param table table to layout + * \param available_width width of containing block + * \param content memory pool for any new boxes + * \return true on success, false on memory exhaustion + */ +bool layout_table( + struct box *table, + int available_width, + html_content *content); + +/** + * Layout a flex container. + * + * \param[in] flex table to layout + * \param[in] available_width width of containing block + * \param[in] content memory pool for any new boxes + * \return true on success, false on memory exhaustion + */ +bool layout_flex( + struct box *flex, + int available_width, + html_content *content); + +typedef uint8_t (*css_len_func)( + const css_computed_style *style, + css_fixed *length, css_unit *unit); +typedef uint8_t (*css_border_style_func)( + const css_computed_style *style); +typedef uint8_t (*css_border_color_func)( + const css_computed_style *style, + css_color *color); + +/** Array of per-side access functions for computed style margins. */ +extern const css_len_func margin_funcs[4]; + +/** Array of per-side access functions for computed style paddings. */ +extern const css_len_func padding_funcs[4]; + +/** Array of per-side access functions for computed style border_widths. */ +extern const css_len_func border_width_funcs[4]; + +/** Array of per-side access functions for computed style border styles. */ +extern const css_border_style_func border_style_funcs[4]; + +/** Array of per-side access functions for computed style border colors. */ +extern const css_border_color_func border_color_funcs[4]; + +/** Layout helper: Check whether box is a float. */ +static inline bool lh__box_is_float_box(const struct box *b) +{ + return b->type == BOX_FLOAT_LEFT || + b->type == BOX_FLOAT_RIGHT; +} + +/** Layout helper: Check whether box takes part in inline flow. */ +static inline bool lh__box_is_inline_flow(const struct box *b) +{ + return b->type == BOX_INLINE || + b->type == BOX_INLINE_FLEX || + b->type == BOX_INLINE_BLOCK || + b->type == BOX_TEXT || + b->type == BOX_INLINE_END; +} + +/** Layout helper: Check whether box takes part in inline flow. */ +static inline bool lh__box_is_flex_container(const struct box *b) +{ + return b->type == BOX_FLEX || + b->type == BOX_INLINE_FLEX; +} + +/** Layout helper: Check whether box takes part in inline flow. */ +static inline bool lh__box_is_flex_item(const struct box *b) +{ + return (b->parent != NULL) && lh__box_is_flex_container(b->parent); +} + +/** Layout helper: Check whether box is inline level. (Includes BR.) */ +static inline bool lh__box_is_inline_level(const struct box *b) +{ + return lh__box_is_inline_flow(b) || + b->type == BOX_BR; +} + +/** Layout helper: Check whether box is inline level. (Includes BR, floats.) */ +static inline bool lh__box_is_inline_content(const struct box *b) +{ + return lh__box_is_float_box(b) || + lh__box_is_inline_level(b); +} + +/** Layout helper: Check whether box is an object. */ +static inline bool lh__box_is_object(const struct box *b) +{ + return b->object || + (b->flags & (IFRAME | REPLACE_DIM)); +} + +/** Layout helper: Check whether box is replaced. */ +static inline bool lh__box_is_replace(const struct box *b) +{ + return b->gadget || + lh__box_is_object(b); +} + +/** Layout helper: Check for CSS border on given side. */ +static inline bool lh__have_border( + enum box_side side, + const css_computed_style *style) +{ + return border_style_funcs[side](style) != CSS_BORDER_STYLE_NONE; +} + +static inline bool lh__box_is_absolute(const struct box *b) +{ + return css_computed_position(b->style) == CSS_POSITION_ABSOLUTE || + css_computed_position(b->style) == CSS_POSITION_FIXED; +} + +static inline bool lh__flex_main_is_horizontal(const struct box *flex) +{ + const css_computed_style *style = flex->style; + + assert(style != NULL); + + switch (css_computed_flex_direction(style)) { + default: /* Fallthrough. */ + case CSS_FLEX_DIRECTION_ROW: /* Fallthrough. */ + case CSS_FLEX_DIRECTION_ROW_REVERSE: + return true; + case CSS_FLEX_DIRECTION_COLUMN: /* Fallthrough. */ + case CSS_FLEX_DIRECTION_COLUMN_REVERSE: + return false; + } +} + +static inline bool lh__flex_direction_reversed(const struct box *flex) +{ + switch (css_computed_flex_direction(flex->style)) { + default: /* Fallthrough. */ + case CSS_FLEX_DIRECTION_ROW_REVERSE: /* Fallthrough. */ + case CSS_FLEX_DIRECTION_COLUMN_REVERSE: + return true; + case CSS_FLEX_DIRECTION_ROW: /* Fallthrough. */ + case CSS_FLEX_DIRECTION_COLUMN: + return false; + } +} + +static inline int lh__non_auto_margin(const struct box *b, enum box_side side) +{ + return (b->margin[side] == AUTO) ? 0 : b->margin[side]; +} + +static inline int lh__delta_outer_height(const struct box *b) +{ + return b->padding[TOP] + + b->padding[BOTTOM] + + b->border[TOP].width + + b->border[BOTTOM].width + + lh__non_auto_margin(b, TOP) + + lh__non_auto_margin(b, BOTTOM); +} + +static inline int lh__delta_outer_width(const struct box *b) +{ + return b->padding[LEFT] + + b->padding[RIGHT] + + b->border[LEFT].width + + b->border[RIGHT].width + + lh__non_auto_margin(b, LEFT) + + lh__non_auto_margin(b, RIGHT); +} + +static inline int lh__delta_outer_main( + const struct box *flex, + const struct box *b) +{ + if (lh__flex_main_is_horizontal(flex)) { + return lh__delta_outer_width(b); + } else { + return lh__delta_outer_height(b); + } +} + +static inline int lh__delta_outer_cross( + const struct box *flex, + const struct box *b) +{ + if (lh__flex_main_is_horizontal(flex) == false) { + return lh__delta_outer_width(b); + } else { + return lh__delta_outer_height(b); + } +} + +static inline int *lh__box_size_main_ptr( + bool horizontal, + struct box *b) +{ + return horizontal ? &b->width : &b->height; +} + +static inline int *lh__box_size_cross_ptr( + bool horizontal, + struct box *b) +{ + return horizontal ? &b->height : &b->width; +} + +static inline int lh__box_size_main( + bool horizontal, + const struct box *b) +{ + return horizontal ? b->width : b->height; +} + +static inline int lh__box_size_cross( + bool horizontal, + const struct box *b) +{ + return horizontal ? b->height : b->width; +} + +static inline bool lh__box_size_cross_is_auto( + bool horizontal, + struct box *b) +{ + css_fixed length; + css_unit unit; + + if (horizontal) { + return css_computed_height(b->style, + &length, &unit) == CSS_HEIGHT_AUTO; + } else { + return css_computed_width(b->style, + &length, &unit) == CSS_WIDTH_AUTO; + } +} + +static inline enum css_align_self_e lh__box_align_self( + const struct box *flex, + const struct box *item) +{ + enum css_align_self_e align_self = css_computed_align_self(item->style); + + if (align_self == CSS_ALIGN_SELF_AUTO) { + align_self = css_computed_align_items(flex->style); + } + + return align_self; +} + +/** + * Determine width of margin, borders, and padding on one side of a box. + * + * \param unit_len_ctx CSS length conversion context for document + * \param style style to measure + * \param side side of box to measure + * \param margin whether margin width is required + * \param border whether border width is required + * \param padding whether padding width is required + * \param fixed increased by sum of fixed margin, border, and padding + * \param frac increased by sum of fractional margin and padding + */ +static inline void calculate_mbp_width( + const css_unit_ctx *unit_len_ctx, + const css_computed_style *style, + unsigned int side, + bool margin, + bool border, + bool padding, + int *fixed, + float *frac) +{ + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + assert(style); + + /* margin */ + if (margin) { + enum css_margin_e type; + + type = margin_funcs[side](style, &value, &unit); + if (type == CSS_MARGIN_SET) { + if (unit == CSS_UNIT_PCT) { + *frac += FIXTOFLT(FDIV(value, F_100)); + } else { + *fixed += FIXTOINT(css_unit_len2device_px( + style, unit_len_ctx, + value, unit)); + } + } + } + + /* border */ + if (border) { + if (lh__have_border(side, style)) { + border_width_funcs[side](style, &value, &unit); + + *fixed += FIXTOINT(css_unit_len2device_px( + style, unit_len_ctx, + value, unit)); + } + } + + /* padding */ + if (padding) { + padding_funcs[side](style, &value, &unit); + if (unit == CSS_UNIT_PCT) { + *frac += FIXTOFLT(FDIV(value, F_100)); + } else { + *fixed += FIXTOINT(css_unit_len2device_px( + style, unit_len_ctx, + value, unit)); + } + } +} + +/** + * Adjust a specified width or height for the box-sizing property. + * + * This turns the specified dimension into a content-box dimension. + * + * \param unit_len_ctx Length conversion context + * \param box gadget to adjust dimensions of + * \param available_width width of containing block + * \param setwidth set true if the dimension to be tweaked is a width, + * else set false for a height + * \param dimension current value for given width/height dimension. + * updated to new value after consideration of + * gadget properties. + */ +static inline void layout_handle_box_sizing( + const css_unit_ctx *unit_len_ctx, + const struct box *box, + int available_width, + bool setwidth, + int *dimension) +{ + enum css_box_sizing_e bs; + + assert(box && box->style); + + bs = css_computed_box_sizing(box->style); + + if (bs == CSS_BOX_SIZING_BORDER_BOX) { + int orig = *dimension; + int fixed = 0; + float frac = 0; + + calculate_mbp_width(unit_len_ctx, box->style, + setwidth ? LEFT : TOP, + false, true, true, &fixed, &frac); + calculate_mbp_width(unit_len_ctx, box->style, + setwidth ? RIGHT : BOTTOM, + false, true, true, &fixed, &frac); + orig -= frac * available_width + fixed; + *dimension = orig > 0 ? orig : 0; + } +} + +/** + * Calculate width, height, and thickness of margins, paddings, and borders. + * + * \param unit_len_ctx Length conversion context + * \param available_width width of containing block + * \param viewport_height height of viewport in pixels or -ve if unknown + * \param box current box + * \param style style giving width, height, margins, paddings, + * and borders + * \param width updated to width, may be NULL + * \param height updated to height, may be NULL + * \param max_width updated to max-width, may be NULL + * \param min_width updated to min-width, may be NULL + * \param max_height updated to max-height, may be NULL + * \param min_height updated to min-height, may be NULL + * \param margin filled with margins, may be NULL + * \param padding filled with paddings, may be NULL + * \param border filled with border widths, may be NULL + */ +static inline void layout_find_dimensions( + const css_unit_ctx *unit_len_ctx, + int available_width, + int viewport_height, + const struct box *box, + const css_computed_style *style, + int *width, + int *height, + int *max_width, + int *min_width, + int *max_height, + int *min_height, + int margin[4], + int padding[4], + struct box_border border[4]) +{ + struct box *containing_block = NULL; + unsigned int i; + + if (width) { + enum css_width_e wtype; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + wtype = css_computed_width(style, &value, &unit); + + if (wtype == CSS_WIDTH_SET) { + if (unit == CSS_UNIT_PCT) { + *width = FPCT_OF_INT_TOINT( + value, available_width); + } else { + *width = FIXTOINT(css_unit_len2device_px( + style, unit_len_ctx, + value, unit)); + } + } else { + *width = AUTO; + } + + if (*width != AUTO) { + layout_handle_box_sizing(unit_len_ctx, box, + available_width, true, width); + } + } + + if (height) { + enum css_height_e htype; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + htype = css_computed_height(style, &value, &unit); + + if (htype == CSS_HEIGHT_SET) { + if (unit == CSS_UNIT_PCT) { + enum css_height_e cbhtype; + + if (css_computed_position(box->style) == + CSS_POSITION_ABSOLUTE && + box->parent) { + /* Box is absolutely positioned */ + assert(box->float_container); + containing_block = box->float_container; + } else if (box->float_container && + css_computed_position(box->style) != + CSS_POSITION_ABSOLUTE && + (css_computed_float(box->style) == + CSS_FLOAT_LEFT || + css_computed_float(box->style) == + CSS_FLOAT_RIGHT)) { + /* Box is a float */ + assert(box->parent && + box->parent->parent && + box->parent->parent->parent); + + containing_block = + box->parent->parent->parent; + } else if (box->parent && box->parent->type != + BOX_INLINE_CONTAINER) { + /* Box is a block level element */ + containing_block = box->parent; + } else if (box->parent && box->parent->type == + BOX_INLINE_CONTAINER) { + /* Box is an inline block */ + assert(box->parent->parent); + containing_block = box->parent->parent; + } + + if (containing_block) { + css_fixed f = 0; + css_unit u = CSS_UNIT_PX; + + cbhtype = css_computed_height( + containing_block->style, + &f, &u); + } + + if (containing_block && + containing_block->height != AUTO && + (css_computed_position(box->style) == + CSS_POSITION_ABSOLUTE || + cbhtype == CSS_HEIGHT_SET)) { + /* Box is absolutely positioned or its + * containing block has a valid + * specified height. + * (CSS 2.1 Section 10.5) */ + *height = FPCT_OF_INT_TOINT(value, + containing_block->height); + } else if ((!box->parent || + !box->parent->parent) && + viewport_height >= 0) { + /* If root element or it's child + * (HTML or BODY) */ + *height = FPCT_OF_INT_TOINT(value, + viewport_height); + } else { + /* precentage height not permissible + * treat height as auto */ + *height = AUTO; + } + } else { + *height = FIXTOINT(css_unit_len2device_px( + style, unit_len_ctx, + value, unit)); + } + } else { + *height = AUTO; + } + + if (*height != AUTO) { + layout_handle_box_sizing(unit_len_ctx, box, + available_width, false, height); + } + } + + if (max_width) { + enum css_max_width_e type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + type = css_computed_max_width(style, &value, &unit); + + if (type == CSS_MAX_WIDTH_SET) { + if (unit == CSS_UNIT_PCT) { + *max_width = FPCT_OF_INT_TOINT(value, + available_width); + } else { + *max_width = FIXTOINT(css_unit_len2device_px( + style, unit_len_ctx, + value, unit)); + } + } else { + /* Inadmissible */ + *max_width = -1; + } + + if (*max_width != -1) { + layout_handle_box_sizing(unit_len_ctx, box, + available_width, true, max_width); + } + } + + if (min_width) { + enum css_min_width_e type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + type = ns_computed_min_width(style, &value, &unit); + + if (type == CSS_MIN_WIDTH_SET) { + if (unit == CSS_UNIT_PCT) { + *min_width = FPCT_OF_INT_TOINT(value, + available_width); + } else { + *min_width = FIXTOINT(css_unit_len2device_px( + style, unit_len_ctx, + value, unit)); + } + } else { + /* Inadmissible */ + *min_width = 0; + } + + if (*min_width != 0) { + layout_handle_box_sizing(unit_len_ctx, box, + available_width, true, min_width); + } + } + + if (max_height) { + enum css_max_height_e type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + type = css_computed_max_height(style, &value, &unit); + + if (type == CSS_MAX_HEIGHT_SET) { + if (unit == CSS_UNIT_PCT) { + /* TODO: handle percentage */ + *max_height = -1; + } else { + *max_height = FIXTOINT(css_unit_len2device_px( + style, unit_len_ctx, + value, unit)); + } + } else { + /* Inadmissible */ + *max_height = -1; + } + } + + if (min_height) { + enum css_min_height_e type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + type = ns_computed_min_height(style, &value, &unit); + + if (type == CSS_MIN_HEIGHT_SET) { + if (unit == CSS_UNIT_PCT) { + /* TODO: handle percentage */ + *min_height = 0; + } else { + *min_height = FIXTOINT(css_unit_len2device_px( + style, unit_len_ctx, + value, unit)); + } + } else { + /* Inadmissible */ + *min_height = 0; + } + } + + for (i = 0; i != 4; i++) { + if (margin) { + enum css_margin_e type = CSS_MARGIN_AUTO; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + type = margin_funcs[i](style, &value, &unit); + + if (type == CSS_MARGIN_SET) { + if (unit == CSS_UNIT_PCT) { + margin[i] = FPCT_OF_INT_TOINT(value, + available_width); + } else { + margin[i] = FIXTOINT(css_unit_len2device_px( + style, unit_len_ctx, + value, unit)); + } + } else { + margin[i] = AUTO; + } + } + + if (padding) { + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + padding_funcs[i](style, &value, &unit); + + if (unit == CSS_UNIT_PCT) { + padding[i] = FPCT_OF_INT_TOINT(value, + available_width); + } else { + padding[i] = FIXTOINT(css_unit_len2device_px( + style, unit_len_ctx, + value, unit)); + } + } + + /* Table cell borders are populated in table.c */ + if (border && box->type != BOX_TABLE_CELL) { + enum css_border_style_e bstyle = CSS_BORDER_STYLE_NONE; + css_color color = 0; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + border_width_funcs[i](style, &value, &unit); + bstyle = border_style_funcs[i](style); + border_color_funcs[i](style, &color); + + border[i].style = bstyle; + border[i].c = color; + + if (bstyle == CSS_BORDER_STYLE_HIDDEN || + bstyle == CSS_BORDER_STYLE_NONE) + /* spec unclear: following Mozilla */ + border[i].width = 0; + else + border[i].width = FIXTOINT(css_unit_len2device_px( + style, unit_len_ctx, + value, unit)); + + /* Special case for border-collapse: make all borders + * on table/table-row-group/table-row zero width. */ + if (css_computed_border_collapse(style) == + CSS_BORDER_COLLAPSE_COLLAPSE && + (box->type == BOX_TABLE || + box->type == BOX_TABLE_ROW_GROUP || + box->type == BOX_TABLE_ROW)) + border[i].width = 0; + } + } +} + +#endif diff --git a/content/handlers/html/redraw_border.c b/content/handlers/html/redraw_border.c index 39ed432cd..3a1f6f308 100644 --- a/content/handlers/html/redraw_border.c +++ b/content/handlers/html/redraw_border.c @@ -25,6 +25,7 @@ #include <stdbool.h> #include <stdlib.h> +#include "utils/utils.h" #include "utils/log.h" #include "netsurf/plotters.h" #include "netsurf/css.h" @@ -121,7 +122,7 @@ html_redraw_border_plot(const int side, switch (style) { case CSS_BORDER_STYLE_DOTTED: plot_style_bdr.stroke_type = PLOT_OP_TYPE_DOT; - /* fall through */ + fallthrough; case CSS_BORDER_STYLE_DASHED: rect.x0 = (p[0] + p[2]) / 2; rect.y0 = (p[1] + p[3]) / 2; @@ -131,7 +132,7 @@ html_redraw_border_plot(const int side, break; case CSS_BORDER_STYLE_SOLID: - /* fall through to default */ + /* solid is the default */ default: if (rectangular || thickness == 1) { @@ -190,7 +191,7 @@ html_redraw_border_plot(const int side, case CSS_BORDER_STYLE_GROOVE: light = 3 - light; - /* fall through */ + fallthrough; case CSS_BORDER_STYLE_RIDGE: /* choose correct colours for each part of the border line */ if (light <= 1) { @@ -300,7 +301,7 @@ html_redraw_border_plot(const int side, case CSS_BORDER_STYLE_INSET: light = (light + 2) % 4; - /* fall through */ + fallthrough; case CSS_BORDER_STYLE_OUTSET: /* choose correct colours for each part of the border line */ switch (light) { diff --git a/content/handlers/html/script.c b/content/handlers/html/script.c index 962386d68..554fc4f70 100644 --- a/content/handlers/html/script.c +++ b/content/handlers/html/script.c @@ -495,6 +495,7 @@ exec_src_script(html_content *c, switch (script_type) { case HTML_SCRIPT_SYNC: ret = DOM_HUBBUB_HUBBUB_ERR | HUBBUB_PAUSED; + break; case HTML_SCRIPT_ASYNC: break; diff --git a/content/handlers/html/table.c b/content/handlers/html/table.c index 4ffcceab9..f8762e862 100644 --- a/content/handlers/html/table.c +++ b/content/handlers/html/table.c @@ -26,6 +26,7 @@ #include <dom/dom.h> #include "utils/log.h" +#include "utils/utils.h" #include "utils/talloc.h" #include "css/utils.h" @@ -93,27 +94,27 @@ table_border_is_more_eyecatching(const css_unit_ctx *unit_len_ctx, /* 3b -- sort by style */ switch (a->style) { - case CSS_BORDER_STYLE_DOUBLE: impact++; /* Fall through */ - case CSS_BORDER_STYLE_SOLID: impact++; /* Fall through */ - case CSS_BORDER_STYLE_DASHED: impact++; /* Fall through */ - case CSS_BORDER_STYLE_DOTTED: impact++; /* Fall through */ - case CSS_BORDER_STYLE_RIDGE: impact++; /* Fall through */ - case CSS_BORDER_STYLE_OUTSET: impact++; /* Fall through */ - case CSS_BORDER_STYLE_GROOVE: impact++; /* Fall through */ - case CSS_BORDER_STYLE_INSET: impact++; /* Fall through */ + case CSS_BORDER_STYLE_DOUBLE: impact++; fallthrough; + case CSS_BORDER_STYLE_SOLID: impact++; fallthrough; + case CSS_BORDER_STYLE_DASHED: impact++; fallthrough; + case CSS_BORDER_STYLE_DOTTED: impact++; fallthrough; + case CSS_BORDER_STYLE_RIDGE: impact++; fallthrough; + case CSS_BORDER_STYLE_OUTSET: impact++; fallthrough; + case CSS_BORDER_STYLE_GROOVE: impact++; fallthrough; + case CSS_BORDER_STYLE_INSET: impact++; fallthrough; default: break; } switch (b->style) { - case CSS_BORDER_STYLE_DOUBLE: impact--; /* Fall through */ - case CSS_BORDER_STYLE_SOLID: impact--; /* Fall through */ - case CSS_BORDER_STYLE_DASHED: impact--; /* Fall through */ - case CSS_BORDER_STYLE_DOTTED: impact--; /* Fall through */ - case CSS_BORDER_STYLE_RIDGE: impact--; /* Fall through */ - case CSS_BORDER_STYLE_OUTSET: impact--; /* Fall through */ - case CSS_BORDER_STYLE_GROOVE: impact--; /* Fall through */ - case CSS_BORDER_STYLE_INSET: impact--; /* Fall through */ + case CSS_BORDER_STYLE_DOUBLE: impact--; fallthrough; + case CSS_BORDER_STYLE_SOLID: impact--; fallthrough; + case CSS_BORDER_STYLE_DASHED: impact--; fallthrough; + case CSS_BORDER_STYLE_DOTTED: impact--; fallthrough; + case CSS_BORDER_STYLE_RIDGE: impact--; fallthrough; + case CSS_BORDER_STYLE_OUTSET: impact--; fallthrough; + case CSS_BORDER_STYLE_GROOVE: impact--; fallthrough; + case CSS_BORDER_STYLE_INSET: impact--; fallthrough; default: break; } @@ -128,20 +129,20 @@ table_border_is_more_eyecatching(const css_unit_ctx *unit_len_ctx, /** \todo COL/COL_GROUP */ switch (a_src) { - case BOX_TABLE_CELL: impact++; /* Fall through */ - case BOX_TABLE_ROW: impact++; /* Fall through */ - case BOX_TABLE_ROW_GROUP: impact++; /* Fall through */ - case BOX_TABLE: impact++; /* Fall through */ + case BOX_TABLE_CELL: impact++; fallthrough; + case BOX_TABLE_ROW: impact++; fallthrough; + case BOX_TABLE_ROW_GROUP: impact++; fallthrough; + case BOX_TABLE: impact++; fallthrough; default: break; } /** \todo COL/COL_GROUP */ switch (b_src) { - case BOX_TABLE_CELL: impact--; /* Fall through */ - case BOX_TABLE_ROW: impact--; /* Fall through */ - case BOX_TABLE_ROW_GROUP: impact--; /* Fall through */ - case BOX_TABLE: impact--; /* Fall through */ + case BOX_TABLE_CELL: impact--; fallthrough; + case BOX_TABLE_ROW: impact--; fallthrough; + case BOX_TABLE_ROW_GROUP: impact--; fallthrough; + case BOX_TABLE: impact--; fallthrough; default: break; } |