netsurf/content/handlers/html/layout_internal.h

585 lines
15 KiB
C
Raw Normal View History

2022-10-15 16:19:36 +03:00
/*
* 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_BLOCK ||
b->type == BOX_TEXT ||
b->type == BOX_INLINE_END;
}
/** 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;
}
/**
* 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));
2022-10-15 16:19:36 +03:00
} 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));
2022-10-15 16:19:36 +03:00
} 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