summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--content/handlers/html/table.c1113
-rw-r--r--content/handlers/html/table.h29
2 files changed, 561 insertions, 581 deletions
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,306 +46,331 @@ 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 */
+ /* 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;
+
+ if (b->style == CSS_BORDER_STYLE_HIDDEN ||
+ a->style == CSS_BORDER_STYLE_NONE)
return true;
- table->col = col = talloc_array(table, struct column, table->columns);
- if (!col)
+ /* 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)
return false;
- for (i = 0; i != table->columns; i++) {
- col[i].type = COLUMN_WIDTH_UNKNOWN;
- col[i].width = 0;
- col[i].positioned = true;
+ /* 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;
}
- /* 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;
+ 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;
+ }
- assert(cell->type == BOX_TABLE_CELL);
- assert(cell->style);
+ if (impact < 0)
+ return true;
+ else if (impact > 0)
+ return false;
- if (cell->columns != 1)
- continue;
- i = cell->start_column;
+ /* 4a -- sort by origin */
+ impact = 0;
- if (css_computed_position(cell->style) !=
- CSS_POSITION_ABSOLUTE &&
- css_computed_position(cell->style) !=
- CSS_POSITION_FIXED) {
- col[i].positioned = false;
- }
+ /** \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;
+ }
- 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;
- }
+ /** \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 (col[i].type != COLUMN_WIDTH_UNKNOWN)
- continue;
+ if (impact < 0)
+ return true;
+ else if (impact > 0)
+ 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;
- }
+ /* 4b -- furthest left (if direction: ltr) and towards top wins */
+ /** \todo Currently assumes b satisifies this */
+ return true;
+}
+
+
+/**
+ * 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
+ */
+static 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;
+
+ /* 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 (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 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
+ */
+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 b;
+ box_type b_src;
- for (j = i; j < i + cell->columns; j++) {
- col[j].positioned = false;
- }
+ /* 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;
- /* 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 (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 (table_border_is_more_eyecatching(len_ctx,
+ a, *a_src, &b, b_src)) {
+ *a = b;
+ *a_src = b_src;
}
- 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;
+ 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;
}
}
- }
- /* 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;
- }
+ if (processed == false) {
+ /* There must be a preceding row */
+ assert(row->prev != NULL);
+
+ row = row->prev;
}
}
}
- /* 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;
}
+
/**
- * Calculate used values of border-{trbl}-{style,color,width} for table cells.
+ * Process a group
*
* \param len_ctx Length conversion context
- * \param cell Table cell to consider
+ * \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 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 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)
{
- 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;
+ struct border b;
+ box_type b_src;
- /* 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));
+ /* 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;
- /* 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));
+ if (table_border_is_more_eyecatching(len_ctx, a, *a_src, &b, b_src)) {
+ *a = b;
+ *a_src = b_src;
+ }
- /* 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));
+ if (group->last != NULL) {
+ /* Process rows in group, starting with last */
+ struct box *row = group->last;
- /* 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));
+ 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 {
- /* Left border */
- table_used_left_border_for_cell(len_ctx, cell);
-
- /* Top border */
- table_used_top_border_for_cell(len_ctx, cell);
+ /* 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;
- /* Right border */
- table_used_right_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;
+ }
- /* Bottom border */
- table_used_bottom_border_for_cell(len_ctx, cell);
+ return false;
}
- /* 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;
- }
+ return true;
}
-/******************************************************************************
- * Helpers for used border calculations *
- ******************************************************************************/
/**
* Calculate used values of border-left-{style,color,width}
@@ -353,9 +378,8 @@ void table_used_border_for_cell(
* \param len_ctx Length conversion context
* \param cell Table cell to consider
*/
-void table_used_left_border_for_cell(
- const nscss_len_ctx *len_ctx,
- struct box *cell)
+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;
@@ -380,10 +404,10 @@ void table_used_left_border_for_cell(
/* 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) {
+ prev = prev->next) {
if (prev->start_column +
- prev->columns ==
- cell->start_column)
+ prev->columns ==
+ cell->start_column)
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