mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-11-22 06:21:45 +03:00
html: layout: Split out common helpers
This commit is contained in:
parent
16252bb9f5
commit
399f0063ba
@ -67,13 +67,9 @@
|
||||
#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);
|
||||
@ -84,7 +80,7 @@ typedef uint8_t (*css_border_color_func)(
|
||||
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 +88,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 +96,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,67 +104,15 @@ 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,
|
||||
[LEFT] = css_computed_border_left_style,
|
||||
};
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** 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,
|
||||
@ -176,10 +120,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,
|
||||
@ -314,74 +254,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 (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 += 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.
|
||||
*
|
||||
@ -1226,375 +1098,6 @@ static void layout_minmax_block(
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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,
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find next block that current margin collapses to.
|
||||
*
|
||||
@ -2058,15 +1561,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 */
|
||||
@ -3977,21 +3475,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 */
|
||||
|
584
content/handlers/html/layout_internal.h
Normal file
584
content/handlers/html/layout_internal.h
Normal file
@ -0,0 +1,584 @@
|
||||
/*
|
||||
* 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 += FIXTOINT(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 += FIXTOINT(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
|
Loading…
Reference in New Issue
Block a user