summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Drake <michael.drake@codethink.co.uk>2020-01-08 15:51:18 +0000
committerMichael Drake <michael.drake@codethink.co.uk>2020-01-08 15:51:18 +0000
commiteba213195070847cd7352f039956cf832a40e9e0 (patch)
tree3035a223ac3e55a7441e2215bfff0bf606b563e2
parent6f31a5cb88ddcb222867dd1f825848600e49c611 (diff)
downloadnetsurf-eba213195070847cd7352f039956cf832a40e9e0.tar.gz
netsurf-eba213195070847cd7352f039956cf832a40e9e0.tar.bz2
WIP: flex
-rw-r--r--content/handlers/html/box.h16
-rw-r--r--content/handlers/html/box_construct.c6
-rw-r--r--content/handlers/html/box_normalise.c204
-rw-r--r--content/handlers/html/layout.c135
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)) ||