diff options
Diffstat (limited to 'content/handlers/html')
26 files changed, 2913 insertions, 1093 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 7bfc35e44..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( @@ -282,7 +286,8 @@ box_get_style(html_content *c, ctx.parent_style = parent_style; /* Select style for element */ - styles = nscss_get_style(&ctx, n, &c->media, inline_style); + styles = nscss_get_style(&ctx, n, &c->media, &c->unit_len_ctx, + inline_style); /* No longer need inline style */ if (inline_style != NULL) @@ -437,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. @@ -450,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; @@ -548,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. @@ -571,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, @@ -586,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; @@ -624,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 @@ -673,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 */ @@ -689,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) == @@ -1341,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 df9a1b4e2..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" @@ -56,7 +57,7 @@ enum box_walk_dir { /** * Determine if a point lies within a box. * - * \param[in] len_ctx CSS length conversion context to use. + * \param[in] unit_len_ctx CSS length conversion context to use. * \param[in] box Box to consider * \param[in] x Coordinate relative to box * \param[in] y Coordinate relative to box @@ -71,7 +72,7 @@ enum box_walk_dir { * This is a helper function for box_at_point(). */ static bool -box_contains_point(const nscss_len_ctx *len_ctx, +box_contains_point(const css_unit_ctx *unit_len_ctx, const struct box *box, int x, int y, @@ -101,30 +102,34 @@ box_contains_point(const nscss_len_ctx *len_ctx, /* Adjust rect to css clip region */ if (css_rect.left_auto == false) { - r.x0 += FIXTOINT(nscss_len2px(len_ctx, - css_rect.left, - css_rect.lunit, - box->style)); + r.x0 += FIXTOINT(css_unit_len2device_px( + box->style, + unit_len_ctx, + css_rect.left, + css_rect.lunit)); } if (css_rect.top_auto == false) { - r.y0 += FIXTOINT(nscss_len2px(len_ctx, - css_rect.top, - css_rect.tunit, - box->style)); + r.y0 += FIXTOINT(css_unit_len2device_px( + box->style, + unit_len_ctx, + css_rect.top, + css_rect.tunit)); } if (css_rect.right_auto == false) { r.x1 = box->border[LEFT].width + - FIXTOINT(nscss_len2px(len_ctx, - css_rect.right, - css_rect.runit, - box->style)); + FIXTOINT(css_unit_len2device_px( + box->style, + unit_len_ctx, + css_rect.right, + css_rect.runit)); } if (css_rect.bottom_auto == false) { r.y1 = box->border[TOP].width + - FIXTOINT(nscss_len2px(len_ctx, - css_rect.bottom, - css_rect.bunit, - box->style)); + FIXTOINT(css_unit_len2device_px( + box->style, + unit_len_ctx, + css_rect.bottom, + css_rect.bunit)); } /* Test if point is in clipped box */ @@ -208,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 { @@ -575,7 +580,7 @@ void box_bounds(struct box *box, struct rect *r) /* Exported function documented in html/box.h */ struct box * -box_at_point(const nscss_len_ctx *len_ctx, +box_at_point(const css_unit_ctx *unit_len_ctx, struct box *box, const int x, const int y, int *box_x, int *box_y) @@ -587,7 +592,7 @@ box_at_point(const nscss_len_ctx *len_ctx, skip_children = false; while ((box = box_next_xy(box, box_x, box_y, skip_children))) { - if (box_contains_point(len_ctx, box, x - *box_x, y - *box_y, + if (box_contains_point(unit_len_ctx, box, x - *box_x, y - *box_y, &physically)) { *box_x -= scrollbar_get_offset(box->scroll_x); *box_y -= scrollbar_get_offset(box->scroll_y); @@ -656,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); @@ -720,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 d50b84813..a218326d8 100644 --- a/content/handlers/html/box_inspect.h +++ b/content/handlers/html/box_inspect.h @@ -46,7 +46,7 @@ void box_bounds(struct box *box, struct rect *r); /** * Find the boxes at a point. * - * \param len_ctx CSS length conversion context for document. + * \param unit_len_ctx CSS length conversion context for document. * \param box box to search children of * \param x point to find, in global document coordinates * \param y point to find, in global document coordinates @@ -62,12 +62,12 @@ void box_bounds(struct box *box, struct rect *r); * struct box *box = top_of_document_to_search; * int box_x = 0, box_y = 0; * - * while ((box = box_at_point(len_ctx, box, x, y, &box_x, &box_y))) { + * while ((box = box_at_point(unit_len_ctx, box, x, y, &box_x, &box_y))) { * // process box * } * \endcode */ -struct box *box_at_point(const nscss_len_ctx *len_ctx, struct box *box, const int x, const int y, int *box_x, int *box_y); +struct box *box_at_point(const css_unit_ctx *unit_len_ctx, struct box *box, const int x, const int y, int *box_x, int *box_y); /** @@ -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 b7032da55..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: @@ -190,7 +191,8 @@ box_normalise_table_row(struct box *row, ctx.base_url = c->base_url; ctx.universal = c->universal; - style = nscss_get_blank_style(&ctx, row->style); + style = nscss_get_blank_style(&ctx, &c->unit_len_ctx, + row->style); if (style == NULL) return false; @@ -210,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 || @@ -237,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: @@ -313,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: @@ -326,7 +331,8 @@ box_normalise_table_row_group(struct box *row_group, ctx.base_url = c->base_url; ctx.universal = c->universal; - style = nscss_get_blank_style(&ctx, row_group->style); + style = nscss_get_blank_style(&ctx, &c->unit_len_ctx, + row_group->style); if (style == NULL) return false; @@ -346,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 || @@ -375,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: @@ -402,7 +410,8 @@ box_normalise_table_row_group(struct box *row_group, ctx.base_url = c->base_url; ctx.universal = c->universal; - style = nscss_get_blank_style(&ctx, row_group->style); + style = nscss_get_blank_style(&ctx, &c->unit_len_ctx, + row_group->style); if (style == NULL) { return false; } @@ -533,6 +542,7 @@ box_normalise_table_spans(struct box *table, ctx.universal = c->universal; style = nscss_get_blank_style(&ctx, + &c->unit_len_ctx, table_row->style); if (style == NULL) return false; @@ -644,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: @@ -657,7 +668,8 @@ box_normalise_table(struct box *table, const struct box *root, html_content * c) ctx.base_url = c->base_url; ctx.universal = c->universal; - style = nscss_get_blank_style(&ctx, table->style); + style = nscss_get_blank_style(&ctx, &c->unit_len_ctx, + table->style); if (style == NULL) { free(col_info.spans); return false; @@ -681,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 || @@ -711,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: @@ -744,7 +758,8 @@ box_normalise_table(struct box *table, const struct box *root, html_content * c) ctx.base_url = c->base_url; ctx.universal = c->universal; - style = nscss_get_blank_style(&ctx, table->style); + style = nscss_get_blank_style(&ctx, &c->unit_len_ctx, + table->style); if (style == NULL) { free(col_info.spans); return false; @@ -759,7 +774,8 @@ box_normalise_table(struct box *table, const struct box *root, html_content * c) } row_group->type = BOX_TABLE_ROW_GROUP; - style = nscss_get_blank_style(&ctx, row_group->style); + style = nscss_get_blank_style(&ctx, &c->unit_len_ctx, + row_group->style); if (style == NULL) { box_free(row_group); free(col_info.spans); @@ -799,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, @@ -829,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 */ @@ -845,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); } @@ -863,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: @@ -881,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) @@ -913,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) @@ -928,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: @@ -948,7 +1155,8 @@ box_normalise_block(struct box *block, const struct box *root, html_content *c) ctx.base_url = c->base_url; ctx.universal = c->universal; - style = nscss_get_blank_style(&ctx, block->style); + style = nscss_get_blank_style(&ctx, &c->unit_len_ctx, + block->style); if (style == NULL) return false; 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..aace1c8f9 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 */ @@ -1202,7 +1212,7 @@ box_image(dom_node *n, ok = html_fetch_object(content, url, box, image_types, false); nsurl_unref(url); - wtype = css_computed_width(box->style, &value, &wunit); + wtype = css_computed_width_static(box->style, &value, &wunit); htype = css_computed_height(box->style, &value, &hunit); if (wtype == CSS_WIDTH_SET && @@ -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/font.c b/content/handlers/html/font.c index 7ebe16825..4a64759d7 100644 --- a/content/handlers/html/font.c +++ b/content/handlers/html/font.c @@ -133,7 +133,7 @@ static plot_font_flags_t plot_font_flags(enum css_font_style_e style, /* exported function documented in html/font.h */ void font_plot_style_from_css( - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, const css_computed_style *css, plot_font_style_t *fstyle) { @@ -147,7 +147,8 @@ void font_plot_style_from_css( fstyle->families = families; css_computed_font_size(css, &length, &unit); - fstyle->size = FIXTOINT(FMUL(nscss_len2pt(len_ctx, length, unit), + fstyle->size = FIXTOINT(FMUL(css_unit_font_size_len2pt(css, + unit_len_ctx, length, unit), INTTOFIX(PLOT_STYLE_SCALE))); /* Clamp font size to configured minimum */ diff --git a/content/handlers/html/font.h b/content/handlers/html/font.h index 5f69ee7d3..26f5bf289 100644 --- a/content/handlers/html/font.h +++ b/content/handlers/html/font.h @@ -32,11 +32,11 @@ struct plot_font_style; /** * Populate a font style using data from a computed CSS style * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param css Computed style to consider * \param fstyle Font style to populate */ -void font_plot_style_from_css(const nscss_len_ctx *len_ctx, +void font_plot_style_from_css(const css_unit_ctx *unit_len_ctx, const css_computed_style *css, struct plot_font_style *fstyle); diff --git a/content/handlers/html/form.c b/content/handlers/html/form.c index 01e624417..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; @@ -1594,12 +1595,12 @@ form_open_select_menu(void *client_data, box->border[RIGHT].width + box->padding[RIGHT] + box->border[LEFT].width + box->padding[LEFT]; - font_plot_style_from_css(&html->len_ctx, control->box->style, - &fstyle); + font_plot_style_from_css(&html->unit_len_ctx, + control->box->style, &fstyle); menu->f_size = fstyle.size; menu->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2), - FMUL(nscss_screen_dpi, + FMUL(html->unit_len_ctx.device_dpi, INTTOFIX(fstyle.size / PLOT_STYLE_SCALE)))), F_72)); @@ -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 5b74e42da..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); @@ -305,6 +305,9 @@ html_proceed_to_done(html_content *html) static void html_get_dimensions(html_content *htmlc) { + css_fixed device_dpi = nscss_screen_dpi; + unsigned f_size; + unsigned f_min; unsigned w; unsigned h; union content_msg_data msg_data = { @@ -316,13 +319,22 @@ static void html_get_dimensions(html_content *htmlc) content_broadcast(&htmlc->base, CONTENT_MSG_GETDIMS, &msg_data); - htmlc->media.width = nscss_pixels_physical_to_css(INTTOFIX(w)); - htmlc->media.height = nscss_pixels_physical_to_css(INTTOFIX(h)); - htmlc->media.client_font_size = - FDIV(INTTOFIX(nsoption_int(font_size)), F_10); - htmlc->media.client_line_height = - FMUL(nscss_len2px(NULL, htmlc->media.client_font_size, - CSS_UNIT_PT, NULL), FLTTOFIX(1.33)); + + w = css_unit_device2css_px(INTTOFIX(w), device_dpi); + h = css_unit_device2css_px(INTTOFIX(h), device_dpi); + + htmlc->media.width = w; + htmlc->media.height = h; + htmlc->unit_len_ctx.viewport_width = w; + htmlc->unit_len_ctx.viewport_height = h; + htmlc->unit_len_ctx.device_dpi = device_dpi; + + /** \todo Change nsoption font sizes to px. */ + f_size = FDIV(FMUL(F_96, FDIV(INTTOFIX(nsoption_int(font_size)), F_10)), F_72); + f_min = FDIV(FMUL(F_96, FDIV(INTTOFIX(nsoption_int(font_min_size)), F_10)), F_72); + + htmlc->unit_len_ctx.font_size_default = f_size; + htmlc->unit_len_ctx.font_size_minimum = f_min; } /* exported function documented in html/html_internal.h */ @@ -446,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; @@ -493,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); @@ -504,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; } @@ -540,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); } @@ -556,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; @@ -1035,9 +1062,11 @@ static void html_reformat(struct content *c, int width, int height) htmlc->reflowing = true; - htmlc->len_ctx.vw = nscss_pixels_physical_to_css(INTTOFIX(width)); - htmlc->len_ctx.vh = nscss_pixels_physical_to_css(INTTOFIX(height)); - htmlc->len_ctx.root_style = htmlc->layout->style; + htmlc->unit_len_ctx.viewport_width = css_unit_device2css_px( + INTTOFIX(width), htmlc->unit_len_ctx.device_dpi); + htmlc->unit_len_ctx.viewport_height = css_unit_device2css_px( + INTTOFIX(height), htmlc->unit_len_ctx.device_dpi); + htmlc->unit_len_ctx.root_style = htmlc->layout->style; layout_document(htmlc, width, height); layout = htmlc->layout; @@ -1260,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); @@ -1427,7 +1461,7 @@ html_get_contextual_content(struct content *c, int x, int y, struct box *next; int box_x = 0, box_y = 0; - while ((next = box_at_point(&html->len_ctx, box, x, y, + while ((next = box_at_point(&html->unit_len_ctx, box, x, y, &box_x, &box_y)) != NULL) { box = next; @@ -1508,7 +1542,7 @@ html_scroll_at_point(struct content *c, int x, int y, int scrx, int scry) /* TODO: invert order; visit deepest box first */ - while ((next = box_at_point(&html->len_ctx, box, x, y, + while ((next = box_at_point(&html->unit_len_ctx, box, x, y, &box_x, &box_y)) != NULL) { box = next; @@ -1657,7 +1691,7 @@ static bool html_drop_file_at_point(struct content *c, int x, int y, char *file) int box_x = 0, box_y = 0; /* Scan box tree for boxes that can handle drop */ - while ((next = box_at_point(&html->len_ctx, box, x, y, + while ((next = box_at_point(&html->unit_len_ctx, box, x, y, &box_x, &box_y)) != NULL) { box = next; diff --git a/content/handlers/html/interaction.c b/content/handlers/html/interaction.c index 90e7b7626..0a843e026 100644 --- a/content/handlers/html/interaction.c +++ b/content/handlers/html/interaction.c @@ -211,7 +211,7 @@ static size_t html_selection_drag_end(struct html_content *html, if (box) { plot_font_style_t fstyle; - font_plot_style_from_css(&html->len_ctx, box->style, &fstyle); + font_plot_style_from_css(&html->unit_len_ctx, box->style, &fstyle); guit->layout->position(&fstyle, box->text, box->length, dx, &idx, &pixel_offset); @@ -424,7 +424,7 @@ mouse_action_drag_selection(html_content *html, box = box_pick_text_box(html, x, y, dir, &dx, &dy); if (box != NULL) { - font_plot_style_from_css(&html->len_ctx, box->style, &fstyle); + font_plot_style_from_css(&html->unit_len_ctx, box->style, &fstyle); guit->layout->position(&fstyle, box->text, @@ -805,7 +805,7 @@ get_mouse_action_node(html_content *html, next_box: /* iterate to next box */ - box = box_at_point(&html->len_ctx, box, x, y, &box_x, &box_y); + box = box_at_point(&html->unit_len_ctx, box, x, y, &box_x, &box_y); } while (box != NULL); /* use of box_x, box_y, or content below this point is probably a @@ -899,7 +899,7 @@ gadget_mouse_action(html_content *html, } free(oldcoords); } - /* Fall through */ + fallthrough; case GADGET_SUBMIT: if (mas->gadget.control->form) { @@ -1209,7 +1209,7 @@ default_mouse_action(html_content *html, size_t idx; plot_font_style_t fstyle; - font_plot_style_from_css(&html->len_ctx, + font_plot_style_from_css(&html->unit_len_ctx, mas->text.box->style, &fstyle); @@ -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 c8c0127ad..280e5aade 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,16 +111,11 @@ 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, const html_content *content); - /** * Compute the size of replaced boxes with auto dimensions, according to * content. @@ -246,7 +228,7 @@ layout_get_object_dimensions(struct box *box, * \return length of indent */ static int layout_text_indent( - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, const css_computed_style *style, int width) { css_fixed value = 0; @@ -257,73 +239,8 @@ static int layout_text_indent( if (unit == CSS_UNIT_PCT) { return FPCT_OF_INT_TOINT(value, width); } else { - return FIXTOINT(nscss_len2px(len_ctx, value, unit, style)); - } -} - - -/** - * Determine width of margin, borders, and padding on one side of a box. - * - * \param 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 nscss_len_ctx *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(nscss_len2px(len_ctx, - value, unit, style)); - } - } - } - - /* border */ - if (border) { - if (border_style_funcs[side](style) != - CSS_BORDER_STYLE_NONE) { - border_width_funcs[side](style, &value, &unit); - - *fixed += FIXTOINT(nscss_len2px(len_ctx, - value, unit, style)); - } - } - - /* padding */ - if (padding) { - padding_funcs[side](style, &value, &unit); - if (unit == CSS_UNIT_PCT) { - *frac += FIXTOINT(FDIV(value, F_100)); - } else { - *fixed += FIXTOINT(nscss_len2px(len_ctx, - value, unit, style)); - } + return FIXTOINT(css_unit_len2device_px(style, unit_len_ctx, + value, unit)); } } @@ -342,22 +259,20 @@ static void layout_minmax_table(struct box *table, const html_content *content) { unsigned int i, j; + int width; int border_spacing_h = 0; int table_min = 0, table_max = 0; int extra_fixed = 0; float extra_frac = 0; struct column *col; struct box *row_group, *row, *cell; - enum css_width_e wtype; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; /* check if the widths have already been calculated */ if (table->max_width != UNKNOWN_MAX_WIDTH) return; - if (table_calculate_column_types(&content->len_ctx, table) == false) { - NSLOG(netsurf, WARNING, + if (table_calculate_column_types(&content->unit_len_ctx, table) == false) { + NSLOG(netsurf, ERROR, "Could not establish table column types."); return; } @@ -379,8 +294,10 @@ static void layout_minmax_table(struct box *table, css_computed_border_spacing(table->style, &h, &hu, &v, &vu); - border_spacing_h = FIXTOINT(nscss_len2px(&content->len_ctx, - h, hu, table->style)); + border_spacing_h = FIXTOINT(css_unit_len2device_px( + table->style, + &content->unit_len_ctx, + h, hu)); } /* 1st pass: consider cells with colspan 1 only */ @@ -483,10 +400,8 @@ static void layout_minmax_table(struct box *table, } /* fixed width takes priority, unless it is too narrow */ - wtype = css_computed_width(table->style, &value, &unit); - if (wtype == CSS_WIDTH_SET && unit != CSS_UNIT_PCT) { - int width = FIXTOINT(nscss_len2px(&content->len_ctx, - value, unit, table->style)); + if (css_computed_width(table->style, &content->unit_len_ctx, + -1, &width) == CSS_WIDTH_SET) { if (table_min < width) table_min = width; if (table_max < width) @@ -494,10 +409,10 @@ static void layout_minmax_table(struct box *table, } /* add margins, border, padding to min, max widths */ - calculate_mbp_width(&content->len_ctx, + calculate_mbp_width(&content->unit_len_ctx, table->style, LEFT, true, true, true, &extra_fixed, &extra_frac); - calculate_mbp_width(&content->len_ctx, + calculate_mbp_width(&content->unit_len_ctx, table->style, RIGHT, true, true, true, &extra_fixed, &extra_frac); if (extra_fixed < 0) @@ -581,11 +496,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); @@ -594,13 +505,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; @@ -610,7 +521,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; @@ -622,17 +533,17 @@ layout_minmax_line(struct box *first, } assert(b->style); - font_plot_style_from_css(&content->len_ctx, b->style, &fstyle); + font_plot_style_from_css(&content->unit_len_ctx, b->style, &fstyle); if (b->type == BOX_INLINE && !b->object && !(b->flags & REPLACE_DIM) && !(b->flags & IFRAME)) { fixed = frac = 0; - calculate_mbp_width(&content->len_ctx, + calculate_mbp_width(&content->unit_len_ctx, b->style, LEFT, true, true, true, &fixed, &frac); if (!b->inline_end) - calculate_mbp_width(&content->len_ctx, + calculate_mbp_width(&content->unit_len_ctx, b->style, RIGHT, true, true, true, &fixed, &frac); @@ -642,7 +553,7 @@ layout_minmax_line(struct box *first, /* \todo update min width, consider fractional extra */ } else if (b->type == BOX_INLINE_END) { fixed = frac = 0; - calculate_mbp_width(&content->len_ctx, + calculate_mbp_width(&content->unit_len_ctx, b->inline_end->style, RIGHT, true, true, true, &fixed, &frac); @@ -661,8 +572,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) @@ -753,33 +663,28 @@ layout_minmax_line(struct box *first, /* inline replaced, 10.3.2 and 10.6.2 */ assert(b->style); - /* calculate box width */ - wtype = css_computed_width(b->style, &value, &unit); bs = css_computed_box_sizing(block->style); + + /* calculate box width */ + wtype = css_computed_width(b->style, + &content->unit_len_ctx, -1, &width); if (wtype == CSS_WIDTH_SET) { - if (unit == CSS_UNIT_PCT) { - width = AUTO; - } else { - width = FIXTOINT(nscss_len2px(&content->len_ctx, - value, unit, b->style)); - - if (bs == CSS_BOX_SIZING_BORDER_BOX) { - fixed = frac = 0; - calculate_mbp_width(&content->len_ctx, - block->style, LEFT, - false, true, true, - &fixed, &frac); - calculate_mbp_width(&content->len_ctx, - block->style, RIGHT, - false, true, true, - &fixed, &frac); - if (width < fixed) { - width = fixed; - } + if (bs == CSS_BOX_SIZING_BORDER_BOX) { + fixed = frac = 0; + calculate_mbp_width(&content->unit_len_ctx, + block->style, LEFT, + false, true, true, + &fixed, &frac); + calculate_mbp_width(&content->unit_len_ctx, + block->style, RIGHT, + false, true, true, + &fixed, &frac); + if (width < fixed) { + width = fixed; } - if (width < 0) - width = 0; } + if (width < 0) + width = 0; } else { width = AUTO; } @@ -787,8 +692,10 @@ layout_minmax_line(struct box *first, /* height */ htype = css_computed_height(b->style, &value, &unit); if (htype == CSS_HEIGHT_SET) { - height = FIXTOINT(nscss_len2px(&content->len_ctx, - value, unit, b->style)); + height = FIXTOINT(css_unit_len2device_px( + b->style, + &content->unit_len_ctx, + value, unit)); } else { height = AUTO; } @@ -804,20 +711,20 @@ layout_minmax_line(struct box *first, fixed = frac = 0; if (bs == CSS_BOX_SIZING_BORDER_BOX) { - calculate_mbp_width(&content->len_ctx, + calculate_mbp_width(&content->unit_len_ctx, b->style, LEFT, true, false, false, &fixed, &frac); - calculate_mbp_width(&content->len_ctx, + calculate_mbp_width(&content->unit_len_ctx, b->style, RIGHT, true, false, false, &fixed, &frac); } else { - calculate_mbp_width(&content->len_ctx, + calculate_mbp_width(&content->unit_len_ctx, b->style, LEFT, true, true, true, &fixed, &frac); - calculate_mbp_width(&content->len_ctx, + calculate_mbp_width(&content->unit_len_ctx, b->style, RIGHT, true, true, true, &fixed, &frac); @@ -831,20 +738,20 @@ layout_minmax_line(struct box *first, fixed = frac = 0; if (bs == CSS_BOX_SIZING_BORDER_BOX) { - calculate_mbp_width(&content->len_ctx, + calculate_mbp_width(&content->unit_len_ctx, b->style, LEFT, true, false, false, &fixed, &frac); - calculate_mbp_width(&content->len_ctx, + calculate_mbp_width(&content->unit_len_ctx, b->style, RIGHT, true, false, false, &fixed, &frac); } else { - calculate_mbp_width(&content->len_ctx, + calculate_mbp_width(&content->unit_len_ctx, b->style, LEFT, true, true, true, &fixed, &frac); - calculate_mbp_width(&content->len_ctx, + calculate_mbp_width(&content->unit_len_ctx, b->style, RIGHT, true, true, true, &fixed, &frac); @@ -856,10 +763,10 @@ layout_minmax_line(struct box *first, } else { /* form control with no object */ if (width == AUTO) - width = FIXTOINT(nscss_len2px( - &content->len_ctx, - INTTOFIX(1), CSS_UNIT_EM, - b->style)); + width = FIXTOINT(css_unit_len2device_px( + b->style, + &content->unit_len_ctx, + INTTOFIX(1), CSS_UNIT_EM)); } if (min < width && !box_has_percentage_max_width(b)) @@ -873,7 +780,7 @@ layout_minmax_line(struct box *first, if (first_line) { /* todo: handle percentage values properly */ /* todo: handle text-indent interaction with floats */ - int text_indent = layout_text_indent(&content->len_ctx, + int text_indent = layout_text_indent(&content->unit_len_ctx, first->parent->parent->style, 100); min = (min + text_indent < 0) ? 0 : min + text_indent; max = (max + text_indent < 0) ? 0 : max + text_indent; @@ -940,7 +847,6 @@ layout_minmax_inline_container(struct box *inline_container, inline_container->max_width); } - /** * Calculate minimum and maximum width of a block. * @@ -966,9 +872,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); @@ -977,15 +887,15 @@ static void layout_minmax_block( return; if (block->style != NULL) { - wtype = css_computed_width(block->style, &width, &wunit); + wtype = css_computed_width_static(block->style, &width, &wunit); htype = css_computed_height(block->style, &height, &hunit); bs = css_computed_box_sizing(block->style); } /* 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; @@ -996,6 +906,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 || @@ -1006,8 +919,8 @@ static void layout_minmax_block( css_fixed size = INTTOFIX(10); css_unit unit = CSS_UNIT_EM; - min = max = FIXTOINT(nscss_len2px(&content->len_ctx, - size, unit, block->style)); + min = max = FIXTOINT(css_unit_len2device_px(block->style, + &content->unit_len_ctx, size, unit)); block->flags |= HAS_HEIGHT; } @@ -1020,8 +933,8 @@ static void layout_minmax_block( /* form checkbox or radio button * if width is AUTO, set it to 1em */ - min = max = FIXTOINT(nscss_len2px(&content->len_ctx, - size, unit, block->style)); + min = max = FIXTOINT(css_unit_len2device_px(block->style, + &content->unit_len_ctx, size, unit)); block->flags |= HAS_HEIGHT; } @@ -1045,6 +958,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); @@ -1086,10 +1000,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; @@ -1102,23 +1030,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(nscss_len2px(&content->len_ctx, - width, wunit, block->style)); - if (bs == CSS_BOX_SIZING_BORDER_BOX) { - int border_box_fixed = 0; - float border_box_frac = 0; - calculate_mbp_width(&content->len_ctx, - block->style, LEFT, - false, true, true, - &border_box_fixed, &border_box_frac); - calculate_mbp_width(&content->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; + int width; + + if (css_computed_width(block->style, &content->unit_len_ctx, + -1, &width) == CSS_WIDTH_SET) { + min = max = width; + 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; } } } @@ -1132,22 +1077,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->len_ctx, - block->style, LEFT, true, false, false, - &extra_fixed, &extra_frac); - calculate_mbp_width(&content->len_ctx, - block->style, RIGHT, true, false, false, - &extra_fixed, &extra_frac); - } else { - calculate_mbp_width(&content->len_ctx, - block->style, LEFT, true, true, true, - &extra_fixed, &extra_frac); - calculate_mbp_width(&content->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) @@ -1166,375 +1119,15 @@ 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 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 nscss_len_ctx *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(len_ctx, box->style, - setwidth ? LEFT : TOP, - false, true, true, &fixed, &frac); - calculate_mbp_width(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 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 nscss_len_ctx *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(nscss_len2px(len_ctx, - value, unit, style)); - } - } else { - *width = AUTO; - } - - if (*width != AUTO) { - layout_handle_box_sizing(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(nscss_len2px(len_ctx, - value, unit, style)); - } - } else { - *height = AUTO; - } - - if (*height != AUTO) { - layout_handle_box_sizing(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(nscss_len2px(len_ctx, - value, unit, style)); - } - } else { - /* Inadmissible */ - *max_width = -1; - } - - if (*max_width != -1) { - layout_handle_box_sizing(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(nscss_len2px(len_ctx, - value, unit, style)); - } - } else { - /* Inadmissible */ - *min_width = 0; - } - - if (*min_width != 0) { - layout_handle_box_sizing(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(nscss_len2px(len_ctx, - value, unit, style)); - } - } 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(nscss_len2px(len_ctx, - value, unit, style)); - } - } 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(nscss_len2px( - len_ctx, - value, unit, style)); - } - } 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(nscss_len2px(len_ctx, - value, unit, style)); - } - } - - /* 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(nscss_len2px(len_ctx, - value, unit, style)); - - /* 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); } /** * Find next block that current margin collapses to. * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param box box to start tree-order search from (top margin is included) * \param block box responsible for current block fromatting context * \param viewport_height height of viewport in px @@ -1543,7 +1136,7 @@ layout_find_dimensions(const nscss_len_ctx *len_ctx, * \return next box that current margin collapses to, or NULL if none. */ static struct box* -layout_next_margin_block(const nscss_len_ctx *len_ctx, +layout_next_margin_block(const css_unit_ctx *unit_len_ctx, struct box *box, struct box *block, int viewport_height, @@ -1563,7 +1156,7 @@ layout_next_margin_block(const nscss_len_ctx *len_ctx, /* Get margins */ if (box->style) { - layout_find_dimensions(len_ctx, + layout_find_dimensions(unit_len_ctx, box->parent->width, viewport_height, box, box->style, @@ -1638,7 +1231,7 @@ layout_next_margin_block(const nscss_len_ctx *len_ctx, /* Get margins */ if (box->style) { - layout_find_dimensions(len_ctx, + layout_find_dimensions(unit_len_ctx, box->parent->width, viewport_height, box, box->style, @@ -1875,7 +1468,7 @@ layout_solve_width(struct box *box, * Compute dimensions of box, margins, paddings, and borders for a block-level * element. * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param available_width Max width available in pixels * \param viewport_height Height of viewport in pixels or -ve if unknown * \param lm min left margin required to avoid floats in px. @@ -1888,7 +1481,7 @@ layout_solve_width(struct box *box, * See CSS 2.1 10.3.3, 10.3.4, 10.6.2, and 10.6.3. */ static void -layout_block_find_dimensions(const nscss_len_ctx *len_ctx, +layout_block_find_dimensions(const css_unit_ctx *unit_len_ctx, int available_width, int viewport_height, int lm, @@ -1902,7 +1495,7 @@ layout_block_find_dimensions(const nscss_len_ctx *len_ctx, struct box_border *border = box->border; const css_computed_style *style = box->style; - layout_find_dimensions(len_ctx, available_width, viewport_height, box, + layout_find_dimensions(unit_len_ctx, available_width, viewport_height, box, style, &width, &height, &max_width, &min_width, &max_height, &min_height, margin, padding, border); @@ -1994,15 +1587,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 */ @@ -2028,7 +1616,6 @@ static bool layout_table(struct box *table, int available_width, struct box **row_span_cell; struct column *col; const css_computed_style *style = table->style; - enum css_width_e wtype; enum css_height_e htype; css_fixed value = 0; css_unit unit = CSS_UNIT_PX; @@ -2056,7 +1643,7 @@ static bool layout_table(struct box *table, int available_width, memcpy(col, table->col, sizeof(col[0]) * columns); /* find margins, paddings, and borders for table and cells */ - layout_find_dimensions(&content->len_ctx, available_width, -1, table, + layout_find_dimensions(&content->unit_len_ctx, available_width, -1, table, style, 0, 0, 0, 0, 0, 0, table->margin, table->padding, table->border); for (row_group = table->children; row_group; @@ -2068,8 +1655,8 @@ static bool layout_table(struct box *table, int available_width, assert(c->style); table_used_border_for_cell( - &content->len_ctx, c); - layout_find_dimensions(&content->len_ctx, + &content->unit_len_ctx, c); + layout_find_dimensions(&content->unit_len_ctx, available_width, -1, c, c->style, 0, 0, 0, 0, 0, 0, 0, c->padding, c->border); @@ -2099,23 +1686,15 @@ static bool layout_table(struct box *table, int available_width, css_computed_border_spacing(style, &h, &hu, &v, &vu); - border_spacing_h = FIXTOINT(nscss_len2px(&content->len_ctx, - h, hu, style)); - border_spacing_v = FIXTOINT(nscss_len2px(&content->len_ctx, - v, vu, style)); + border_spacing_h = FIXTOINT(css_unit_len2device_px( + style, &content->unit_len_ctx, h, hu)); + border_spacing_v = FIXTOINT(css_unit_len2device_px( + style, &content->unit_len_ctx, v, vu)); } /* find specified table width, or available width if auto-width */ - wtype = css_computed_width(style, &value, &unit); - if (wtype == CSS_WIDTH_SET) { - if (unit == CSS_UNIT_PCT) { - table_width = FPCT_OF_INT_TOINT(value, available_width); - } else { - table_width = - FIXTOINT(nscss_len2px(&content->len_ctx, - value, unit, style)); - } - + if (css_computed_width(style, &content->unit_len_ctx, + available_width, &table_width) == CSS_WIDTH_SET) { /* specified width includes border */ table_width -= table->border[LEFT].width + table->border[RIGHT].width; @@ -2191,8 +1770,9 @@ static bool layout_table(struct box *table, int available_width, } else { /* This is the minimum height for the table * (see 17.5.3) */ - min_height = FIXTOINT(nscss_len2px(&content->len_ctx, - value, unit, style)); + min_height = FIXTOINT(css_unit_len2device_px( + style, &content->unit_len_ctx, + value, unit)); } } @@ -2382,9 +1962,10 @@ static bool layout_table(struct box *table, int available_width, htype = css_computed_height(row->style, &value, &unit); if (htype == CSS_HEIGHT_SET && unit != CSS_UNIT_PCT) { - row_height = FIXTOINT(nscss_len2px( - &content->len_ctx, - value, unit, row->style)); + row_height = FIXTOINT(css_unit_len2device_px( + row->style, + &content->unit_len_ctx, + value, unit)); } for (c = row->children; c; c = c->next) { assert(c->style); @@ -2421,9 +2002,10 @@ static bool layout_table(struct box *table, int available_width, /* some sites use height="1" or similar * to attempt to make cells as small as * possible, so treat it as a minimum */ - int h = FIXTOINT(nscss_len2px( - &content->len_ctx, - value, unit, c->style)); + int h = FIXTOINT(css_unit_len2device_px( + c->style, + &content->unit_len_ctx, + value, unit)); if (c->height < h) c->height = h; } @@ -2567,14 +2149,14 @@ static bool layout_table(struct box *table, int available_width, /** * Manimpulate box height according to CSS min-height and max-height properties * - * \param len_ctx CSS length conversion context for document. + * \param unit_len_ctx CSS length conversion context for document. * \param box block to modify with any min-height or max-height * \param container containing block for absolutely positioned elements, or * NULL for non absolutely positioned elements. * \return whether the height has been changed */ static bool layout_apply_minmax_height( - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, struct box *box, struct box *container) { @@ -2635,8 +2217,9 @@ static bool layout_apply_minmax_height( } } } else { - h = FIXTOINT(nscss_len2px(len_ctx, - value, unit, box->style)); + h = FIXTOINT(css_unit_len2device_px( + box->style, unit_len_ctx, + value, unit)); if (h < box->height) { box->height = h; updated = true; @@ -2665,8 +2248,9 @@ static bool layout_apply_minmax_height( } } } else { - h = FIXTOINT(nscss_len2px(len_ctx, - value, unit, box->style)); + h = FIXTOINT(css_unit_len2device_px( + box->style, unit_len_ctx, + value, unit)); if (h > box->height) { box->height = h; updated = true; @@ -2688,7 +2272,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); @@ -2696,7 +2282,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 */ @@ -2834,7 +2420,7 @@ layout_text_box_split(html_content *content, * Compute dimensions of box, margins, paddings, and borders for a floating * element using shrink-to-fit. Also used for inline-blocks. * - * \param len_ctx CSS length conversion context for document. + * \param unit_len_ctx CSS length conversion context for document. * \param available_width Max width available in pixels * \param style Box's style * \param box Box for which to find dimensions @@ -2843,7 +2429,7 @@ layout_text_box_split(html_content *content, */ static void layout_float_find_dimensions( - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, int available_width, const css_computed_style *style, struct box *box) @@ -2863,7 +2449,7 @@ layout_float_find_dimensions( overflow_y == CSS_OVERFLOW_AUTO) ? SCROLLBAR_WIDTH : 0; - layout_find_dimensions(len_ctx, available_width, -1, box, style, + layout_find_dimensions(unit_len_ctx, available_width, -1, box, style, &width, &height, &max_width, &min_width, &max_height, &min_height, margin, padding, border); @@ -2899,26 +2485,30 @@ layout_float_find_dimensions( box->gadget->type == GADGET_FILE) { if (width == AUTO) { size = INTTOFIX(10); - width = FIXTOINT(nscss_len2px(len_ctx, - size, unit, box->style)); + width = FIXTOINT(css_unit_len2device_px( + box->style, unit_len_ctx, + size, unit)); } if (box->gadget->type == GADGET_FILE && height == AUTO) { size = FLTTOFIX(1.5); - height = FIXTOINT(nscss_len2px(len_ctx, - size, unit, box->style)); + height = FIXTOINT(css_unit_len2device_px( + box->style, unit_len_ctx, + size, unit)); } } if (box->gadget->type == GADGET_TEXTAREA) { if (width == AUTO) { size = INTTOFIX(10); - width = FIXTOINT(nscss_len2px(len_ctx, - size, unit, box->style)); + width = FIXTOINT(css_unit_len2device_px( + box->style, unit_len_ctx, + size, unit)); } if (height == AUTO) { size = INTTOFIX(4); - height = FIXTOINT(nscss_len2px(len_ctx, - size, unit, box->style)); + height = FIXTOINT(css_unit_len2device_px( + box->style, unit_len_ctx, + size, unit)); } } } else if (width == AUTO) { @@ -2939,9 +2529,9 @@ layout_float_find_dimensions( * mbp as was used in layout_minmax_block() */ int fixed = 0; float frac = 0; - calculate_mbp_width(len_ctx, box->style, LEFT, + calculate_mbp_width(unit_len_ctx, box->style, LEFT, true, true, true, &fixed, &frac); - calculate_mbp_width(len_ctx, box->style, RIGHT, + calculate_mbp_width(unit_len_ctx, box->style, RIGHT, true, true, true, &fixed, &frac); if (fixed < 0) fixed = 0; @@ -2978,12 +2568,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); - layout_float_find_dimensions(&content->len_ctx, width, b->style, b); - if (b->type == BOX_TABLE) { - if (!layout_table(b, width, content)) - return false; + 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 || 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) @@ -2992,8 +2590,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; } @@ -3053,7 +2652,7 @@ place_float_below(struct box *c, int width, int cx, int y, struct box *cont) * Calculate line height from a style. */ static int line_height( - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, const css_computed_style *style) { enum css_line_height_e lhtype; @@ -3072,16 +2671,16 @@ static int line_height( if (lhtype == CSS_LINE_HEIGHT_NUMBER || lhunit == CSS_UNIT_PCT) { - line_height = nscss_len2px(len_ctx, - lhvalue, CSS_UNIT_EM, style); + line_height = css_unit_len2device_px(style, unit_len_ctx, + lhvalue, CSS_UNIT_EM); if (lhtype != CSS_LINE_HEIGHT_NUMBER) line_height = FDIV(line_height, F_100); } else { assert(lhunit != CSS_UNIT_PCT); - line_height = nscss_len2px(len_ctx, - lhvalue, lhunit, style); + line_height = css_unit_len2device_px(style, unit_len_ctx, + lhvalue, lhunit); } return FIXTOINT(line_height); @@ -3153,7 +2752,7 @@ layout_line(struct box *first, x1 -= cx; if (indent) - x0 += layout_text_indent(&content->len_ctx, + x0 += layout_text_indent(&content->unit_len_ctx, first->parent->parent->style, *width); if (x1 < x0) @@ -3163,7 +2762,7 @@ layout_line(struct box *first, * this is the line-height if there are text children and also in the * case of an initially empty text input */ if (has_text_children || first->parent->parent->gadget) - used_height = height = line_height(&content->len_ctx, + used_height = height = line_height(&content->unit_len_ctx, first->parent->parent->style); else /* inline containers with no text are usually for layout and @@ -3180,20 +2779,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) == @@ -3203,11 +2796,12 @@ layout_line(struct box *first, continue; assert(b->style != NULL); - font_plot_style_from_css(&content->len_ctx, b->style, &fstyle); + font_plot_style_from_css(&content->unit_len_ctx, b->style, &fstyle); 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; @@ -3227,7 +2821,7 @@ layout_line(struct box *first, if (b->type == BOX_INLINE) { /* calculate borders, margins, and padding */ - layout_find_dimensions(&content->len_ctx, + layout_find_dimensions(&content->unit_len_ctx, *width, -1, b, b->style, 0, 0, 0, 0, 0, 0, b->margin, b->padding, b->border); for (i = 0; i != 4; i++) @@ -3259,10 +2853,9 @@ 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->len_ctx, + b->height = line_height(&content->unit_len_ctx, b->style ? b->style : b->parent->parent->style); if (height < b->height) @@ -3333,7 +2926,7 @@ layout_line(struct box *first, /* inline replaced, 10.3.2 and 10.6.2 */ assert(b->style); - layout_find_dimensions(&content->len_ctx, + layout_find_dimensions(&content->unit_len_ctx, *width, -1, b, b->style, &b->width, &b->height, &max_width, &min_width, @@ -3356,17 +2949,19 @@ layout_line(struct box *first, } else { /* form control with no object */ if (b->width == AUTO) - b->width = FIXTOINT(nscss_len2px( - &content->len_ctx, INTTOFIX(1), - CSS_UNIT_EM, b->style)); + b->width = FIXTOINT(css_unit_len2device_px( + b->style, + &content->unit_len_ctx, INTTOFIX(1), + CSS_UNIT_EM)); if (b->height == AUTO) - b->height = FIXTOINT(nscss_len2px( - &content->len_ctx, INTTOFIX(1), - CSS_UNIT_EM, b->style)); + b->height = FIXTOINT(css_unit_len2device_px( + b->style, + &content->unit_len_ctx, INTTOFIX(1), + CSS_UNIT_EM)); } /* 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; @@ -3395,7 +2990,7 @@ layout_line(struct box *first, x1 -= cx; if (indent) - x0 += layout_text_indent(&content->len_ctx, + x0 += layout_text_indent(&content->unit_len_ctx, first->parent->parent->style, *width); if (x1 < x0) @@ -3418,10 +3013,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; @@ -3429,7 +3021,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] + @@ -3454,7 +3047,7 @@ layout_line(struct box *first, else if (b->text || b->type == BOX_INLINE_END) { if (b->space == UNKNOWN_WIDTH) { font_plot_style_from_css( - &content->len_ctx, + &content->unit_len_ctx, b->style, &fstyle); /** \todo handle errors */ font_func->width(&fstyle, " ", 1, @@ -3608,7 +3201,7 @@ layout_line(struct box *first, !(split_box->flags & IFRAME) && !split_box->gadget && split_box->text) { - font_plot_style_from_css(&content->len_ctx, + font_plot_style_from_css(&content->unit_len_ctx, split_box->style, &fstyle); /** \todo handle errors */ font_func->split(&fstyle, @@ -3760,9 +3353,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) { @@ -3883,8 +3474,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; @@ -3914,21 +3504,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 */ @@ -3943,7 +3523,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); @@ -3972,9 +3554,10 @@ layout_block_context(struct box *block, gadget_unit = CSS_UNIT_EM; gadget_size = INTTOFIX(1); if (block->height == AUTO) - block->height = FIXTOINT(nscss_len2px( - &content->len_ctx, gadget_size, - gadget_unit, block->style)); + block->height = FIXTOINT(css_unit_len2device_px( + block->style, + &content->unit_len_ctx, + gadget_size, gadget_unit)); } box = block->children; @@ -4011,7 +3594,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 @@ -4038,7 +3623,7 @@ layout_block_context(struct box *block, * through to, find out. Update the pos/neg margin values. */ if (margin_collapse == NULL) { margin_collapse = layout_next_margin_block( - &content->len_ctx, box, block, + &content->unit_len_ctx, box, block, viewport_height, &max_pos_margin, &max_neg_margin); /* We have a margin that has not yet been applied. */ @@ -4062,9 +3647,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)) { @@ -4089,7 +3675,7 @@ layout_block_context(struct box *block, box->parent->padding[RIGHT] - x1; } - layout_block_find_dimensions(&content->len_ctx, + layout_block_find_dimensions(&content->unit_len_ctx, box->parent->width, viewport_height, lm, rm, box); if (box->type == BOX_BLOCK && !(box->flags & IFRAME)) { @@ -4102,7 +3688,7 @@ layout_block_context(struct box *block, css_fixed width = 0; css_unit unit = CSS_UNIT_PX; - wtype = css_computed_width(box->style, &width, + wtype = css_computed_width_static(box->style, &width, &unit); if (wtype == CSS_WIDTH_AUTO) { @@ -4149,6 +3735,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)) || @@ -4173,11 +3760,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]; @@ -4198,7 +3793,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) { @@ -4221,7 +3817,7 @@ layout_block_context(struct box *block, css_fixed width = 0; css_unit unit = CSS_UNIT_PX; - wtype = css_computed_width(box->style, + wtype = css_computed_width_static(box->style, &width, &unit); x0 = cx; @@ -4330,7 +3926,7 @@ layout_block_context(struct box *block, css_computed_position(box->style) != CSS_POSITION_ABSOLUTE && layout_apply_minmax_height( - &content->len_ctx, + &content->unit_len_ctx, box, NULL)) { /* Height altered */ /* Set current cy */ @@ -4387,7 +3983,7 @@ layout_block_context(struct box *block, if (block->style && css_computed_position(block->style) != CSS_POSITION_ABSOLUTE) { /* Block is in normal flow */ - layout_apply_minmax_height(&content->len_ctx, block, NULL); + layout_apply_minmax_height(&content->unit_len_ctx, block, NULL); } if (block->gadget && @@ -4399,7 +3995,7 @@ layout_block_context(struct box *block, block->padding[RIGHT]; int ta_height = block->padding[TOP] + block->height + block->padding[BOTTOM]; - font_plot_style_from_css(&content->len_ctx, + font_plot_style_from_css(&content->unit_len_ctx, block->style, &fstyle); fstyle.background = NS_TRANSPARENT; textarea_set_layout(block->gadget->data.text.ta, @@ -4575,7 +4171,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; @@ -4647,7 +4243,7 @@ layout__ordered_list_count( dom_exception exc; dom_node *child; int step = 1; - int next; + dom_long next; if (box->node == NULL) { return; @@ -4820,14 +4416,14 @@ layout_lists(const html_content *content, struct box *box) marker->height = content_get_height(marker->object); marker->y = (line_height( - &content->len_ctx, + &content->unit_len_ctx, marker->style) - marker->height) / 2; } else if (marker->text) { if (marker->width == UNKNOWN_WIDTH) { plot_font_style_t fstyle; font_plot_style_from_css( - &content->len_ctx, + &content->unit_len_ctx, marker->style, &fstyle); content->font_func->width(&fstyle, @@ -4839,7 +4435,7 @@ layout_lists(const html_content *content, struct box *box) marker->x = -marker->width; marker->y = 0; marker->height = line_height( - &content->len_ctx, + &content->unit_len_ctx, marker->style); } else { marker->x = 0; @@ -4859,7 +4455,7 @@ layout_lists(const html_content *content, struct box *box) * Compute box offsets for a relatively or absolutely positioned box with * respect to a box. * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param box box to compute offsets for * \param containing_block box to compute percentages with respect to * \param top updated to top offset, or AUTO @@ -4870,7 +4466,7 @@ layout_lists(const html_content *content, struct box *box) * See CSS 2.1 9.3.2. containing_block must have width and height. */ static void -layout_compute_offsets(const nscss_len_ctx *len_ctx, +layout_compute_offsets(const css_unit_ctx *unit_len_ctx, struct box *box, struct box *containing_block, int *top, @@ -4882,9 +4478,9 @@ layout_compute_offsets(const nscss_len_ctx *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); @@ -4893,8 +4489,9 @@ layout_compute_offsets(const nscss_len_ctx *len_ctx, *left = FPCT_OF_INT_TOINT(value, containing_block->width); } else { - *left = FIXTOINT(nscss_len2px(len_ctx, - value, unit, box->style)); + *left = FIXTOINT(css_unit_len2device_px( + box->style, unit_len_ctx, + value, unit)); } } else { *left = AUTO; @@ -4907,8 +4504,9 @@ layout_compute_offsets(const nscss_len_ctx *len_ctx, *right = FPCT_OF_INT_TOINT(value, containing_block->width); } else { - *right = FIXTOINT(nscss_len2px(len_ctx, - value, unit, box->style)); + *right = FIXTOINT(css_unit_len2device_px( + box->style, unit_len_ctx, + value, unit)); } } else { *right = AUTO; @@ -4921,8 +4519,9 @@ layout_compute_offsets(const nscss_len_ctx *len_ctx, *top = FPCT_OF_INT_TOINT(value, containing_block->height); } else { - *top = FIXTOINT(nscss_len2px(len_ctx, - value, unit, box->style)); + *top = FIXTOINT(css_unit_len2device_px( + box->style, unit_len_ctx, + value, unit)); } } else { *top = AUTO; @@ -4935,8 +4534,9 @@ layout_compute_offsets(const nscss_len_ctx *len_ctx, *bottom = FPCT_OF_INT_TOINT(value, containing_block->height); } else { - *bottom = FIXTOINT(nscss_len2px(len_ctx, - value, unit, box->style)); + *bottom = FIXTOINT(css_unit_len2device_px( + box->style, unit_len_ctx, + value, unit)); } } else { *bottom = AUTO; @@ -4970,7 +4570,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 @@ -4988,18 +4590,16 @@ 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->len_ctx, box, containing_block, + layout_compute_offsets(&content->unit_len_ctx, box, containing_block, &top, &right, &bottom, &left); /* Pass containing block into layout_find_dimensions via the float * containing block box member. This is unused for absolutely positioned * boxes because a box can't be floated and absolutely positioned. */ box->float_container = containing_block; - layout_find_dimensions(&content->len_ctx, available_width, -1, + layout_find_dimensions(&content->unit_len_ctx, available_width, -1, box, box->style, &width, &height, &max_width, &min_width, 0, 0, margin, padding, border); @@ -5207,6 +4807,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 */ @@ -5317,7 +4924,7 @@ layout_absolute(struct box *box, /** \todo Inline ancestors */ } box->height = height; - layout_apply_minmax_height(&content->len_ctx, box, containing_block); + layout_apply_minmax_height(&content->unit_len_ctx, box, containing_block); return true; } @@ -5343,7 +4950,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) == @@ -5394,13 +5003,13 @@ layout_position_absolute(struct box *box, /** * Compute a box's relative offset as per CSS 2.1 9.4.3 * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param box Box to compute relative offsets for. * \param x Receives relative offset in x. * \param y Receives relative offset in y. */ static void layout_compute_relative_offset( - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, struct box *box, int *x, int *y) @@ -5420,7 +5029,7 @@ static void layout_compute_relative_offset( containing_block = box->parent; } - layout_compute_offsets(len_ctx, box, containing_block, + layout_compute_offsets(unit_len_ctx, box, containing_block, &top, &right, &bottom, &left); if (left == AUTO && right == AUTO) @@ -5468,7 +5077,7 @@ static void layout_compute_relative_offset( /** * Adjust positions of relatively positioned boxes. * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param root box to adjust the position of * \param fp box which forms the block formatting context for children of * "root" which are floats @@ -5481,7 +5090,7 @@ static void layout_compute_relative_offset( */ static void layout_position_relative( - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, struct box *root, struct box *fp, int fx, @@ -5510,7 +5119,7 @@ layout_position_relative( if (box->style && css_computed_position(box->style) == CSS_POSITION_RELATIVE) layout_compute_relative_offset( - len_ctx, box, &x, &y); + unit_len_ctx, box, &x, &y); else x = y = 0; @@ -5546,7 +5155,7 @@ layout_position_relative( } /* recurse first */ - layout_position_relative(len_ctx, box, fn, fnx, fny); + layout_position_relative(unit_len_ctx, box, fn, fnx, fny); /* Ignore things we're not interested in. */ if (!box->style || (box->style && @@ -5575,7 +5184,7 @@ layout_position_relative( /** * Find a box's bounding box relative to itself, i.e. the box's border edge box * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param box box find bounding box of * \param desc_x0 updated to left of box's bbox * \param desc_y0 updated to top of box's bbox @@ -5584,7 +5193,7 @@ layout_position_relative( */ static void layout_get_box_bbox( - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, struct box *box, int *desc_x0, int *desc_y0, int *desc_x1, int *desc_y1) @@ -5607,8 +5216,8 @@ layout_get_box_bbox( int text_height; css_computed_font_size(box->style, &font_size, &font_unit); - text_height = nscss_len2px(len_ctx, font_size, font_unit, - box->style); + text_height = css_unit_len2device_px(box->style, unit_len_ctx, + font_size, font_unit); text_height = FIXTOINT(text_height * 3 / 4); *desc_y0 = (*desc_y0 < -text_height) ? *desc_y0 : -text_height; } @@ -5618,7 +5227,7 @@ layout_get_box_bbox( /** * Apply changes to box descendant_[xy][01] values due to given child. * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param box box to update * \param child a box, which may affect box's descendant bbox * \param off_x offset to apply to child->x coord to treat as child of box @@ -5626,7 +5235,7 @@ layout_get_box_bbox( */ static void layout_update_descendant_bbox( - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, struct box *box, struct box *child, int off_x, @@ -5650,7 +5259,7 @@ layout_update_descendant_bbox( } /* Get child's border edge */ - layout_get_box_bbox(len_ctx, child, + layout_get_box_bbox(unit_len_ctx, child, &child_desc_x0, &child_desc_y0, &child_desc_x1, &child_desc_y1); @@ -5688,11 +5297,11 @@ layout_update_descendant_bbox( * Recursively calculate the descendant_[xy][01] values for a laid-out box tree * and inform iframe browser windows of their size and position. * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param box tree of boxes to update */ static void layout_calculate_descendant_bboxes( - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, struct box *box) { struct box *child; @@ -5702,7 +5311,7 @@ static void layout_calculate_descendant_bboxes( /* assert((box->width >= 0) && (box->height >= 0)); */ /* Initialise box's descendant box to border edge box */ - layout_get_box_bbox(len_ctx, box, + layout_get_box_bbox(unit_len_ctx, box, &box->descendant_x0, &box->descendant_y0, &box->descendant_x1, &box->descendant_y1); @@ -5736,7 +5345,7 @@ static void layout_calculate_descendant_bboxes( child->type == BOX_FLOAT_RIGHT) continue; - layout_update_descendant_bbox(len_ctx, box, child, + layout_update_descendant_bbox(unit_len_ctx, box, child, box->x, box->y); if (child == box->inline_end) @@ -5754,7 +5363,7 @@ static void layout_calculate_descendant_bboxes( child->type == BOX_FLOAT_RIGHT) continue; - layout_calculate_descendant_bboxes(len_ctx, child); + layout_calculate_descendant_bboxes(unit_len_ctx, child); if (box->style && css_computed_overflow_x(box->style) == CSS_OVERFLOW_HIDDEN && @@ -5762,23 +5371,23 @@ static void layout_calculate_descendant_bboxes( CSS_OVERFLOW_HIDDEN) continue; - layout_update_descendant_bbox(len_ctx, box, child, 0, 0); + layout_update_descendant_bbox(unit_len_ctx, box, child, 0, 0); } for (child = box->float_children; child; child = child->next_float) { assert(child->type == BOX_FLOAT_LEFT || child->type == BOX_FLOAT_RIGHT); - layout_calculate_descendant_bboxes(len_ctx, child); + layout_calculate_descendant_bboxes(unit_len_ctx, child); - layout_update_descendant_bbox(len_ctx, box, child, 0, 0); + layout_update_descendant_bbox(unit_len_ctx, box, child, 0, 0); } if (box->list_marker) { child = box->list_marker; - layout_calculate_descendant_bboxes(len_ctx, child); + layout_calculate_descendant_bboxes(unit_len_ctx, child); - layout_update_descendant_bbox(len_ctx, box, child, 0, 0); + layout_update_descendant_bbox(unit_len_ctx, box, child, 0, 0); } } @@ -5796,7 +5405,7 @@ bool layout_document(html_content *content, int width, int height) layout_minmax_block(doc, font_func, content); - layout_block_find_dimensions(&content->len_ctx, + layout_block_find_dimensions(&content->unit_len_ctx, width, height, 0, 0, doc); doc->x = doc->margin[LEFT] + doc->border[LEFT].width; doc->y = doc->margin[TOP] + doc->border[TOP].width; @@ -5830,9 +5439,9 @@ bool layout_document(html_content *content, int width, int height) layout_lists(content, doc); layout_position_absolute(doc, doc, 0, 0, content); - layout_position_relative(&content->len_ctx, doc, doc, 0, 0); + layout_position_relative(&content->unit_len_ctx, doc, doc, 0, 0); - layout_calculate_descendant_bboxes(&content->len_ctx, doc); + layout_calculate_descendant_bboxes(&content->unit_len_ctx, doc); return ret; } 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..e98e7a5f5 --- /dev/null +++ b/content/handlers/html/layout_internal.h @@ -0,0 +1,719 @@ +/* + * 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 + +/** + * 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_static(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) { + if (css_computed_width(style, unit_len_ctx, + available_width, width) == CSS_WIDTH_SET) { + layout_handle_box_sizing(unit_len_ctx, box, + available_width, true, width); + } else { + *width = AUTO; + } + } + + 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/object.c b/content/handlers/html/object.c index e6edf840d..a1f020bf7 100644 --- a/content/handlers/html/object.c +++ b/content/handlers/html/object.c @@ -265,18 +265,20 @@ html_object_callback(hlcache_handle *object, if (hunit == CSS_UNIT_PCT) { l = (width - w) * hpos / INTTOFIX(100); } else { - l = FIXTOINT(nscss_len2px(&c->len_ctx, - hpos, hunit, - box->style)); + l = FIXTOINT(css_unit_len2device_px( + box->style, + &c->unit_len_ctx, + hpos, hunit)); } h = content_get_height(box->background); if (vunit == CSS_UNIT_PCT) { t = (height - h) * vpos / INTTOFIX(100); } else { - t = FIXTOINT(nscss_len2px(&c->len_ctx, - vpos, vunit, - box->style)); + t = FIXTOINT(css_unit_len2device_px( + box->style, + &c->unit_len_ctx, + vpos, vunit)); } /* Redraw area depends on background-repeat */ diff --git a/content/handlers/html/private.h b/content/handlers/html/private.h index 2bd9cff9d..56cd957d5 100644 --- a/content/handlers/html/private.h +++ b/content/handlers/html/private.h @@ -112,9 +112,6 @@ typedef struct html_content { /** Base target */ char *base_target; - /** CSS length conversion context for document. */ - nscss_len_ctx len_ctx; - /** Content has been aborted in the LOADING state */ bool aborted; @@ -162,6 +159,8 @@ typedef struct html_content { css_select_ctx *select_ctx; /**< Style selection media specification */ css_media media; + /** CSS length conversion context for document. */ + css_unit_ctx unit_len_ctx; /**< Universal selector */ lwc_string *universal; diff --git a/content/handlers/html/redraw.c b/content/handlers/html/redraw.c index 9807512b9..f770699bb 100644 --- a/content/handlers/html/redraw.c +++ b/content/handlers/html/redraw.c @@ -528,14 +528,14 @@ static bool html_redraw_radio(int x, int y, int width, int height, * \param box box of input * \param scale scale for redraw * \param background_colour current background colour - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param ctx current redraw context * \return true if successful, false otherwise */ static bool html_redraw_file(int x, int y, int width, int height, struct box *box, float scale, colour background_colour, - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, const struct redraw_context *ctx) { int text_width; @@ -544,7 +544,7 @@ static bool html_redraw_file(int x, int y, int width, int height, plot_font_style_t fstyle; nserror res; - font_plot_style_from_css(len_ctx, box->style, &fstyle); + font_plot_style_from_css(unit_len_ctx, box->style, &fstyle); fstyle.background = background_colour; if (box->gadget->value) { @@ -587,7 +587,7 @@ static bool html_redraw_file(int x, int y, int width, int height, * \param clip current clip rectangle * \param background_colour current background colour * \param background box containing background details (usually \a box) - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param ctx current redraw context * \return true if successful, false otherwise */ @@ -595,7 +595,7 @@ static bool html_redraw_file(int x, int y, int width, int height, static bool html_redraw_background(int x, int y, struct box *box, float scale, const struct rect *clip, colour *background_colour, struct box *background, - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, const struct redraw_context *ctx) { bool repeat_x = false; @@ -672,8 +672,9 @@ static bool html_redraw_background(int x, int y, struct box *box, float scale, content_get_width(background->background)) * scale * FIXTOFLT(hpos) / 100.; } else { - x += (int) (FIXTOFLT(nscss_len2px(len_ctx, hpos, hunit, - background->style)) * scale); + x += (int) (FIXTOFLT(css_unit_len2device_px( + background->style, unit_len_ctx, + hpos, hunit)) * scale); } if (vunit == CSS_UNIT_PCT) { @@ -681,8 +682,9 @@ static bool html_redraw_background(int x, int y, struct box *box, float scale, content_get_height(background->background)) * scale * FIXTOFLT(vpos) / 100.; } else { - y += (int) (FIXTOFLT(nscss_len2px(len_ctx, vpos, vunit, - background->style)) * scale); + y += (int) (FIXTOFLT(css_unit_len2device_px( + background->style, unit_len_ctx, + vpos, vunit)) * scale); } } @@ -814,7 +816,7 @@ static bool html_redraw_background(int x, int y, struct box *box, float scale, * \param first true if this is the first rectangle associated with the inline * \param last true if this is the last rectangle associated with the inline * \param background_colour updated to current background colour if plotted - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param ctx current redraw context * \return true if successful, false otherwise */ @@ -822,7 +824,7 @@ static bool html_redraw_background(int x, int y, struct box *box, float scale, static bool html_redraw_inline_background(int x, int y, struct box *box, float scale, const struct rect *clip, struct rect b, bool first, bool last, colour *background_colour, - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, const struct redraw_context *ctx) { struct rect r = *clip; @@ -883,8 +885,9 @@ static bool html_redraw_inline_background(int x, int y, struct box *box, plot_content = false; } } else { - x += (int) (FIXTOFLT(nscss_len2px(len_ctx, hpos, hunit, - box->style)) * scale); + x += (int) (FIXTOFLT(css_unit_len2device_px( + box->style, unit_len_ctx, + hpos, hunit)) * scale); } if (vunit == CSS_UNIT_PCT) { @@ -892,8 +895,9 @@ static bool html_redraw_inline_background(int x, int y, struct box *box, content_get_height(box->background) * scale) * FIXTOFLT(vpos) / 100.; } else { - y += (int) (FIXTOFLT(nscss_len2px(len_ctx, vpos, vunit, - box->style)) * scale); + y += (int) (FIXTOFLT(css_unit_len2device_px( + box->style, unit_len_ctx, + vpos, vunit)) * scale); } } @@ -1134,7 +1138,7 @@ static bool html_redraw_text_box(const html_content *html, struct box *box, bool excluded = (box->object != NULL); plot_font_style_t fstyle; - font_plot_style_from_css(&html->len_ctx, box->style, &fstyle); + font_plot_style_from_css(&html->unit_len_ctx, box->style, &fstyle); fstyle.background = current_background_color; if (!text_redraw(box->text, @@ -1405,28 +1409,24 @@ bool html_redraw_box(const html_content *html, struct box *box, CSS_CLIP_RECT) { /* We have an absolutly positioned box with a clip rect */ if (css_rect.left_auto == false) - r.x0 = x - border_left + FIXTOINT(nscss_len2px( - &html->len_ctx, - css_rect.left, css_rect.lunit, - box->style)); + r.x0 = x - border_left + FIXTOINT(css_unit_len2device_px( + box->style, &html->unit_len_ctx, + css_rect.left, css_rect.lunit)); if (css_rect.top_auto == false) - r.y0 = y - border_top + FIXTOINT(nscss_len2px( - &html->len_ctx, - css_rect.top, css_rect.tunit, - box->style)); + r.y0 = y - border_top + FIXTOINT(css_unit_len2device_px( + box->style, &html->unit_len_ctx, + css_rect.top, css_rect.tunit)); if (css_rect.right_auto == false) - r.x1 = x - border_left + FIXTOINT(nscss_len2px( - &html->len_ctx, - css_rect.right, css_rect.runit, - box->style)); + r.x1 = x - border_left + FIXTOINT(css_unit_len2device_px( + box->style, &html->unit_len_ctx, + css_rect.right, css_rect.runit)); if (css_rect.bottom_auto == false) - r.y1 = y - border_top + FIXTOINT(nscss_len2px( - &html->len_ctx, - css_rect.bottom, css_rect.bunit, - box->style)); + r.y1 = y - border_top + FIXTOINT(css_unit_len2device_px( + box->style, &html->unit_len_ctx, + css_rect.bottom, css_rect.bunit)); /* find intersection of clip rectangle and box */ if (r.x0 < clip->x0) r.x0 = clip->x0; @@ -1515,7 +1515,7 @@ bool html_redraw_box(const html_content *html, struct box *box, /* plot background */ if (!html_redraw_background(x, y, box, scale, &p, ¤t_background_color, bg_box, - &html->len_ctx, ctx)) + &html->unit_len_ctx, ctx)) return false; /* restore previous graphics window */ if (ctx->plot->clip(ctx, &r) != NSERROR_OK) @@ -1595,7 +1595,7 @@ bool html_redraw_box(const html_content *html, struct box *box, x, y, box, scale, &p, b, first, false, ¤t_background_color, - &html->len_ctx, ctx)) + &html->unit_len_ctx, ctx)) return false; /* restore previous graphics window */ if (ctx->plot->clip(ctx, &r) != NSERROR_OK) @@ -1628,7 +1628,7 @@ bool html_redraw_box(const html_content *html, struct box *box, * the inline */ if (!html_redraw_inline_background(x, ib_y, box, scale, &p, b, first, true, ¤t_background_color, - &html->len_ctx, ctx)) + &html->unit_len_ctx, ctx)) return false; /* restore previous graphics window */ if (ctx->plot->clip(ctx, &r) != NSERROR_OK) @@ -1843,7 +1843,7 @@ bool html_redraw_box(const html_content *html, struct box *box, } else if (box->gadget && box->gadget->type == GADGET_FILE) { if (!html_redraw_file(x + padding_left, y + padding_top, width, height, box, scale, - current_background_color, &html->len_ctx, ctx)) + current_background_color, &html->unit_len_ctx, ctx)) return false; } else if (box->gadget && 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 263ddf1f6..a0f5b94fa 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" @@ -50,7 +51,7 @@ struct border { /** * Determine if a border style is more eyecatching than another * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param a Reference border style * \param a_src Source of \a a * \param b Candidate border style @@ -58,7 +59,7 @@ struct border { * \return True if \a b is more eyecatching than \a a */ static bool -table_border_is_more_eyecatching(const nscss_len_ctx *len_ctx, +table_border_is_more_eyecatching(const css_unit_ctx *unit_len_ctx, const struct border *a, box_type a_src, const struct border *b, @@ -83,8 +84,8 @@ table_border_is_more_eyecatching(const nscss_len_ctx *len_ctx, * if they've come from a computed style. */ assert(a->unit != CSS_UNIT_EM && a->unit != CSS_UNIT_EX); assert(b->unit != CSS_UNIT_EM && b->unit != CSS_UNIT_EX); - awidth = nscss_len2px(len_ctx, a->width, a->unit, NULL); - bwidth = nscss_len2px(len_ctx, b->width, b->unit, NULL); + awidth = css_unit_len2device_px(NULL, unit_len_ctx, a->width, a->unit); + bwidth = css_unit_len2device_px(NULL, unit_len_ctx, b->width, b->unit); if (awidth < bwidth) return true; @@ -93,27 +94,27 @@ table_border_is_more_eyecatching(const nscss_len_ctx *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 nscss_len_ctx *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; } @@ -160,7 +161,7 @@ table_border_is_more_eyecatching(const nscss_len_ctx *len_ctx, /** * Process a table * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param table Table to process * \param a Current border style for cell * \param a_src Source of \a a @@ -169,7 +170,7 @@ table_border_is_more_eyecatching(const nscss_len_ctx *len_ctx, * \post \a a_src will be updated also */ static void -table_cell_top_process_table(const nscss_len_ctx *len_ctx, +table_cell_top_process_table(const css_unit_ctx *unit_len_ctx, struct box *table, struct border *a, box_type *a_src) @@ -181,11 +182,12 @@ table_cell_top_process_table(const nscss_len_ctx *len_ctx, b.style = css_computed_border_top_style(table->style); b.color = css_computed_border_top_color(table->style, &b.c); css_computed_border_top_width(table->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, table->style); + b.width = css_unit_len2device_px(table->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE; - if (table_border_is_more_eyecatching(len_ctx, a, *a_src, &b, b_src)) { + if (table_border_is_more_eyecatching(unit_len_ctx, a, *a_src, &b, b_src)) { *a = b; *a_src = b_src; } @@ -195,7 +197,7 @@ table_cell_top_process_table(const nscss_len_ctx *len_ctx, /** * Process a row * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param cell Cell being considered * \param row Row to process * \param a Current border style for cell @@ -206,7 +208,7 @@ table_cell_top_process_table(const nscss_len_ctx *len_ctx, * \post \a a_src will be updated also */ static bool -table_cell_top_process_row(const nscss_len_ctx *len_ctx, +table_cell_top_process_row(const css_unit_ctx *unit_len_ctx, struct box *cell, struct box *row, struct border *a, @@ -219,11 +221,12 @@ table_cell_top_process_row(const nscss_len_ctx *len_ctx, b.style = css_computed_border_bottom_style(row->style); b.color = css_computed_border_bottom_color(row->style, &b.c); css_computed_border_bottom_width(row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, row->style); + b.width = css_unit_len2device_px(row->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW; - if (table_border_is_more_eyecatching(len_ctx, a, *a_src, &b, b_src)) { + if (table_border_is_more_eyecatching(unit_len_ctx, a, *a_src, &b, b_src)) { *a = b; *a_src = b_src; } @@ -233,11 +236,12 @@ table_cell_top_process_row(const nscss_len_ctx *len_ctx, b.style = css_computed_border_top_style(row->style); b.color = css_computed_border_top_color(row->style, &b.c); css_computed_border_top_width(row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, row->style); + b.width = css_unit_len2device_px(row->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, a, *a_src, &b, b_src)) { *a = b; *a_src = b_src; @@ -272,14 +276,13 @@ table_cell_top_process_row(const nscss_len_ctx *len_ctx, c->style, &b.c); css_computed_border_bottom_width(c->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, - b.width, - b.unit, - c->style); + b.width = css_unit_len2device_px( + c->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_CELL; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, a, *a_src, &b, @@ -305,7 +308,7 @@ table_cell_top_process_row(const nscss_len_ctx *len_ctx, /** * Process a group * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param cell Cell being considered * \param group Group to process * \param a Current border style for cell @@ -316,7 +319,7 @@ table_cell_top_process_row(const nscss_len_ctx *len_ctx, * \post \a a_src will be updated also */ static bool -table_cell_top_process_group(const nscss_len_ctx *len_ctx, +table_cell_top_process_group(const css_unit_ctx *unit_len_ctx, struct box *cell, struct box *group, struct border *a, @@ -329,11 +332,12 @@ table_cell_top_process_group(const nscss_len_ctx *len_ctx, b.style = css_computed_border_bottom_style(group->style); b.color = css_computed_border_bottom_color(group->style, &b.c); css_computed_border_bottom_width(group->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); + b.width = css_unit_len2device_px(group->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW_GROUP; - if (table_border_is_more_eyecatching(len_ctx, a, *a_src, &b, b_src)) { + if (table_border_is_more_eyecatching(unit_len_ctx, a, *a_src, &b, b_src)) { *a = b; *a_src = b_src; } @@ -342,7 +346,7 @@ table_cell_top_process_group(const nscss_len_ctx *len_ctx, /* Process rows in group, starting with last */ struct box *row = group->last; - while (table_cell_top_process_row(len_ctx, cell, row, + while (table_cell_top_process_row(unit_len_ctx, cell, row, a, a_src) == false) { if (row->prev == NULL) { return false; @@ -355,11 +359,12 @@ table_cell_top_process_group(const nscss_len_ctx *len_ctx, b.style = css_computed_border_top_style(group->style); b.color = css_computed_border_top_color(group->style, &b.c); css_computed_border_top_width(group->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); + b.width = css_unit_len2device_px(group->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW_GROUP; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, a, *a_src, &b, b_src)) { *a = b; *a_src = b_src; @@ -375,11 +380,11 @@ table_cell_top_process_group(const nscss_len_ctx *len_ctx, /** * Calculate used values of border-left-{style,color,width} * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param cell Table cell to consider */ static void -table_used_left_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) +table_used_left_border_for_cell(const css_unit_ctx *unit_len_ctx, struct box *cell) { struct border a, b; box_type a_src, b_src; @@ -390,7 +395,8 @@ table_used_left_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) a.style = css_computed_border_left_style(cell->style); a.color = css_computed_border_left_color(cell->style, &a.c); css_computed_border_left_width(cell->style, &a.width, &a.unit); - a.width = nscss_len2px(len_ctx, a.width, a.unit, cell->style); + a.width = css_unit_len2device_px(cell->style, unit_len_ctx, + a.width, a.unit); a.unit = CSS_UNIT_PX; a_src = BOX_TABLE_CELL; @@ -423,11 +429,12 @@ table_used_left_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) b.style = css_computed_border_right_style(prev->style); b.color = css_computed_border_right_color(prev->style, &b.c); css_computed_border_right_width(prev->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, prev->style); + b.width = css_unit_len2device_px(prev->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_CELL; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, &a, a_src, &b, b_src)) { a = b; a_src = b_src; @@ -446,12 +453,13 @@ table_used_left_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) row->style, &b.c); css_computed_border_left_width( row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, - b.width, b.unit, row->style); + b.width = css_unit_len2device_px( + row->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, &a, a_src, &b, b_src)) { a = b; a_src = b_src; @@ -466,11 +474,12 @@ table_used_left_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) b.style = css_computed_border_left_style(group->style); b.color = css_computed_border_left_color(group->style, &b.c); css_computed_border_left_width(group->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); + b.width = css_unit_len2device_px(group->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW_GROUP; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, &a, a_src, &b, b_src)) { a = b; a_src = b_src; @@ -480,11 +489,12 @@ table_used_left_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) b.style = css_computed_border_left_style(table->style); b.color = css_computed_border_left_color(table->style, &b.c); css_computed_border_left_width(table->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, table->style); + b.width = css_unit_len2device_px(table->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, &a, a_src, &b, b_src)) { a = b; a_src = b_src; @@ -494,21 +504,19 @@ table_used_left_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) /* a now contains the used left border for the cell */ cell->border[LEFT].style = a.style; cell->border[LEFT].c = a.c; - cell->border[LEFT].width = FIXTOINT(nscss_len2px(len_ctx, - a.width, - a.unit, - cell->style)); + cell->border[LEFT].width = FIXTOINT(css_unit_len2device_px( + cell->style, unit_len_ctx, a.width, a.unit)); } /** * Calculate used values of border-top-{style,color,width} * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param cell Table cell to consider */ static void -table_used_top_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) +table_used_top_border_for_cell(const css_unit_ctx *unit_len_ctx, struct box *cell) { struct border a, b; box_type a_src, b_src; @@ -519,7 +527,8 @@ table_used_top_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) a.style = css_computed_border_top_style(cell->style); css_computed_border_top_color(cell->style, &a.c); css_computed_border_top_width(cell->style, &a.width, &a.unit); - a.width = nscss_len2px(len_ctx, a.width, a.unit, cell->style); + a.width = css_unit_len2device_px(cell->style, unit_len_ctx, + a.width, a.unit); a.unit = CSS_UNIT_PX; a_src = BOX_TABLE_CELL; @@ -527,18 +536,19 @@ table_used_top_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) b.style = css_computed_border_top_style(row->style); css_computed_border_top_color(row->style, &b.c); css_computed_border_top_width(row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, row->style); + b.width = css_unit_len2device_px(row->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW; - if (table_border_is_more_eyecatching(len_ctx, &a, a_src, &b, b_src)) { + if (table_border_is_more_eyecatching(unit_len_ctx, &a, a_src, &b, b_src)) { a = b; a_src = b_src; } if (row->prev != NULL) { /* Consider row(s) above */ - while (table_cell_top_process_row(len_ctx, cell, row->prev, + while (table_cell_top_process_row(unit_len_ctx, cell, row->prev, &a, &a_src) == false) { if (row->prev->prev == NULL) { /* Consider row group */ @@ -559,11 +569,12 @@ table_used_top_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) b.style = css_computed_border_top_style(group->style); b.color = css_computed_border_top_color(group->style, &b.c); css_computed_border_top_width(group->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); + b.width = css_unit_len2device_px(group->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW_GROUP; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, &a, a_src, &b, b_src)) { a = b; a_src = b_src; @@ -571,16 +582,16 @@ table_used_top_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) if (group->prev == NULL) { /* Top border of table */ - table_cell_top_process_table(len_ctx, + table_cell_top_process_table(unit_len_ctx, group->parent, &a, &a_src); } else { /* Process previous group(s) */ - while (table_cell_top_process_group(len_ctx, + while (table_cell_top_process_group(unit_len_ctx, cell, group->prev, &a, &a_src) == false) { if (group->prev->prev == NULL) { /* Top border of table */ - table_cell_top_process_table(len_ctx, + table_cell_top_process_table(unit_len_ctx, group->parent, &a, &a_src); break; @@ -594,20 +605,18 @@ table_used_top_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) /* a now contains the used top border for the cell */ cell->border[TOP].style = a.style; cell->border[TOP].c = a.c; - cell->border[TOP].width = FIXTOINT(nscss_len2px(len_ctx, - a.width, - a.unit, - cell->style)); + cell->border[TOP].width = FIXTOINT(css_unit_len2device_px( + cell->style, unit_len_ctx, a.width, a.unit)); } /** * Calculate used values of border-right-{style,color,width} * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param cell Table cell to consider */ static void -table_used_right_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) +table_used_right_border_for_cell(const css_unit_ctx *unit_len_ctx, struct box *cell) { struct border a, b; box_type a_src, b_src; @@ -618,7 +627,8 @@ table_used_right_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) a.style = css_computed_border_right_style(cell->style); css_computed_border_right_color(cell->style, &a.c); css_computed_border_right_width(cell->style, &a.width, &a.unit); - a.width = nscss_len2px(len_ctx, a.width, a.unit, cell->style); + a.width = css_unit_len2device_px(cell->style, unit_len_ctx, + a.width, a.unit); a.unit = CSS_UNIT_PX; a_src = BOX_TABLE_CELL; @@ -643,14 +653,13 @@ table_used_right_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) css_computed_border_right_width(row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, - b.width, - b.unit, - row->style); + b.width = css_unit_len2device_px( + row->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, &a, a_src, &b, b_src)) { a = b; @@ -667,11 +676,12 @@ table_used_right_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) b.color = css_computed_border_right_color(group->style, &b.c); css_computed_border_right_width(group->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); + b.width = css_unit_len2device_px(group->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW_GROUP; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, &a, a_src, &b, b_src)) { a = b; a_src = b_src; @@ -682,11 +692,12 @@ table_used_right_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) b.color = css_computed_border_right_color(table->style, &b.c); css_computed_border_right_width(table->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, table->style); + b.width = css_unit_len2device_px(table->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, &a, a_src, &b, b_src)) { a = b; @@ -697,21 +708,19 @@ table_used_right_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) /* a now contains the used right border for the cell */ cell->border[RIGHT].style = a.style; cell->border[RIGHT].c = a.c; - cell->border[RIGHT].width = FIXTOINT(nscss_len2px(len_ctx, - a.width, - a.unit, - cell->style)); + cell->border[RIGHT].width = FIXTOINT(css_unit_len2device_px( + cell->style, unit_len_ctx, a.width, a.unit)); } /** * Calculate used values of border-bottom-{style,color,width} * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param cell Table cell to consider */ static void -table_used_bottom_border_for_cell(const nscss_len_ctx *len_ctx, +table_used_bottom_border_for_cell(const css_unit_ctx *unit_len_ctx, struct box *cell) { struct border a, b; @@ -723,7 +732,8 @@ table_used_bottom_border_for_cell(const nscss_len_ctx *len_ctx, a.style = css_computed_border_bottom_style(cell->style); css_computed_border_bottom_color(cell->style, &a.c); css_computed_border_bottom_width(cell->style, &a.width, &a.unit); - a.width = nscss_len2px(len_ctx, a.width, a.unit, cell->style); + a.width = css_unit_len2device_px(cell->style, unit_len_ctx, + a.width, a.unit); a.unit = CSS_UNIT_PX; a_src = BOX_TABLE_CELL; @@ -747,11 +757,12 @@ table_used_bottom_border_for_cell(const nscss_len_ctx *len_ctx, b.style = css_computed_border_bottom_style(row->style); b.color = css_computed_border_bottom_color(row->style, &b.c); css_computed_border_bottom_width(row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, row->style); + b.width = css_unit_len2device_px(row->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, &a, a_src, &b, b_src)) { a = b; a_src = b_src; @@ -762,11 +773,12 @@ table_used_bottom_border_for_cell(const nscss_len_ctx *len_ctx, b.color = css_computed_border_bottom_color(group->style, &b.c); css_computed_border_bottom_width(group->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); + b.width = css_unit_len2device_px(group->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW_GROUP; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, &a, a_src, &b, b_src)) { a = b; a_src = b_src; @@ -777,11 +789,12 @@ table_used_bottom_border_for_cell(const nscss_len_ctx *len_ctx, b.color = css_computed_border_bottom_color(table->style, &b.c); css_computed_border_bottom_width(table->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, table->style); + b.width = css_unit_len2device_px(table->style, unit_len_ctx, + b.width, b.unit); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE; - if (table_border_is_more_eyecatching(len_ctx, + if (table_border_is_more_eyecatching(unit_len_ctx, &a, a_src, &b, b_src)) { a = b; } @@ -790,14 +803,14 @@ table_used_bottom_border_for_cell(const nscss_len_ctx *len_ctx, /* a now contains the used bottom border for the cell */ cell->border[BOTTOM].style = a.style; cell->border[BOTTOM].c = a.c; - cell->border[BOTTOM].width = FIXTOINT(nscss_len2px(len_ctx, - a.width, a.unit, cell->style)); + cell->border[BOTTOM].width = FIXTOINT(css_unit_len2device_px( + cell->style, unit_len_ctx, a.width, a.unit)); } /* exported interface documented in html/table.h */ bool -table_calculate_column_types(const nscss_len_ctx *len_ctx, struct box *table) +table_calculate_column_types(const css_unit_ctx *unit_len_ctx, struct box *table) { unsigned int i, j; struct column *col; @@ -839,14 +852,16 @@ table_calculate_column_types(const nscss_len_ctx *len_ctx, struct box *table) col[i].positioned = false; } - type = css_computed_width(cell->style, &value, &unit); + type = css_computed_width_static(cell->style, &value, &unit); /* fixed width takes priority over any other width type */ if (col[i].type != COLUMN_WIDTH_FIXED && type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT) { col[i].type = COLUMN_WIDTH_FIXED; - col[i].width = FIXTOINT(nscss_len2px(len_ctx, - value, unit, cell->style)); + col[i].width = FIXTOINT(css_unit_len2device_px( + cell->style, + unit_len_ctx, + value, unit)); if (col[i].width < 0) col[i].width = 0; continue; @@ -904,16 +919,18 @@ table_calculate_column_types(const nscss_len_ctx *len_ctx, struct box *table) if (!unknown_columns) continue; - type = css_computed_width(cell->style, &value, &unit); + type = css_computed_width_static(cell->style, &value, &unit); /* if cell is fixed width, and all spanned columns are fixed * or unknown width, split extra width among unknown columns */ if (type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT && fixed_columns + unknown_columns == cell->columns) { - int width = (FIXTOFLT(nscss_len2px(len_ctx, value, unit, - cell->style)) - fixed_width) / - unknown_columns; + int width = (FIXTOFLT(css_unit_len2device_px( + cell->style, + unit_len_ctx, + value, unit)) - + fixed_width) / unknown_columns; if (width < 0) width = 0; for (j = 0; j != cell->columns; j++) { @@ -968,7 +985,7 @@ table_calculate_column_types(const nscss_len_ctx *len_ctx, struct box *table) /* exported interface documented in html/table.h */ -void table_used_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) +void table_used_border_for_cell(const css_unit_ctx *unit_len_ctx, struct box *cell) { int side; @@ -986,8 +1003,9 @@ void table_used_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) &cell->border[LEFT].c); css_computed_border_left_width(cell->style, &width, &unit); cell->border[LEFT].width = - FIXTOINT(nscss_len2px(len_ctx, - width, unit, cell->style)); + FIXTOINT(css_unit_len2device_px( + cell->style, unit_len_ctx, + width, unit)); /* Top border */ cell->border[TOP].style = @@ -996,8 +1014,9 @@ void table_used_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) &cell->border[TOP].c); css_computed_border_top_width(cell->style, &width, &unit); cell->border[TOP].width = - FIXTOINT(nscss_len2px(len_ctx, - width, unit, cell->style)); + FIXTOINT(css_unit_len2device_px( + cell->style, unit_len_ctx, + width, unit)); /* Right border */ cell->border[RIGHT].style = @@ -1006,8 +1025,9 @@ void table_used_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) &cell->border[RIGHT].c); css_computed_border_right_width(cell->style, &width, &unit); cell->border[RIGHT].width = - FIXTOINT(nscss_len2px(len_ctx, - width, unit, cell->style)); + FIXTOINT(css_unit_len2device_px( + cell->style, unit_len_ctx, + width, unit)); /* Bottom border */ cell->border[BOTTOM].style = @@ -1016,20 +1036,21 @@ void table_used_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) &cell->border[BOTTOM].c); css_computed_border_bottom_width(cell->style, &width, &unit); cell->border[BOTTOM].width = - FIXTOINT(nscss_len2px(len_ctx, - width, unit, cell->style)); + FIXTOINT(css_unit_len2device_px( + cell->style, unit_len_ctx, + width, unit)); } else { /* Left border */ - table_used_left_border_for_cell(len_ctx, cell); + table_used_left_border_for_cell(unit_len_ctx, cell); /* Top border */ - table_used_top_border_for_cell(len_ctx, cell); + table_used_top_border_for_cell(unit_len_ctx, cell); /* Right border */ - table_used_right_border_for_cell(len_ctx, cell); + table_used_right_border_for_cell(unit_len_ctx, cell); /* Bottom border */ - table_used_bottom_border_for_cell(len_ctx, cell); + table_used_bottom_border_for_cell(unit_len_ctx, cell); } /* Finally, ensure that any borders configured as diff --git a/content/handlers/html/table.h b/content/handlers/html/table.h index ac4af25ad..557032b06 100644 --- a/content/handlers/html/table.h +++ b/content/handlers/html/table.h @@ -33,24 +33,24 @@ struct box; /** * Determine the column width types for a table. * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param table box of type BOX_TABLE * \return true on success, false on memory exhaustion * * The table->col array is allocated and type and width are filled in for each * column. */ -bool table_calculate_column_types(const nscss_len_ctx *len_ctx, struct box *table); +bool table_calculate_column_types(const css_unit_ctx *unit_len_ctx, struct box *table); /** * Calculate used values of border-{trbl}-{style,color,width} for table cells. * - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param cell Table cell to consider * * \post \a cell's border array is populated */ -void table_used_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell); +void table_used_border_for_cell(const css_unit_ctx *unit_len_ctx, struct box *cell); #endif diff --git a/content/handlers/html/textselection.c b/content/handlers/html/textselection.c index 9de7590f0..9b83e73ec 100644 --- a/content/handlers/html/textselection.c +++ b/content/handlers/html/textselection.c @@ -240,7 +240,7 @@ coords_from_range(struct box *box, * \param text pointer to text being added, or NULL for newline * \param length length of text to be appended (bytes) * \param box pointer to text box, or NULL if from textplain - * \param len_ctx Length conversion context + * \param unit_len_ctx Length conversion context * \param handle selection string to append to * \param whitespace_text whitespace to place before text for formatting * may be NULL @@ -251,7 +251,7 @@ static nserror selection_copy_box(const char *text, size_t length, struct box *box, - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, struct selection_string *handle, const char *whitespace_text, size_t whitespace_length) @@ -278,7 +278,7 @@ selection_copy_box(const char *text, if (box->style != NULL) { /* Override default font style */ - font_plot_style_from_css(len_ctx, box->style, &style); + font_plot_style_from_css(unit_len_ctx, box->style, &style); pstyle = &style; } else { /* If there's no style, there must be no text */ @@ -300,7 +300,7 @@ selection_copy_box(const char *text, * boxes that lie (partially) within the given range * * \param box box subtree - * \param len_ctx Length conversion context. + * \param unit_len_ctx Length conversion context. * \param start_idx start of range within textual representation (bytes) * \param end_idx end of range * \param handler handler function to call @@ -312,7 +312,7 @@ selection_copy_box(const char *text, */ static nserror selection_copy(struct box *box, - const nscss_len_ctx *len_ctx, + const css_unit_ctx *unit_len_ctx, unsigned start_idx, unsigned end_idx, struct selection_string *selstr, @@ -340,7 +340,7 @@ selection_copy(struct box *box, /* do the marker box before continuing with the rest of the * list element */ res = selection_copy(box->list_marker, - len_ctx, + unit_len_ctx, start_idx, end_idx, selstr, @@ -383,7 +383,7 @@ selection_copy(struct box *box, res = selection_copy_box(box->text + start_off, min(box->length, end_off) - start_off, box, - len_ctx, + unit_len_ctx, selstr, whitespace_text, whitespace_length); @@ -415,7 +415,7 @@ selection_copy(struct box *box, struct box *next = child->next; res = selection_copy(child, - len_ctx, + unit_len_ctx, start_idx, end_idx, selstr, @@ -518,7 +518,7 @@ html_textselection_copy(struct content *c, } return selection_copy(html->layout, - &html->len_ctx, + &html->unit_len_ctx, start_idx, end_idx, selstr, |