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