From 2a0f5525243f992518897730bf2e8618adbbad80 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Sun, 3 May 2020 19:02:47 +0100 Subject: remove forward delcarations and clean up formatting in html table layout processing --- content/handlers/html/table.c | 1191 ++++++++++++++++++++--------------------- content/handlers/html/table.h | 29 +- 2 files changed, 600 insertions(+), 620 deletions(-) (limited to 'content') diff --git a/content/handlers/html/table.c b/content/handlers/html/table.c index 5609e8f29..263ddf1f6 100644 --- a/content/handlers/html/table.c +++ b/content/handlers/html/table.c @@ -46,346 +46,370 @@ struct border { css_unit unit; /**< border-width units */ }; -static void table_used_left_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell); -static void table_used_top_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell); -static void table_used_right_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell); -static void table_used_bottom_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell); -static bool table_border_is_more_eyecatching( - const nscss_len_ctx *len_ctx, - const struct border *a, - box_type a_src, - const struct border *b, - box_type b_src); -static void table_cell_top_process_table( - const nscss_len_ctx *len_ctx, - struct box *table, - struct border *a, - box_type *a_src); -static bool table_cell_top_process_group( - const nscss_len_ctx *len_ctx, - struct box *cell, - struct box *group, - struct border *a, - box_type *a_src); -static bool table_cell_top_process_row( - const nscss_len_ctx *len_ctx, - struct box *cell, - struct box *row, - struct border *a, - box_type *a_src); - /** - * Determine the column width types for a table. - * - * \param len_ctx Length conversion context - * \param table box of type BOX_TABLE - * \return true on success, false on memory exhaustion + * Determine if a border style is more eyecatching than another * - * The table->col array is allocated and type and width are filled in for each - * column. + * \param len_ctx Length conversion context + * \param a Reference border style + * \param a_src Source of \a a + * \param b Candidate border style + * \param b_src Source of \a b + * \return True if \a b is more eyecatching than \a a */ - -bool table_calculate_column_types( - const nscss_len_ctx *len_ctx, - struct box *table) +static bool +table_border_is_more_eyecatching(const nscss_len_ctx *len_ctx, + const struct border *a, + box_type a_src, + const struct border *b, + box_type b_src) { - unsigned int i, j; - struct column *col; - struct box *row_group, *row, *cell; + css_fixed awidth, bwidth; + int impact = 0; - if (table->col) - /* table->col already constructed, for example frameset table */ - return true; + /* See CSS 2.1 $17.6.2.1 */ - table->col = col = talloc_array(table, struct column, table->columns); - if (!col) + /* 1 + 2 -- hidden beats everything, none beats nothing */ + if (a->style == CSS_BORDER_STYLE_HIDDEN || + b->style == CSS_BORDER_STYLE_NONE) return false; - for (i = 0; i != table->columns; i++) { - col[i].type = COLUMN_WIDTH_UNKNOWN; - col[i].width = 0; - col[i].positioned = true; - } - - /* 1st pass: cells with colspan 1 only */ - for (row_group = table->children; row_group; row_group =row_group->next) - for (row = row_group->children; row; row = row->next) - for (cell = row->children; cell; cell = cell->next) { - enum css_width_e type; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - assert(cell->type == BOX_TABLE_CELL); - assert(cell->style); - - if (cell->columns != 1) - continue; - i = cell->start_column; - - if (css_computed_position(cell->style) != - CSS_POSITION_ABSOLUTE && - css_computed_position(cell->style) != - CSS_POSITION_FIXED) { - col[i].positioned = false; - } + if (b->style == CSS_BORDER_STYLE_HIDDEN || + a->style == CSS_BORDER_STYLE_NONE) + return true; - type = css_computed_width(cell->style, &value, &unit); - - /* fixed width takes priority over any other width type */ - if (col[i].type != COLUMN_WIDTH_FIXED && - type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT) { - col[i].type = COLUMN_WIDTH_FIXED; - col[i].width = FIXTOINT(nscss_len2px(len_ctx, - value, unit, cell->style)); - if (col[i].width < 0) - col[i].width = 0; - continue; - } + /* 3a -- wider borders beat narrow ones */ + /* The widths must be absolute, which will be the case + * if they've come from a computed style. */ + assert(a->unit != CSS_UNIT_EM && a->unit != CSS_UNIT_EX); + assert(b->unit != CSS_UNIT_EM && b->unit != CSS_UNIT_EX); + awidth = nscss_len2px(len_ctx, a->width, a->unit, NULL); + bwidth = nscss_len2px(len_ctx, b->width, b->unit, NULL); - if (col[i].type != COLUMN_WIDTH_UNKNOWN) - continue; + if (awidth < bwidth) + return true; + else if (bwidth < awidth) + return false; - if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT) { - col[i].type = COLUMN_WIDTH_PERCENT; - col[i].width = FIXTOINT(value); - if (col[i].width < 0) - col[i].width = 0; - } else if (type == CSS_WIDTH_AUTO) { - col[i].type = COLUMN_WIDTH_AUTO; - } + /* 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 */ + default: + break; } - /* 2nd pass: cells which span multiple columns */ - for (row_group = table->children; row_group; row_group =row_group->next) - for (row = row_group->children; row; row = row->next) - for (cell = row->children; cell; cell = cell->next) { - unsigned int fixed_columns = 0, percent_columns = 0, - auto_columns = 0, unknown_columns = 0; - int fixed_width = 0, percent_width = 0; - enum css_width_e type; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - if (cell->columns == 1) - continue; - i = cell->start_column; - - for (j = i; j < i + cell->columns; j++) { - col[j].positioned = false; - } + 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 */ + default: + break; + } - /* count column types in spanned cells */ - for (j = 0; j != cell->columns; j++) { - if (col[i + j].type == COLUMN_WIDTH_FIXED) { - fixed_width += col[i + j].width; - fixed_columns++; - } else if (col[i + j].type == COLUMN_WIDTH_PERCENT) { - percent_width += col[i + j].width; - percent_columns++; - } else if (col[i + j].type == COLUMN_WIDTH_AUTO) { - auto_columns++; - } else { - unknown_columns++; - } - } + if (impact < 0) + return true; + else if (impact > 0) + return false; - if (!unknown_columns) - continue; - - type = css_computed_width(cell->style, &value, &unit); - - /* if cell is fixed width, and all spanned columns are fixed - * or unknown width, split extra width among unknown columns */ - if (type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT && - fixed_columns + unknown_columns == - cell->columns) { - int width = (FIXTOFLT(nscss_len2px(len_ctx, value, unit, - cell->style)) - fixed_width) / - unknown_columns; - if (width < 0) - width = 0; - for (j = 0; j != cell->columns; j++) { - if (col[i + j].type == COLUMN_WIDTH_UNKNOWN) { - col[i + j].type = COLUMN_WIDTH_FIXED; - col[i + j].width = width; - } - } - } + /* 4a -- sort by origin */ + impact = 0; - /* as above for percentage width */ - if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT && - percent_columns + unknown_columns == - cell->columns) { - int width = (FIXTOFLT(value) - - percent_width) / unknown_columns; - if (width < 0) - width = 0; - for (j = 0; j != cell->columns; j++) { - if (col[i + j].type == COLUMN_WIDTH_UNKNOWN) { - col[i + j].type = COLUMN_WIDTH_PERCENT; - col[i + j].width = width; - } - } - } + /** \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 */ + default: + break; } - /* use AUTO if no width type was specified */ - for (i = 0; i != table->columns; i++) { - if (col[i].type == COLUMN_WIDTH_UNKNOWN) - col[i].type = COLUMN_WIDTH_AUTO; + /** \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 */ + default: + break; } -#ifdef TABLE_DEBUG - for (i = 0; i != table->columns; i++) - NSLOG(netsurf, INFO, - "table %p, column %u: type %s, width %i", table, i, ((const char *[]){ - "UNKNOWN", - "FIXED", - "AUTO", - "PERCENT", - "RELATIVE", - })[col[i].type], col[i].width); -#endif + if (impact < 0) + return true; + else if (impact > 0) + return false; + /* 4b -- furthest left (if direction: ltr) and towards top wins */ + /** \todo Currently assumes b satisifies this */ return true; } + /** - * Calculate used values of border-{trbl}-{style,color,width} for table cells. + * Process a table * * \param len_ctx Length conversion context - * \param cell Table cell to consider + * \param table Table to process + * \param a Current border style for cell + * \param a_src Source of \a a * - * \post \a cell's border array is populated + * \post \a a will be updated with most eyecatching style + * \post \a a_src will be updated also */ -void table_used_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell) +static void +table_cell_top_process_table(const nscss_len_ctx *len_ctx, + struct box *table, + struct border *a, + box_type *a_src) { - int side; - - assert(cell->type == BOX_TABLE_CELL); - - if (css_computed_border_collapse(cell->style) == - CSS_BORDER_COLLAPSE_SEPARATE) { - css_fixed width = 0; - css_unit unit = CSS_UNIT_PX; - - /* Left border */ - cell->border[LEFT].style = - css_computed_border_left_style(cell->style); - css_computed_border_left_color(cell->style, - &cell->border[LEFT].c); - css_computed_border_left_width(cell->style, &width, &unit); - cell->border[LEFT].width = - FIXTOINT(nscss_len2px(len_ctx, - width, unit, cell->style)); - - /* Top border */ - cell->border[TOP].style = - css_computed_border_top_style(cell->style); - css_computed_border_top_color(cell->style, - &cell->border[TOP].c); - css_computed_border_top_width(cell->style, &width, &unit); - cell->border[TOP].width = - FIXTOINT(nscss_len2px(len_ctx, - width, unit, cell->style)); - - /* Right border */ - cell->border[RIGHT].style = - css_computed_border_right_style(cell->style); - css_computed_border_right_color(cell->style, - &cell->border[RIGHT].c); - css_computed_border_right_width(cell->style, &width, &unit); - cell->border[RIGHT].width = - FIXTOINT(nscss_len2px(len_ctx, - width, unit, cell->style)); - - /* Bottom border */ - cell->border[BOTTOM].style = - css_computed_border_bottom_style(cell->style); - css_computed_border_bottom_color(cell->style, - &cell->border[BOTTOM].c); - css_computed_border_bottom_width(cell->style, &width, &unit); - cell->border[BOTTOM].width = - FIXTOINT(nscss_len2px(len_ctx, - width, unit, cell->style)); - } else { - /* Left border */ - table_used_left_border_for_cell(len_ctx, cell); - - /* Top border */ - table_used_top_border_for_cell(len_ctx, cell); - - /* Right border */ - table_used_right_border_for_cell(len_ctx, cell); + struct border b; + box_type b_src; - /* Bottom border */ - table_used_bottom_border_for_cell(len_ctx, cell); - } + /* Top border of table */ + b.style = css_computed_border_top_style(table->style); + b.color = css_computed_border_top_color(table->style, &b.c); + css_computed_border_top_width(table->style, &b.width, &b.unit); + b.width = nscss_len2px(len_ctx, b.width, b.unit, table->style); + b.unit = CSS_UNIT_PX; + b_src = BOX_TABLE; - /* Finally, ensure that any borders configured as - * hidden or none have zero width. (c.f. layout_find_dimensions) */ - for (side = 0; side != 4; side++) { - if (cell->border[side].style == CSS_BORDER_STYLE_HIDDEN || - cell->border[side].style == - CSS_BORDER_STYLE_NONE) - cell->border[side].width = 0; + if (table_border_is_more_eyecatching(len_ctx, a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; } } -/****************************************************************************** - * Helpers for used border calculations * - ******************************************************************************/ /** - * Calculate used values of border-left-{style,color,width} + * Process a row * * \param len_ctx Length conversion context - * \param cell Table cell to consider + * \param cell Cell being considered + * \param row Row to process + * \param a Current border style for cell + * \param a_src Source of \a a + * \return true if row has cells, false otherwise + * + * \post \a a will be updated with most eyecatching style + * \post \a a_src will be updated also */ -void table_used_left_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell) +static bool +table_cell_top_process_row(const nscss_len_ctx *len_ctx, + struct box *cell, + struct box *row, + struct border *a, + box_type *a_src) { - struct border a, b; - box_type a_src, b_src; + struct border b; + box_type b_src; - /** \todo Need column and column_group, too */ + /* Bottom border of row */ + b.style = css_computed_border_bottom_style(row->style); + b.color = css_computed_border_bottom_color(row->style, &b.c); + css_computed_border_bottom_width(row->style, &b.width, &b.unit); + b.width = nscss_len2px(len_ctx, b.width, b.unit, row->style); + b.unit = CSS_UNIT_PX; + b_src = BOX_TABLE_ROW; - /* Initialise to computed left border for cell */ - a.style = css_computed_border_left_style(cell->style); - a.color = css_computed_border_left_color(cell->style, &a.c); - css_computed_border_left_width(cell->style, &a.width, &a.unit); - a.width = nscss_len2px(len_ctx, a.width, a.unit, cell->style); - a.unit = CSS_UNIT_PX; - a_src = BOX_TABLE_CELL; + if (table_border_is_more_eyecatching(len_ctx, a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; + } - if (cell->prev != NULL || cell->start_column != 0) { - /* Cell to the left -- consider its right border */ - struct box *prev = NULL; + if (row->children == NULL) { + /* Row is empty, so consider its top border */ + b.style = css_computed_border_top_style(row->style); + b.color = css_computed_border_top_color(row->style, &b.c); + css_computed_border_top_width(row->style, &b.width, &b.unit); + b.width = nscss_len2px(len_ctx, b.width, b.unit, row->style); + b.unit = CSS_UNIT_PX; + b_src = BOX_TABLE_ROW; - if (cell->prev == NULL) { - struct box *row; + if (table_border_is_more_eyecatching(len_ctx, + a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; + } - /* Spanned from a previous row in current row group */ - for (row = cell->parent; row != NULL; row = row->prev) { - for (prev = row->children; prev != NULL; - prev = prev->next) { - if (prev->start_column + - prev->columns == - cell->start_column) - break; - } + return false; + } else { + /* Process cells that are directly above the cell being + * considered. They may not be in this row, but in one of the + * rows above it in the case where rowspan > 1. */ + struct box *c; + bool processed = false; + + while (processed == false) { + for (c = row->children; c != NULL; c = c->next) { + /* Ignore cells to the left */ + if (c->start_column + c->columns - 1 < + cell->start_column) + continue; + /* Ignore cells to the right */ + if (c->start_column > cell->start_column + + cell->columns - 1) + continue; + + /* Flag that we've processed a cell */ + processed = true; + + /* Consider bottom border */ + b.style = css_computed_border_bottom_style( + c->style); + b.color = css_computed_border_bottom_color( + c->style, &b.c); + css_computed_border_bottom_width(c->style, + &b.width, &b.unit); + b.width = nscss_len2px(len_ctx, + b.width, + b.unit, + c->style); + b.unit = CSS_UNIT_PX; + b_src = BOX_TABLE_CELL; + + if (table_border_is_more_eyecatching(len_ctx, + a, + *a_src, + &b, + b_src)) { + *a = b; + *a_src = b_src; + } + } + + if (processed == false) { + /* There must be a preceding row */ + assert(row->prev != NULL); + + row = row->prev; + } + } + } + + return true; +} + + +/** + * Process a group + * + * \param len_ctx Length conversion context + * \param cell Cell being considered + * \param group Group to process + * \param a Current border style for cell + * \param a_src Source of \a a + * \return true if group has non-empty rows, false otherwise + * + * \post \a a will be updated with most eyecatching style + * \post \a a_src will be updated also + */ +static bool +table_cell_top_process_group(const nscss_len_ctx *len_ctx, + struct box *cell, + struct box *group, + struct border *a, + box_type *a_src) +{ + struct border b; + box_type b_src; + + /* Bottom border of group */ + b.style = css_computed_border_bottom_style(group->style); + b.color = css_computed_border_bottom_color(group->style, &b.c); + css_computed_border_bottom_width(group->style, &b.width, &b.unit); + b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); + b.unit = CSS_UNIT_PX; + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(len_ctx, a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; + } + + if (group->last != NULL) { + /* Process rows in group, starting with last */ + struct box *row = group->last; + + while (table_cell_top_process_row(len_ctx, cell, row, + a, a_src) == false) { + if (row->prev == NULL) { + return false; + } else { + row = row->prev; + } + } + } else { + /* Group is empty, so consider its top border */ + b.style = css_computed_border_top_style(group->style); + b.color = css_computed_border_top_color(group->style, &b.c); + css_computed_border_top_width(group->style, &b.width, &b.unit); + b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); + b.unit = CSS_UNIT_PX; + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(len_ctx, + a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; + } + + return false; + } + + return true; +} + + +/** + * Calculate used values of border-left-{style,color,width} + * + * \param len_ctx Length conversion context + * \param cell Table cell to consider + */ +static void +table_used_left_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) +{ + struct border a, b; + box_type a_src, b_src; + + /** \todo Need column and column_group, too */ + + /* Initialise to computed left border for cell */ + a.style = css_computed_border_left_style(cell->style); + a.color = css_computed_border_left_color(cell->style, &a.c); + css_computed_border_left_width(cell->style, &a.width, &a.unit); + a.width = nscss_len2px(len_ctx, a.width, a.unit, cell->style); + a.unit = CSS_UNIT_PX; + a_src = BOX_TABLE_CELL; + + if (cell->prev != NULL || cell->start_column != 0) { + /* Cell to the left -- consider its right border */ + struct box *prev = NULL; + + if (cell->prev == NULL) { + struct box *row; + + /* Spanned from a previous row in current row group */ + for (row = cell->parent; row != NULL; row = row->prev) { + for (prev = row->children; prev != NULL; + prev = prev->next) { + if (prev->start_column + + prev->columns == + cell->start_column) + break; + } if (prev != NULL) break; @@ -404,7 +428,7 @@ void table_used_left_border_for_cell( b_src = BOX_TABLE_CELL; if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { + &a, a_src, &b, b_src)) { a = b; a_src = b_src; } @@ -419,16 +443,16 @@ void table_used_left_border_for_cell( /* Spanned rows -- consider their left border */ b.style = css_computed_border_left_style(row->style); b.color = css_computed_border_left_color( - row->style, &b.c); + row->style, &b.c); css_computed_border_left_width( - row->style, &b.width, &b.unit); + row->style, &b.width, &b.unit); b.width = nscss_len2px(len_ctx, - b.width, b.unit, row->style); + b.width, b.unit, row->style); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW; if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { + &a, a_src, &b, b_src)) { a = b; a_src = b_src; } @@ -447,7 +471,7 @@ void table_used_left_border_for_cell( b_src = BOX_TABLE_ROW_GROUP; if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { + &a, a_src, &b, b_src)) { a = b; a_src = b_src; } @@ -461,7 +485,7 @@ void table_used_left_border_for_cell( b_src = BOX_TABLE; if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { + &a, a_src, &b, b_src)) { a = b; a_src = b_src; } @@ -471,18 +495,20 @@ void table_used_left_border_for_cell( cell->border[LEFT].style = a.style; cell->border[LEFT].c = a.c; cell->border[LEFT].width = FIXTOINT(nscss_len2px(len_ctx, - a.width, a.unit, cell->style)); + a.width, + a.unit, + cell->style)); } + /** * Calculate used values of border-top-{style,color,width} * * \param len_ctx Length conversion context * \param cell Table cell to consider */ -void table_used_top_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell) +static void +table_used_top_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) { struct border a, b; box_type a_src, b_src; @@ -513,7 +539,7 @@ void table_used_top_border_for_cell( if (row->prev != NULL) { /* Consider row(s) above */ while (table_cell_top_process_row(len_ctx, cell, row->prev, - &a, &a_src) == false) { + &a, &a_src) == false) { if (row->prev->prev == NULL) { /* Consider row group */ process_group = true; @@ -538,7 +564,7 @@ void table_used_top_border_for_cell( b_src = BOX_TABLE_ROW_GROUP; if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { + &a, a_src, &b, b_src)) { a = b; a_src = b_src; } @@ -546,17 +572,17 @@ void table_used_top_border_for_cell( if (group->prev == NULL) { /* Top border of table */ table_cell_top_process_table(len_ctx, - group->parent, &a, &a_src); + group->parent, &a, &a_src); } else { /* Process previous group(s) */ while (table_cell_top_process_group(len_ctx, - cell, group->prev, - &a, &a_src) == false) { + cell, group->prev, + &a, &a_src) == false) { if (group->prev->prev == NULL) { /* Top border of table */ table_cell_top_process_table(len_ctx, - group->parent, - &a, &a_src); + group->parent, + &a, &a_src); break; } else { group = group->prev; @@ -569,7 +595,9 @@ void table_used_top_border_for_cell( cell->border[TOP].style = a.style; cell->border[TOP].c = a.c; cell->border[TOP].width = FIXTOINT(nscss_len2px(len_ctx, - a.width, a.unit, cell->style)); + a.width, + a.unit, + cell->style)); } /** @@ -578,9 +606,8 @@ void table_used_top_border_for_cell( * \param len_ctx Length conversion context * \param cell Table cell to consider */ -void table_used_right_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell) +static void +table_used_right_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) { struct border a, b; box_type a_src, b_src; @@ -596,7 +623,7 @@ void table_used_right_border_for_cell( a_src = BOX_TABLE_CELL; if (cell->next != NULL || cell->start_column + cell->columns != - cell->parent->parent->parent->columns) { + cell->parent->parent->parent->columns) { /* Cell is not at right edge of table -- no right border */ a.style = CSS_BORDER_STYLE_NONE; a.width = 0; @@ -611,17 +638,21 @@ void table_used_right_border_for_cell( while (rows-- > 0 && row != NULL) { /* Spanned rows -- consider their right border */ b.style = css_computed_border_right_style(row->style); - b.color = css_computed_border_right_color( - row->style, &b.c); - css_computed_border_right_width( - row->style, &b.width, &b.unit); + b.color = css_computed_border_right_color(row->style, + &b.c); + css_computed_border_right_width(row->style, + &b.width, + &b.unit); b.width = nscss_len2px(len_ctx, - b.width, b.unit, row->style); + b.width, + b.unit, + row->style); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW; if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { + &a, a_src, + &b, b_src)) { a = b; a_src = b_src; } @@ -635,13 +666,13 @@ void table_used_right_border_for_cell( b.style = css_computed_border_right_style(group->style); b.color = css_computed_border_right_color(group->style, &b.c); css_computed_border_right_width(group->style, - &b.width, &b.unit); + &b.width, &b.unit); b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW_GROUP; if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { + &a, a_src, &b, b_src)) { a = b; a_src = b_src; } @@ -650,13 +681,14 @@ void table_used_right_border_for_cell( b.style = css_computed_border_right_style(table->style); b.color = css_computed_border_right_color(table->style, &b.c); css_computed_border_right_width(table->style, - &b.width, &b.unit); + &b.width, &b.unit); b.width = nscss_len2px(len_ctx, b.width, b.unit, table->style); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE; if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { + &a, a_src, + &b, b_src)) { a = b; a_src = b_src; } @@ -666,18 +698,21 @@ void table_used_right_border_for_cell( cell->border[RIGHT].style = a.style; cell->border[RIGHT].c = a.c; cell->border[RIGHT].width = FIXTOINT(nscss_len2px(len_ctx, - a.width, a.unit, cell->style)); + a.width, + a.unit, + cell->style)); } + /** * Calculate used values of border-bottom-{style,color,width} * * \param len_ctx Length conversion context * \param cell Table cell to consider */ -void table_used_bottom_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell) +static void +table_used_bottom_border_for_cell(const nscss_len_ctx *len_ctx, + struct box *cell) { struct border a, b; box_type a_src, b_src; @@ -717,7 +752,7 @@ void table_used_bottom_border_for_cell( b_src = BOX_TABLE_ROW; if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { + &a, a_src, &b, b_src)) { a = b; a_src = b_src; } @@ -726,13 +761,13 @@ void table_used_bottom_border_for_cell( b.style = css_computed_border_bottom_style(group->style); b.color = css_computed_border_bottom_color(group->style, &b.c); css_computed_border_bottom_width(group->style, - &b.width, &b.unit); + &b.width, &b.unit); b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE_ROW_GROUP; if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { + &a, a_src, &b, b_src)) { a = b; a_src = b_src; } @@ -741,13 +776,13 @@ void table_used_bottom_border_for_cell( b.style = css_computed_border_bottom_style(table->style); b.color = css_computed_border_bottom_color(table->style, &b.c); css_computed_border_bottom_width(table->style, - &b.width, &b.unit); + &b.width, &b.unit); b.width = nscss_len2px(len_ctx, b.width, b.unit, table->style); b.unit = CSS_UNIT_PX; b_src = BOX_TABLE; if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { + &a, a_src, &b, b_src)) { a = b; } } @@ -756,325 +791,253 @@ void table_used_bottom_border_for_cell( cell->border[BOTTOM].style = a.style; cell->border[BOTTOM].c = a.c; cell->border[BOTTOM].width = FIXTOINT(nscss_len2px(len_ctx, - a.width, a.unit, cell->style)); + a.width, a.unit, cell->style)); } -/** - * Determine if a border style is more eyecatching than another - * - * \param len_ctx Length conversion context - * \param a Reference border style - * \param a_src Source of \a a - * \param b Candidate border style - * \param b_src Source of \a b - * \return True if \a b is more eyecatching than \a a - */ -bool table_border_is_more_eyecatching( - const nscss_len_ctx *len_ctx, - const struct border *a, - box_type a_src, - const struct border *b, - box_type b_src) -{ - css_fixed awidth, bwidth; - int impact = 0; - /* See CSS 2.1 $17.6.2.1 */ - - /* 1 + 2 -- hidden beats everything, none beats nothing */ - if (a->style == CSS_BORDER_STYLE_HIDDEN || - b->style == CSS_BORDER_STYLE_NONE) - return false; +/* exported interface documented in html/table.h */ +bool +table_calculate_column_types(const nscss_len_ctx *len_ctx, struct box *table) +{ + unsigned int i, j; + struct column *col; + struct box *row_group, *row, *cell; - if (b->style == CSS_BORDER_STYLE_HIDDEN || - a->style == CSS_BORDER_STYLE_NONE) + if (table->col) + /* table->col already constructed, for example frameset table */ return true; - /* 3a -- wider borders beat narrow ones */ - /* The widths must be absolute, which will be the case - * if they've come from a computed style. */ - assert(a->unit != CSS_UNIT_EM && a->unit != CSS_UNIT_EX); - assert(b->unit != CSS_UNIT_EM && b->unit != CSS_UNIT_EX); - awidth = nscss_len2px(len_ctx, a->width, a->unit, NULL); - bwidth = nscss_len2px(len_ctx, b->width, b->unit, NULL); - - if (awidth < bwidth) - return true; - else if (bwidth < awidth) + table->col = col = talloc_array(table, struct column, table->columns); + if (!col) return false; - /* 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 */ - 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 */ - default: - break; + for (i = 0; i != table->columns; i++) { + col[i].type = COLUMN_WIDTH_UNKNOWN; + col[i].width = 0; + col[i].positioned = true; } - if (impact < 0) - return true; - else if (impact > 0) - return false; - - /* 4a -- sort by origin */ - impact = 0; + /* 1st pass: cells with colspan 1 only */ + for (row_group = table->children; row_group; row_group =row_group->next) + for (row = row_group->children; row; row = row->next) + for (cell = row->children; cell; cell = cell->next) { + enum css_width_e type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; - /** \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 */ - default: - break; - } + assert(cell->type == BOX_TABLE_CELL); + assert(cell->style); - /** \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 */ - default: - break; - } + if (cell->columns != 1) + continue; + i = cell->start_column; - if (impact < 0) - return true; - else if (impact > 0) - return false; + if (css_computed_position(cell->style) != + CSS_POSITION_ABSOLUTE && + css_computed_position(cell->style) != + CSS_POSITION_FIXED) { + col[i].positioned = false; + } - /* 4b -- furthest left (if direction: ltr) and towards top wins */ - /** \todo Currently assumes b satisifies this */ - return true; -} + type = css_computed_width(cell->style, &value, &unit); -/****************************************************************************** - * Helpers for top border collapsing * - ******************************************************************************/ + /* fixed width takes priority over any other width type */ + if (col[i].type != COLUMN_WIDTH_FIXED && + type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT) { + col[i].type = COLUMN_WIDTH_FIXED; + col[i].width = FIXTOINT(nscss_len2px(len_ctx, + value, unit, cell->style)); + if (col[i].width < 0) + col[i].width = 0; + continue; + } -/** - * Process a table - * - * \param len_ctx Length conversion context - * \param table Table to process - * \param a Current border style for cell - * \param a_src Source of \a a - * - * \post \a a will be updated with most eyecatching style - * \post \a a_src will be updated also - */ -void table_cell_top_process_table( - const nscss_len_ctx *len_ctx, - struct box *table, - struct border *a, - box_type *a_src) -{ - struct border b; - box_type b_src; + if (col[i].type != COLUMN_WIDTH_UNKNOWN) + continue; - /* Top border of table */ - b.style = css_computed_border_top_style(table->style); - b.color = css_computed_border_top_color(table->style, &b.c); - css_computed_border_top_width(table->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, table->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE; + if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT) { + col[i].type = COLUMN_WIDTH_PERCENT; + col[i].width = FIXTOINT(value); + if (col[i].width < 0) + col[i].width = 0; + } else if (type == CSS_WIDTH_AUTO) { + col[i].type = COLUMN_WIDTH_AUTO; + } + } - if (table_border_is_more_eyecatching(len_ctx, a, *a_src, &b, b_src)) { - *a = b; - *a_src = b_src; - } -} + /* 2nd pass: cells which span multiple columns */ + for (row_group = table->children; row_group; row_group =row_group->next) + for (row = row_group->children; row; row = row->next) + for (cell = row->children; cell; cell = cell->next) { + unsigned int fixed_columns = 0, + percent_columns = 0, + auto_columns = 0, + unknown_columns = 0; + int fixed_width = 0, percent_width = 0; + enum css_width_e type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + if (cell->columns == 1) + continue; + i = cell->start_column; -/** - * Process a group - * - * \param len_ctx Length conversion context - * \param cell Cell being considered - * \param group Group to process - * \param a Current border style for cell - * \param a_src Source of \a a - * \return true if group has non-empty rows, false otherwise - * - * \post \a a will be updated with most eyecatching style - * \post \a a_src will be updated also - */ -bool table_cell_top_process_group( - const nscss_len_ctx *len_ctx, - struct box *cell, - struct box *group, - struct border *a, - box_type *a_src) -{ - struct border b; - box_type b_src; + for (j = i; j < i + cell->columns; j++) { + col[j].positioned = false; + } - /* Bottom border of group */ - b.style = css_computed_border_bottom_style(group->style); - b.color = css_computed_border_bottom_color(group->style, &b.c); - css_computed_border_bottom_width(group->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW_GROUP; + /* count column types in spanned cells */ + for (j = 0; j != cell->columns; j++) { + if (col[i + j].type == COLUMN_WIDTH_FIXED) { + fixed_width += col[i + j].width; + fixed_columns++; + } else if (col[i + j].type == COLUMN_WIDTH_PERCENT) { + percent_width += col[i + j].width; + percent_columns++; + } else if (col[i + j].type == COLUMN_WIDTH_AUTO) { + auto_columns++; + } else { + unknown_columns++; + } + } - if (table_border_is_more_eyecatching(len_ctx, a, *a_src, &b, b_src)) { - *a = b; - *a_src = b_src; - } + if (!unknown_columns) + continue; - if (group->last != NULL) { - /* Process rows in group, starting with last */ - struct box *row = group->last; + type = css_computed_width(cell->style, &value, &unit); + + /* if cell is fixed width, and all spanned columns are fixed + * or unknown width, split extra width among unknown columns */ + if (type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT && + fixed_columns + unknown_columns == + cell->columns) { + int width = (FIXTOFLT(nscss_len2px(len_ctx, value, unit, + cell->style)) - fixed_width) / + unknown_columns; + if (width < 0) + width = 0; + for (j = 0; j != cell->columns; j++) { + if (col[i + j].type == COLUMN_WIDTH_UNKNOWN) { + col[i + j].type = COLUMN_WIDTH_FIXED; + col[i + j].width = width; + } + } + } - while (table_cell_top_process_row(len_ctx, cell, row, - a, a_src) == false) { - if (row->prev == NULL) { - return false; - } else { - row = row->prev; + /* as above for percentage width */ + if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT && + percent_columns + unknown_columns == + cell->columns) { + int width = (FIXTOFLT(value) - + percent_width) / unknown_columns; + if (width < 0) + width = 0; + for (j = 0; j != cell->columns; j++) { + if (col[i + j].type == COLUMN_WIDTH_UNKNOWN) { + col[i + j].type = COLUMN_WIDTH_PERCENT; + col[i + j].width = width; + } + } + } } - } - } else { - /* Group is empty, so consider its top border */ - b.style = css_computed_border_top_style(group->style); - b.color = css_computed_border_top_color(group->style, &b.c); - css_computed_border_top_width(group->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW_GROUP; - if (table_border_is_more_eyecatching(len_ctx, - a, *a_src, &b, b_src)) { - *a = b; - *a_src = b_src; - } - - return false; + /* use AUTO if no width type was specified */ + for (i = 0; i != table->columns; i++) { + if (col[i].type == COLUMN_WIDTH_UNKNOWN) + col[i].type = COLUMN_WIDTH_AUTO; } +#ifdef TABLE_DEBUG + for (i = 0; i != table->columns; i++) + NSLOG(netsurf, INFO, + "table %p, column %u: type %s, width %i", + table, + i, + ((const char *[]){ + "UNKNOWN", + "FIXED", + "AUTO", + "PERCENT", + "RELATIVE", + })[col[i].type], + col[i].width); +#endif + return true; } -/** - * Process a row - * - * \param len_ctx Length conversion context - * \param cell Cell being considered - * \param row Row to process - * \param a Current border style for cell - * \param a_src Source of \a a - * \return true if row has cells, false otherwise - * - * \post \a a will be updated with most eyecatching style - * \post \a a_src will be updated also - */ -bool table_cell_top_process_row( - const nscss_len_ctx *len_ctx, - struct box *cell, - struct box *row, - struct border *a, - box_type *a_src) -{ - struct border b; - box_type b_src; - - /* Bottom border of row */ - b.style = css_computed_border_bottom_style(row->style); - b.color = css_computed_border_bottom_color(row->style, &b.c); - css_computed_border_bottom_width(row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, row->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW; - if (table_border_is_more_eyecatching(len_ctx, a, *a_src, &b, b_src)) { - *a = b; - *a_src = b_src; - } +/* exported interface documented in html/table.h */ +void table_used_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell) +{ + int side; - if (row->children == NULL) { - /* Row is empty, so consider its top border */ - b.style = css_computed_border_top_style(row->style); - b.color = css_computed_border_top_color(row->style, &b.c); - css_computed_border_top_width(row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, row->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW; + assert(cell->type == BOX_TABLE_CELL); - if (table_border_is_more_eyecatching(len_ctx, - a, *a_src, &b, b_src)) { - *a = b; - *a_src = b_src; - } + if (css_computed_border_collapse(cell->style) == + CSS_BORDER_COLLAPSE_SEPARATE) { + css_fixed width = 0; + css_unit unit = CSS_UNIT_PX; - return false; - } else { - /* Process cells that are directly above the cell being - * considered. They may not be in this row, but in one of the - * rows above it in the case where rowspan > 1. */ - struct box *c; - bool processed = false; + /* Left border */ + cell->border[LEFT].style = + css_computed_border_left_style(cell->style); + css_computed_border_left_color(cell->style, + &cell->border[LEFT].c); + css_computed_border_left_width(cell->style, &width, &unit); + cell->border[LEFT].width = + FIXTOINT(nscss_len2px(len_ctx, + width, unit, cell->style)); - while (processed == false) { - for (c = row->children; c != NULL; c = c->next) { - /* Ignore cells to the left */ - if (c->start_column + c->columns - 1 < - cell->start_column) - continue; - /* Ignore cells to the right */ - if (c->start_column > cell->start_column + - cell->columns - 1) - continue; + /* Top border */ + cell->border[TOP].style = + css_computed_border_top_style(cell->style); + css_computed_border_top_color(cell->style, + &cell->border[TOP].c); + css_computed_border_top_width(cell->style, &width, &unit); + cell->border[TOP].width = + FIXTOINT(nscss_len2px(len_ctx, + width, unit, cell->style)); - /* Flag that we've processed a cell */ - processed = true; + /* Right border */ + cell->border[RIGHT].style = + css_computed_border_right_style(cell->style); + css_computed_border_right_color(cell->style, + &cell->border[RIGHT].c); + css_computed_border_right_width(cell->style, &width, &unit); + cell->border[RIGHT].width = + FIXTOINT(nscss_len2px(len_ctx, + width, unit, cell->style)); - /* Consider bottom border */ - b.style = css_computed_border_bottom_style( - c->style); - b.color = css_computed_border_bottom_color( - c->style, &b.c); - css_computed_border_bottom_width(c->style, - &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, - b.width, b.unit, c->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_CELL; + /* Bottom border */ + cell->border[BOTTOM].style = + css_computed_border_bottom_style(cell->style); + css_computed_border_bottom_color(cell->style, + &cell->border[BOTTOM].c); + css_computed_border_bottom_width(cell->style, &width, &unit); + cell->border[BOTTOM].width = + FIXTOINT(nscss_len2px(len_ctx, + width, unit, cell->style)); + } else { + /* Left border */ + table_used_left_border_for_cell(len_ctx, cell); - if (table_border_is_more_eyecatching(len_ctx, - a, *a_src, &b, b_src)) { - *a = b; - *a_src = b_src; - } - } + /* Top border */ + table_used_top_border_for_cell(len_ctx, cell); - if (processed == false) { - /* There must be a preceding row */ - assert(row->prev != NULL); + /* Right border */ + table_used_right_border_for_cell(len_ctx, cell); - row = row->prev; - } - } + /* Bottom border */ + table_used_bottom_border_for_cell(len_ctx, cell); } - return true; + /* Finally, ensure that any borders configured as + * hidden or none have zero width. (c.f. layout_find_dimensions) */ + for (side = 0; side != 4; side++) { + if (cell->border[side].style == CSS_BORDER_STYLE_HIDDEN || + cell->border[side].style == + CSS_BORDER_STYLE_NONE) + cell->border[side].width = 0; + } } diff --git a/content/handlers/html/table.h b/content/handlers/html/table.h index 11ab653c6..ac4af25ad 100644 --- a/content/handlers/html/table.h +++ b/content/handlers/html/table.h @@ -29,11 +29,28 @@ struct box; -bool table_calculate_column_types( - const nscss_len_ctx *len_ctx, - struct box *table); -void table_used_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell); + +/** + * Determine the column width types for a table. + * + * \param len_ctx Length conversion context + * \param table box of type BOX_TABLE + * \return true on success, false on memory exhaustion + * + * The table->col array is allocated and type and width are filled in for each + * column. + */ +bool table_calculate_column_types(const nscss_len_ctx *len_ctx, struct box *table); + + +/** + * Calculate used values of border-{trbl}-{style,color,width} for table cells. + * + * \param len_ctx Length conversion context + * \param cell Table cell to consider + * + * \post \a cell's border array is populated + */ +void table_used_border_for_cell(const nscss_len_ctx *len_ctx, struct box *cell); #endif -- cgit v1.2.3