diff options
-rw-r--r-- | content/handlers/html/box.h | 16 | ||||
-rw-r--r-- | content/handlers/html/box_construct.c | 6 | ||||
-rw-r--r-- | content/handlers/html/box_normalise.c | 204 | ||||
-rw-r--r-- | content/handlers/html/layout.c | 135 |
4 files changed, 345 insertions, 16 deletions
diff --git a/content/handlers/html/box.h b/content/handlers/html/box.h index 8ab2875a0..fbe7cb0f1 100644 --- a/content/handlers/html/box.h +++ b/content/handlers/html/box.h @@ -118,7 +118,8 @@ typedef enum { BOX_TABLE_ROW_GROUP, BOX_FLOAT_LEFT, BOX_FLOAT_RIGHT, BOX_INLINE_BLOCK, BOX_BR, BOX_TEXT, - BOX_INLINE_END, BOX_NONE + BOX_INLINE_END, BOX_NONE, + BOX_FLEX, BOX_INLINE_FLEX, } box_type; @@ -389,6 +390,19 @@ 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; +} + /** * Retrieve the box for a dom node, if there is one * diff --git a/content/handlers/html/box_construct.c b/content/handlers/html/box_construct.c index f545785cd..1932b69b7 100644 --- a/content/handlers/html/box_construct.c +++ b/content/handlers/html/box_construct.c @@ -230,8 +230,8 @@ static const box_type box_map[] = { BOX_TABLE_CELL, /* CSS_DISPLAY_TABLE_CELL */ BOX_INLINE, /* CSS_DISPLAY_TABLE_CAPTION */ BOX_NONE, /* CSS_DISPLAY_NONE */ - BOX_BLOCK, /* CSS_DISPLAY_FLEX */ - BOX_INLINE_BLOCK, /* CSS_DISPLAY_INLINE_FLEX */ + BOX_FLEX, /* CSS_DISPLAY_FLEX */ + BOX_INLINE_FLEX, /* CSS_DISPLAY_INLINE_FLEX */ }; /* Exported function, see box.h */ @@ -946,6 +946,7 @@ bool box_construct_element(struct box_construct_ctx *ctx, (box->type == BOX_INLINE || box->type == BOX_BR || box->type == BOX_INLINE_BLOCK || + box->type == BOX_INLINE_FLEX || css_computed_float(box->style) == CSS_FLOAT_LEFT || css_computed_float(box->style) == CSS_FLOAT_RIGHT) && props.node_is_root == false) { @@ -994,6 +995,7 @@ bool box_construct_element(struct box_construct_ctx *ctx, 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 */ diff --git a/content/handlers/html/box_normalise.c b/content/handlers/html/box_normalise.c index 7155cb722..6bb107d64 100644 --- a/content/handlers/html/box_normalise.c +++ b/content/handlers/html/box_normalise.c @@ -93,6 +93,180 @@ static bool box_normalise_inline_container( const struct box *root, html_content *c); +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, 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, 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; +} + /** * Ensure the box tree is correctly nested by adding and removing nodes. * @@ -104,14 +278,15 @@ static bool box_normalise_inline_container( * 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 */ @@ -147,6 +322,11 @@ bool box_normalise_block( 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) @@ -162,6 +342,7 @@ bool box_normalise_block( break; case BOX_INLINE: case BOX_INLINE_END: + case BOX_INLINE_FLEX: case BOX_INLINE_BLOCK: case BOX_FLOAT_LEFT: case BOX_FLOAT_RIGHT: @@ -275,6 +456,7 @@ bool box_normalise_table( return false; } break; + case BOX_FLEX: case BOX_BLOCK: case BOX_INLINE_CONTAINER: case BOX_TABLE: @@ -312,6 +494,7 @@ bool box_normalise_table( 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 || @@ -342,6 +525,7 @@ bool box_normalise_table( break; case BOX_INLINE: case BOX_INLINE_END: + case BOX_INLINE_FLEX: case BOX_INLINE_BLOCK: case BOX_FLOAT_LEFT: case BOX_FLOAT_RIGHT: @@ -634,6 +818,7 @@ bool box_normalise_table_row_group( c) == false) return false; break; + case BOX_FLEX: case BOX_BLOCK: case BOX_INLINE_CONTAINER: case BOX_TABLE: @@ -667,6 +852,7 @@ bool box_normalise_table_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 || @@ -696,6 +882,7 @@ bool box_normalise_table_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: @@ -787,6 +974,7 @@ bool box_normalise_table_row( return false; cell = child; break; + case BOX_FLEX: case BOX_BLOCK: case BOX_INLINE_CONTAINER: case BOX_TABLE: @@ -820,6 +1008,7 @@ bool box_normalise_table_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 || @@ -847,6 +1036,7 @@ bool box_normalise_table_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: @@ -994,6 +1184,11 @@ bool box_normalise_inline_container( 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 */ @@ -1028,6 +1223,7 @@ bool box_normalise_inline_container( box_free(child); } break; + case BOX_FLEX: case BOX_BLOCK: case BOX_INLINE_CONTAINER: case BOX_TABLE: diff --git a/content/handlers/html/layout.c b/content/handlers/html/layout.c index ab8c83afe..0beee0361 100644 --- a/content/handlers/html/layout.c +++ b/content/handlers/html/layout.c @@ -653,7 +653,8 @@ 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; @@ -1011,7 +1012,9 @@ static void layout_minmax_block( bool child_has_height = false; assert(block->type == BOX_BLOCK || + block->type == BOX_FLEX || block->type == BOX_INLINE_BLOCK || + block->type == BOX_INLINE_FLEX || block->type == BOX_TABLE_CELL); /* check if the widths have already been calculated */ @@ -1026,7 +1029,8 @@ static void layout_minmax_block( /* set whether the minimum width is of any interest for this box */ if (((block->parent && lh__box_is_float_box(block->parent)) || - block->type == BOX_INLINE_BLOCK) && + 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; @@ -1112,6 +1116,11 @@ static void layout_minmax_block( child_has_height = true; child->flags |= MAKE_HEIGHT; break; + case BOX_FLEX: + layout_minmax_block(child, font_func, content); + child_has_height = true; + child->flags |= MAKE_HEIGHT; + break; default: assert(0); } @@ -2034,6 +2043,101 @@ static void layout_move_children(struct box *box, int x, int y) } } +static inline bool layout_flex__main_is_horizontal(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; + } +} + +/** + * 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 + */ +static bool layout_flex(struct box *flex, int available_width, + html_content *content) +{ + bool main_is_horizontal = layout_flex__main_is_horizontal(flex); + int max_width, min_width, max_height, min_height; + const nscss_len_ctx *len_ctx = &content->len_ctx; + struct box_border *border = flex->border; + int *padding = flex->padding; + int *margin = flex->margin; + int *len_main, *len_cross; + unsigned n_children; + struct flex_item_data { + int main_size; + } *flex_item; + + assert(flex->width != UNKNOWN_WIDTH); + assert(flex->width != AUTO); + + layout_find_dimensions(len_ctx, available_width, -1, flex, flex->style, + &flex->width, &flex->height, &max_width, &min_width, + &max_height, &min_height, margin, padding, border); + + if (flex->width == AUTO) { + int w = available_width; + int fixed = 0; + float frac = 0; + + calculate_mbp_width(len_ctx, flex->style, LEFT, + false, true, true, &fixed, &frac); + calculate_mbp_width(len_ctx, flex->style, RIGHT, + false, true, true, &fixed, &frac); + + w -= frac * available_width + fixed; + flex->width = w > 0 ? w : 0; + } + + if (max_width >= 0 && flex->width > max_width) { + flex->width = max_width; + } + if (min_width > 0 && flex->width < min_width) { + flex->width = min_width; + } + + 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; + } + } + + if (main_is_horizontal) { + len_main = &flex->width; + len_cross = &flex->height; + } else { + len_main = &flex->height; + len_cross = &flex->width; + } + + flex_item = calloc(box_count_children(flex), sizeof(*flex_item)); + if (flex_item == false) { + return false; + } + + + free(flex_item); + return true; +} + /** * Layout a table. @@ -4039,7 +4143,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 @@ -4123,7 +4229,7 @@ layout_block_context(struct box *block, layout_block_add_scrollbar(box, RIGHT); layout_block_add_scrollbar(box, BOTTOM); } - } else if (box->type == BOX_TABLE) { + } else if (box->type == BOX_TABLE || box->type == BOX_FLEX) { if (box->style != NULL) { enum css_width_e wtype; css_fixed width = 0; @@ -4156,11 +4262,21 @@ layout_block_context(struct box *block, x1; } } - if (!layout_table(box, box->parent->width - lm - rm, - content)) - return false; - layout_solve_width(box, box->parent->width, box->width, - lm, rm, -1, -1); + if (box->type == BOX_TABLE) { + if (!layout_table(box, + box->parent->width - lm - rm, + content)) + return false; + layout_solve_width(box, + box->parent->width, box->width, + lm, rm, -1, -1); + } else { + if (!layout_flex(box, + box->parent->width - lm - rm, + content)) { + return false; + } + } } /* Position box: horizontal. */ @@ -4176,6 +4292,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)) || |