You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
netsurf/content/handlers/html/layout/block.c

909 lines
25 KiB

/*
* Copyright 2023 Vincent Sanders <vince@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 for blocks
*/
#include "utils/log.h"
#include "utils/utils.h"
#include "netsurf/content.h"
#include "netsurf/mouse.h"
#include "netsurf/plot_style.h"
#include "content/content.h"
#include "desktop/scrollbar.h"
#include "desktop/textarea.h"
#include "html/private.h"
#include "html/box.h"
#include "html/box_inspect.h"
#include "html/font.h"
#include "html/form_internal.h"
#include "html/layout/internal.h"
#include "html/layout/flex.h"
#include "html/layout/table.h"
#include "html/layout/line.h"
#include "html/layout/block.h"
/**
* Find next block that current margin collapses to.
*
* \param unit_len_ctx Length conversion context
* \param box box to start tree-order search from (top margin is included)
* \param block box responsible for current block fromatting context
* \param viewport_height height of viewport in px
* \param max_pos_margin updated to to maximum positive margin encountered
* \param max_neg_margin updated to to maximum negative margin encountered
* \return next box that current margin collapses to, or NULL if none.
*/
static struct box*
layout_next_margin_block(const css_unit_ctx *unit_len_ctx,
struct box *box,
struct box *block,
int viewport_height,
int *max_pos_margin,
int *max_neg_margin)
{
assert(block != NULL);
while (box != NULL) {
if (box->type == BOX_INLINE_CONTAINER || (box->style &&
(css_computed_position(box->style) !=
CSS_POSITION_ABSOLUTE &&
css_computed_position(box->style) !=
CSS_POSITION_FIXED))) {
/* Not positioned */
/* Get margins */
if (box->style) {
layout_find_dimensions(unit_len_ctx,
box->parent->width,
viewport_height, box,
box->style,
NULL, NULL, NULL, NULL,
NULL, NULL, box->margin,
box->padding, box->border);
/* Apply top margin */
if (*max_pos_margin < box->margin[TOP])
*max_pos_margin = box->margin[TOP];
else if (*max_neg_margin < -box->margin[TOP])
*max_neg_margin = -box->margin[TOP];
}
/* Check whether box is the box current margin collapses
* to */
if (box->flags & MAKE_HEIGHT ||
box->border[TOP].width ||
box->padding[TOP] ||
(box->style &&
css_computed_overflow_y(box->style) !=
CSS_OVERFLOW_VISIBLE) ||
(box->type == BOX_INLINE_CONTAINER &&
!box_is_first_child(box))) {
/* Collapse to this box; return it */
return box;
}
}
/* Find next box */
if (box->type == BOX_BLOCK && !box->object && box->children &&
box->style &&
css_computed_overflow_y(box->style) ==
CSS_OVERFLOW_VISIBLE) {
/* Down into children. */
box = box->children;
} else {
if (!box->next) {
/* No more siblings:
* Go up to first ancestor with a sibling. */
do {
/* Apply bottom margin */
if (*max_pos_margin <
box->margin[BOTTOM])
*max_pos_margin =
box->margin[BOTTOM];
else if (*max_neg_margin <
-box->margin[BOTTOM])
*max_neg_margin =
-box->margin[BOTTOM];
box = box->parent;
} while (box != block && !box->next);
if (box == block) {
/* Margins don't collapse with stuff
* outside the block formatting context
*/
return block;
}
}
/* Apply bottom margin */
if (*max_pos_margin < box->margin[BOTTOM])
*max_pos_margin = box->margin[BOTTOM];
else if (*max_neg_margin < -box->margin[BOTTOM])
*max_neg_margin = -box->margin[BOTTOM];
/* To next sibling. */
box = box->next;
/* Get margins */
if (box->style) {
layout_find_dimensions(unit_len_ctx,
box->parent->width,
viewport_height, box,
box->style,
NULL, NULL, NULL, NULL,
NULL, NULL, box->margin,
box->padding, box->border);
}
}
}
return NULL;
}
/**
* Manipulate a block's [RB]padding/height/width to accommodate scrollbars
*
* \param box Box to apply scrollbar space too. Must be BOX_BLOCK.
* \param which Which scrollbar to make space for. Must be RIGHT or BOTTOM.
*/
static void layout_block_add_scrollbar(struct box *box, int which)
{
enum css_overflow_e overflow_x, overflow_y;
assert(box->type == BOX_BLOCK && (which == RIGHT || which == BOTTOM));
if (box->style == NULL)
return;
overflow_x = css_computed_overflow_x(box->style);
overflow_y = css_computed_overflow_y(box->style);
if (which == BOTTOM &&
(overflow_x == CSS_OVERFLOW_SCROLL ||
overflow_x == CSS_OVERFLOW_AUTO ||
(box->object &&
content_get_type(box->object) == CONTENT_HTML))) {
/* make space for scrollbar, unless height is AUTO */
if (box->height != AUTO &&
(overflow_x == CSS_OVERFLOW_SCROLL ||
box_hscrollbar_present(box))) {
box->padding[BOTTOM] += SCROLLBAR_WIDTH;
}
} else if (which == RIGHT &&
(overflow_y == CSS_OVERFLOW_SCROLL ||
overflow_y == CSS_OVERFLOW_AUTO ||
(box->object &&
content_get_type(box->object) == CONTENT_HTML))) {
/* make space for scrollbars, unless width is AUTO */
enum css_height_e htype;
css_fixed height = 0;
css_unit hunit = CSS_UNIT_PX;
htype = css_computed_height(box->style, &height, &hunit);
if (which == RIGHT && box->width != AUTO &&
htype == CSS_HEIGHT_SET &&
(overflow_y == CSS_OVERFLOW_SCROLL ||
box_vscrollbar_present(box))) {
box->width -= SCROLLBAR_WIDTH;
box->padding[RIGHT] += SCROLLBAR_WIDTH;
}
}
}
/**
* Layout a block which contains an object.
*
* \param block box of type BLOCK, INLINE_BLOCK, TABLE, or TABLE_CELL
* \return true on success, false on memory exhaustion
*/
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);
NSLOG(layout, DEBUG, "block %p, object %p, width %i", block,
hlcache_handle_get_url(block->object), block->width);
if (content_can_reformat(block->object)) {
content_reformat(block->object, false, block->width, 1);
} else {
/* Non-HTML objects */
/* this case handled already in
* layout_block_find_dimensions() */
}
return true;
}
/**
* Layout lines of text or inline boxes with floats.
*
* \param box inline container box
* \param width horizontal space available
* \param cont ancestor box which defines horizontal space, for floats
* \param cx box position relative to cont
* \param cy box position relative to cont
* \param content memory pool for any new boxes
* \return true on success, false on memory exhaustion
*/
static bool
layout_inline_container(struct box *inline_container,
int width,
struct box *cont,
int cx,
int cy,
html_content *content)
{
bool first_line = true;
bool has_text_children;
struct box *c, *next;
int y = 0;
int curwidth,maxwidth = width;
assert(inline_container->type == BOX_INLINE_CONTAINER);
NSLOG(layout, DEBUG,
"inline_container %p, width %i, cont %p, cx %i, cy %i",
inline_container,
width,
cont,
cx,
cy);
has_text_children = false;
for (c = inline_container->children; c; c = c->next) {
bool is_pre = false;
if (c->style) {
enum css_white_space_e whitespace;
whitespace = css_computed_white_space(c->style);
is_pre = (whitespace == CSS_WHITE_SPACE_PRE ||
whitespace == CSS_WHITE_SPACE_PRE_LINE ||
whitespace == CSS_WHITE_SPACE_PRE_WRAP);
}
if ((lh__box_is_object(c) == false &&
c->text && (c->length || is_pre)) ||
c->type == BOX_BR)
has_text_children = true;
}
/** \todo fix wrapping so that a box with horizontal scrollbar will
* shrink back to 'width' if no word is wider than 'width' (Or just set
* curwidth = width and have the multiword lines wrap to the min width)
*/
for (c = inline_container->children; c; ) {
NSLOG(layout, DEBUG, "c %p", c);
curwidth = inline_container->width;
if (!layout_line(c, &curwidth, &y, cx, cy + y, cont, first_line,
has_text_children, content, &next))
return false;
maxwidth = max(maxwidth,curwidth);
c = next;
first_line = false;
}
inline_container->width = maxwidth;
inline_container->height = y;
return true;
}
/* Documented in layout_block.h */
bool layout_block_context(
struct box *block,
int viewport_height,
html_content *content)
{
struct box *box;
int cx, cy; /**< current coordinates */
int max_pos_margin = 0;
int max_neg_margin = 0;
int y = 0;
int lm, rm;
struct box *margin_collapse = NULL;
bool in_margin = false;
css_fixed gadget_size;
css_unit gadget_unit; /* Checkbox / radio buttons */
assert(block->type == BOX_BLOCK ||
block->type == BOX_INLINE_BLOCK ||
block->type == BOX_TABLE_CELL ||
block->type == BOX_FLEX ||
block->type == BOX_INLINE_FLEX);
assert(block->width != UNKNOWN_WIDTH);
assert(block->width != AUTO);
block->float_children = NULL;
block->cached_place_below_level = 0;
block->clear_level = 0;
/* special case if the block contains an object */
if (block->object) {
int temp_width = block->width;
if (!layout_block_object(block))
return false;
layout_get_object_dimensions(block, &temp_width,
&block->height, INT_MIN, INT_MAX,
INT_MIN, INT_MAX);
return true;
} else if (block->flags & REPLACE_DIM) {
return true;
}
/* special case if the block contains an radio button or checkbox */
if (block->gadget && (block->gadget->type == GADGET_RADIO ||
block->gadget->type == GADGET_CHECKBOX)) {
/* form checkbox or radio button
* if width or height is AUTO, set it to 1em */
gadget_unit = CSS_UNIT_EM;
gadget_size = INTTOFIX(1);
if (block->height == AUTO)
block->height = FIXTOINT(css_unit_len2device_px(
block->style,
&content->unit_len_ctx,
gadget_size, gadget_unit));
}
box = block->children;
/* set current coordinates to top-left of the block */
cx = 0;
y = cy = block->padding[TOP];
if (box)
box->y = block->padding[TOP];
/* Step through the descendants of the block in depth-first order, but
* not into the children of boxes which aren't blocks. For example, if
* the tree passed to this function looks like this (box->type shown):
*
* block -> BOX_BLOCK
* BOX_BLOCK * (1)
* BOX_INLINE_CONTAINER * (2)
* BOX_INLINE
* BOX_TEXT
* ...
* BOX_BLOCK * (3)
* BOX_TABLE * (4)
* BOX_TABLE_ROW
* BOX_TABLE_CELL
* ...
* BOX_TABLE_CELL
* ...
* BOX_BLOCK * (5)
* BOX_INLINE_CONTAINER * (6)
* BOX_TEXT
* ...
* then the while loop will visit each box marked with *, setting box
* to each in the order shown. */
while (box) {
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_FLEX ||
box->type == BOX_TABLE ||
box->type == BOX_INLINE_CONTAINER);
/* Tables are laid out before being positioned, because the
* position depends on the width which is calculated in
* table layout. Blocks and inline containers are positioned
* before being laid out, because width is not dependent on
* content, and the position is required during layout for
* correct handling of floats.
*/
if (box->style &&
(css_computed_position(box->style) ==
CSS_POSITION_ABSOLUTE ||
css_computed_position(box->style) ==
CSS_POSITION_FIXED)) {
box->x = box->parent->padding[LEFT];
/* absolute positioned; this element will establish
* its own block context when it gets laid out later,
* so no need to look at its children now. */
goto advance_to_next_box;
}
/* If we don't know which box the current margin collapses
* through to, find out. Update the pos/neg margin values. */
if (margin_collapse == NULL) {
margin_collapse = layout_next_margin_block(
&content->unit_len_ctx, box, block,
viewport_height,
&max_pos_margin, &max_neg_margin);
/* We have a margin that has not yet been applied. */
in_margin = true;
}
/* Clearance. */
y = 0;
if (box->style && css_computed_clear(box->style) !=
CSS_CLEAR_NONE)
y = layout_clear(block->float_children,
css_computed_clear(box->style));
/* Find box's overflow properties */
if (box->style) {
overflow_x = css_computed_overflow_x(box->style);
overflow_y = css_computed_overflow_y(box->style);
}
/* Blocks establishing a block formatting context get minimum
* left and right margins to avoid any floats. */
lm = rm = 0;
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)) {
/* box establishes new block formatting context
* so available width may be diminished due to
* floats. */
int x0, x1, top;
struct box *left, *right;
top = cy + max_pos_margin - max_neg_margin;
top = (top > y) ? top : y;
x0 = cx;
x1 = cx + box->parent->width -
box->parent->padding[LEFT] -
box->parent->padding[RIGHT];
find_sides(block->float_children, top, top,
&x0, &x1, &left, &right);
/* calculate min required left & right margins
* needed to avoid floats */
lm = x0 - cx;
rm = cx + box->parent->width -
box->parent->padding[LEFT] -
box->parent->padding[RIGHT] -
x1;
}
layout_block_find_dimensions(&content->unit_len_ctx,
box->parent->width,
viewport_height, lm, rm, box);
if (box->type == BOX_BLOCK && !(box->flags & IFRAME)) {
layout_block_add_scrollbar(box, RIGHT);
layout_block_add_scrollbar(box, BOTTOM);
}
} else if (box->type == BOX_TABLE) {
if (box->style != NULL) {
enum css_width_e wtype;
css_fixed width = 0;
css_unit unit = CSS_UNIT_PX;
wtype = css_computed_width(box->style, &width,
&unit);
if (wtype == CSS_WIDTH_AUTO) {
/* max available width may be
* diminished due to floats. */
int x0, x1, top;
struct box *left, *right;
top = cy + max_pos_margin -
max_neg_margin;
top = (top > y) ? top : y;
x0 = cx;
x1 = cx + box->parent->width -
box->parent->padding[LEFT] -
box->parent->padding[RIGHT];
find_sides(block->float_children,
top, top, &x0, &x1,
&left, &right);
/* calculate min required left & right
* margins needed to avoid floats */
lm = x0 - cx;
rm = cx + box->parent->width -
box->parent->padding[LEFT] -
box->parent->padding[RIGHT] -
x1;
}
}
if (!layout_table(box, box->parent->width - lm - rm,
content))
return false;
layout_solve_width(box, box->parent->width, box->width,
lm, rm, -1, -1);
}
/* Position box: horizontal. */
box->x = box->parent->padding[LEFT] + box->margin[LEFT] +
box->border[LEFT].width;
cx += box->x;
/* Position box: vertical. */
if (box->border[TOP].width) {
box->y += box->border[TOP].width;
cy += box->border[TOP].width;
}
/* 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)) ||
margin_collapse == box) &&
in_margin == true) {
/* Margin goes above this box. */
cy += max_pos_margin - max_neg_margin;
box->y += max_pos_margin - max_neg_margin;
/* Current margin has been applied. */
in_margin = false;
max_pos_margin = max_neg_margin = 0;
}
/* Handle clearance */
if (box->type != BOX_INLINE_CONTAINER &&
(y > 0) && (cy < y)) {
/* box clears something*/
box->y += y - cy;
cy = y;
}
/* Unless the box has an overflow style of visible, the box
* establishes a new block context. */
if (box->type == BOX_FLEX ||
(box->type == BOX_BLOCK && box->style &&
(overflow_x != CSS_OVERFLOW_VISIBLE ||
overflow_y != CSS_OVERFLOW_VISIBLE))) {
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];
if (box->height == AUTO) {
box->height = 0;
layout_block_add_scrollbar(box, BOTTOM);
}
cx -= box->x;
cy += box->height + box->padding[BOTTOM] +
box->border[BOTTOM].width;
y = box->y + box->padding[TOP] + box->height +
box->padding[BOTTOM] +
box->border[BOTTOM].width;
/* Skip children, because they are done in the new
* block context */
goto advance_to_next_box;
}
NSLOG(layout, DEBUG, "box %p, cx %i, cy %i, width %i",
box, cx, cy, box->width);
/* Layout (except tables). */
if (box->object) {
if (!layout_block_object(box))
return false;
} else if (box->type == BOX_INLINE_CONTAINER) {
box->width = box->parent->width;
if (!layout_inline_container(box, box->width, block,
cx, cy, content))
return false;
} else if (box->type == BOX_TABLE) {
/* Move down to avoid floats if necessary. */
int x0, x1;
struct box *left, *right;
y = cy;
while (1) {
enum css_width_e wtype;
css_fixed width = 0;
css_unit unit = CSS_UNIT_PX;
wtype = css_computed_width(box->style,
&width, &unit);
x0 = cx;
x1 = cx + box->parent->width;
find_sides(block->float_children, y,
y + box->height,
&x0, &x1, &left, &right);
if (wtype == CSS_WIDTH_AUTO)
break;
if (box->width <= x1 - x0)
break;
if (!left && !right)
break;
else if (!left)
y = right->y + right->height + 1;
else if (!right)
y = left->y + left->height + 1;
else if (left->y + left->height <
right->y + right->height)
y = left->y + left->height + 1;
else
y = right->y + right->height + 1;
}
box->x += x0 - cx;
cx = x0;
box->y += y - cy;
cy = y;
}
/* Advance to next box. */
if (box->type == BOX_BLOCK && !box->object && !(box->iframe) &&
box->children) {
/* Down into children. */
if (box == margin_collapse) {
/* Current margin collapsed though to this box.
* Unset margin_collapse. */
margin_collapse = NULL;
}
y = box->padding[TOP];
box = box->children;
box->y = y;
cy += y;
continue;
} else if (box->type == BOX_BLOCK || box->object ||
box->flags & IFRAME)
cy += box->padding[TOP];
if (box->type == BOX_BLOCK && box->height == AUTO) {
box->height = 0;
layout_block_add_scrollbar(box, BOTTOM);
}
cy += box->height + box->padding[BOTTOM] +
box->border[BOTTOM].width;
cx -= box->x;
y = box->y + box->padding[TOP] + box->height +
box->padding[BOTTOM] +
box->border[BOTTOM].width;
advance_to_next_box:
if (!box->next) {
/* No more siblings:
* up to first ancestor with a sibling. */
do {
if (box == margin_collapse) {
/* Current margin collapsed though to
* this box. Unset margin_collapse. */
margin_collapse = NULL;
}
/* Apply bottom margin */
if (max_pos_margin < box->margin[BOTTOM])
max_pos_margin = box->margin[BOTTOM];
else if (max_neg_margin < -box->margin[BOTTOM])
max_neg_margin = -box->margin[BOTTOM];
box = box->parent;
if (box == block)
break;
/* Margin is invalidated if this is a box
* margins can't collapse through. */
if (box->type == BOX_BLOCK &&
box->flags & MAKE_HEIGHT) {
margin_collapse = NULL;
in_margin = false;
max_pos_margin = max_neg_margin = 0;
}
if (box->height == AUTO) {
box->height = y - box->padding[TOP];
if (box->type == BOX_BLOCK)
layout_block_add_scrollbar(box,
BOTTOM);
} else
cy += box->height -
(y - box->padding[TOP]);
/* Apply any min-height and max-height to
* boxes in normal flow */
if (box->style &&
css_computed_position(box->style) !=
CSS_POSITION_ABSOLUTE &&
layout_apply_minmax_height(
&content->unit_len_ctx,
box, NULL)) {
/* Height altered */
/* Set current cy */
cy += box->height -
(y - box->padding[TOP]);
}
cy += box->padding[BOTTOM] +
box->border[BOTTOM].width;
cx -= box->x;
y = box->y + box->padding[TOP] + box->height +
box->padding[BOTTOM] +
box->border[BOTTOM].width;
} while (box->next == NULL);
if (box == block)
break;
}
/* To next sibling. */
if (box == margin_collapse) {
/* Current margin collapsed though to this box.
* Unset margin_collapse. */
margin_collapse = NULL;
}
if (max_pos_margin < box->margin[BOTTOM])
max_pos_margin = box->margin[BOTTOM];
else if (max_neg_margin < -box->margin[BOTTOM])
max_neg_margin = -box->margin[BOTTOM];
box = box->next;
box->y = y;
}
/* Account for bottom margin of last contained block */
cy += max_pos_margin - max_neg_margin;
/* Increase height to contain any floats inside (CSS 2.1 10.6.7). */
for (box = block->float_children; box; box = box->next_float) {
y = box->y + box->height + box->padding[BOTTOM] +
box->border[BOTTOM].width + box->margin[BOTTOM];
if (cy < y)
cy = y;
}
if (block->height == AUTO) {
block->height = cy - block->padding[TOP];
if (block->type == BOX_BLOCK)
layout_block_add_scrollbar(block, BOTTOM);
}
if (block->style && css_computed_position(block->style) !=
CSS_POSITION_ABSOLUTE) {
/* Block is in normal flow */
layout_apply_minmax_height(&content->unit_len_ctx, block, NULL);
}
if (block->gadget &&
(block->gadget->type == GADGET_TEXTAREA ||
block->gadget->type == GADGET_PASSWORD ||
block->gadget->type == GADGET_TEXTBOX)) {
plot_font_style_t fstyle;
int ta_width = block->padding[LEFT] + block->width +
block->padding[RIGHT];
int ta_height = block->padding[TOP] + block->height +
block->padding[BOTTOM];
font_plot_style_from_css(&content->unit_len_ctx,
block->style, &fstyle);
fstyle.background = NS_TRANSPARENT;
textarea_set_layout(block->gadget->data.text.ta,
&fstyle, ta_width, ta_height,
block->padding[TOP], block->padding[RIGHT],
block->padding[BOTTOM], block->padding[LEFT]);
}
return true;
}
/*
* Compute dimensions of box, margins, paddings, and borders for a block-level
* element.
*
* \param unit_len_ctx Length conversion context
* \param available_width Max width available in pixels
* \param viewport_height Height of viewport in pixels or -ve if unknown
* \param lm min left margin required to avoid floats in px.
* zero if not applicable
* \param rm min right margin required to avoid floats in px.
* zero if not applicable
* \param box box to find dimensions of. updated with new width,
* height, margins, borders and paddings
*
* See CSS 2.1 10.3.3, 10.3.4, 10.6.2, and 10.6.3.
*/
/* Documented in layout_block.h */
void
layout_block_find_dimensions(const css_unit_ctx *unit_len_ctx,
int available_width,
int viewport_height,
int lm,
int rm,
struct box *box)
{
int width, max_width, min_width;
int height, max_height, min_height;
int *margin = box->margin;
int *padding = box->padding;
struct box_border *border = box->border;
const css_computed_style *style = box->style;
layout_find_dimensions(unit_len_ctx,
available_width,
viewport_height,
box,
style,
&width,
&height,
&max_width,
&min_width,
&max_height,
&min_height,
margin,
padding,
border);
if (box->object && !(box->flags & REPLACE_DIM) &&
content_get_type(box->object) != CONTENT_HTML) {
/* block-level replaced element, see 10.3.4 and 10.6.2 */
layout_get_object_dimensions(box,
&width,
&height,
min_width,
max_width,
min_height,
max_height);
}
box->width = layout_solve_width(box,
available_width,
width,
lm,
rm,
max_width,
min_width);
box->height = height;
if (margin[TOP] == AUTO)
margin[TOP] = 0;
if (margin[BOTTOM] == AUTO)
margin[BOTTOM] = 0;
}