LibWeb: Implement table rowspan
Adjust computing the table height and positioning of cells to account for the rowspan property. Fixes #18952.
This commit is contained in:
parent
6cb9d755d9
commit
e6221117a5
Notes:
sideshowbarker
2024-07-17 06:33:00 +09:00
Author: https://github.com/axgallo Commit: https://github.com/SerenityOS/serenity/commit/e6221117a5 Pull-request: https://github.com/SerenityOS/serenity/pull/18953 Issue: https://github.com/SerenityOS/serenity/issues/18952 Reviewed-by: https://github.com/kalenikaliaksandr ✅
4 changed files with 178 additions and 2 deletions
111
Tests/LibWeb/Layout/expected/table/rowspan.txt
Normal file
111
Tests/LibWeb/Layout/expected/table/rowspan.txt
Normal file
|
@ -0,0 +1,111 @@
|
|||
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||
BlockContainer <html> at (0,0) content-size 800x93.875 [BFC] children: not-inline
|
||||
BlockContainer <(anonymous)> at (0,0) content-size 800x0 children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <body> at (8,8) content-size 784x77.875 children: not-inline
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 784x0 children: inline
|
||||
TextNode <#text>
|
||||
TableWrapper <(anonymous)> at (8,8) content-size 221.359375x77.875 [BFC] children: not-inline
|
||||
TableBox <table> at (8,8) content-size 221.359375x77.875 [TFC] children: not-inline
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableRowGroupBox <tbody> at (8,8) content-size 221.359375x77.875 children: not-inline
|
||||
TableRowBox <tr> at (8,8) content-size 221.359375x19.46875 children: not-inline
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableCellBox <th> at (9,9) content-size 70.046875x17.46875 [BFC] children: inline
|
||||
line 0 width: 70.046875, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 8, rect: [9,9 70.046875x17.46875]
|
||||
"Header 1"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableCellBox <th> at (81.046875,9) content-size 72.515625x17.46875 [BFC] children: inline
|
||||
line 0 width: 72.515625, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 8, rect: [81.046875,9 72.515625x17.46875]
|
||||
"Header 2"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableCellBox <th> at (155.5625,9) content-size 72.796875x17.46875 [BFC] children: inline
|
||||
line 0 width: 72.796875, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 8, rect: [155.5625,9 72.796875x17.46875]
|
||||
"Header 3"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableRowBox <tr> at (8,27.46875) content-size 221.359375x19.46875 children: not-inline
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableCellBox <td> at (9,38.203125) content-size 70.046875x17.46875 [BFC] children: inline
|
||||
line 0 width: 49.609375, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 5, rect: [9,38.203125 49.609375x17.46875]
|
||||
"Row 1"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableCellBox <td> at (81.046875,28.46875) content-size 72.515625x17.46875 [BFC] children: inline
|
||||
line 0 width: 41.84375, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 6, rect: [81.046875,28.46875 41.84375x17.46875]
|
||||
"Cell 1"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableCellBox <td> at (155.5625,28.46875) content-size 72.796875x17.46875 [BFC] children: inline
|
||||
line 0 width: 44.3125, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 6, rect: [155.5625,28.46875 44.3125x17.46875]
|
||||
"Cell 2"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableRowBox <tr> at (8,46.9375) content-size 221.359375x19.46875 children: not-inline
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableCellBox <td> at (81.046875,47.9375) content-size 72.515625x17.46875 [BFC] children: inline
|
||||
line 0 width: 44.59375, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 6, rect: [81.046875,47.9375 44.59375x17.46875]
|
||||
"Cell 3"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableCellBox <td> at (155.5625,47.9375) content-size 72.796875x17.46875 [BFC] children: inline
|
||||
line 0 width: 43.25, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 6, rect: [155.5625,47.9375 43.25x17.46875]
|
||||
"Cell 4"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableRowBox <tr> at (8,66.40625) content-size 221.359375x19.46875 children: not-inline
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableCellBox <td> at (9,67.40625) content-size 70.046875x17.46875 [BFC] children: inline
|
||||
line 0 width: 52.078125, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 5, rect: [9,67.40625 52.078125x17.46875]
|
||||
"Row 2"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableCellBox <td> at (81.046875,67.40625) content-size 72.515625x17.46875 [BFC] children: inline
|
||||
line 0 width: 43.953125, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 6, rect: [81.046875,67.40625 43.953125x17.46875]
|
||||
"Cell 5"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TableCellBox <td> at (155.5625,67.40625) content-size 72.796875x17.46875 [BFC] children: inline
|
||||
line 0 width: 44.234375, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 6, rect: [155.5625,67.40625 44.234375x17.46875]
|
||||
"Cell 6"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,85.875) content-size 784x0 children: inline
|
||||
TextNode <#text>
|
32
Tests/LibWeb/Layout/input/table/rowspan.html
Normal file
32
Tests/LibWeb/Layout/input/table/rowspan.html
Normal file
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Table with rowspan</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<th>Header 1</th>
|
||||
<th>Header 2</th>
|
||||
<th>Header 3</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2">Row 1</td>
|
||||
<td>Cell 1</td>
|
||||
<td>Cell 2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cell 3</td>
|
||||
<td>Cell 4</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Row 2</td>
|
||||
<td>Cell 5</td>
|
||||
<td>Cell 6</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -457,7 +457,11 @@ void TableFormattingContext::compute_table_height(LayoutMode layout_mode)
|
|||
|
||||
cell.baseline = box_baseline(m_state, cell.box);
|
||||
|
||||
row.base_height = max(row.base_height, cell_state.border_box_height());
|
||||
// Only cells spanning the current row exclusively are part of computing minimum height of a row,
|
||||
// as described in https://www.w3.org/TR/css-tables-3/#computing-the-table-height
|
||||
if (cell.row_span == 1) {
|
||||
row.base_height = max(row.base_height, cell_state.border_box_height());
|
||||
}
|
||||
row.baseline = max(row.baseline, cell.baseline);
|
||||
}
|
||||
|
||||
|
@ -645,7 +649,7 @@ void TableFormattingContext::position_cell_boxes()
|
|||
auto& cell_state = m_state.get_mutable(cell.box);
|
||||
auto& row_state = m_state.get(m_rows[cell.row_index].box);
|
||||
CSSPixels const cell_border_box_height = cell_state.content_height() + cell_state.border_box_top() + cell_state.border_box_bottom();
|
||||
CSSPixels const row_content_height = row_state.content_height();
|
||||
CSSPixels const row_content_height = compute_row_content_height(cell);
|
||||
auto const& vertical_align = cell.box->computed_values().vertical_align();
|
||||
if (vertical_align.has<CSS::VerticalAlign>()) {
|
||||
switch (vertical_align.get<CSS::VerticalAlign>()) {
|
||||
|
@ -671,6 +675,33 @@ void TableFormattingContext::position_cell_boxes()
|
|||
}
|
||||
}
|
||||
|
||||
CSSPixels TableFormattingContext::compute_row_content_height(Cell const& cell) const
|
||||
{
|
||||
auto& row_state = m_state.get(m_rows[cell.row_index].box);
|
||||
if (cell.row_span == 1) {
|
||||
return row_state.content_height();
|
||||
}
|
||||
// The height of a cell is the sum of all spanned rows, as described in
|
||||
// https://www.w3.org/TR/css-tables-3/#bounding-box-assignment
|
||||
|
||||
// When the row span is greater than 1, the borders of inner rows within the span have to be
|
||||
// included in the content height of the spanning cell. First top and final bottom borders are
|
||||
// excluded to be consistent with the handling of row span 1 case above, which uses the content
|
||||
// height (no top and bottom borders) of the row.
|
||||
CSSPixels span_height = 0;
|
||||
for (size_t i = 0; i < cell.row_span; ++i) {
|
||||
auto const& row_state = m_state.get(m_rows[cell.row_index + i].box);
|
||||
if (i == 0) {
|
||||
span_height += row_state.content_height() + row_state.border_box_bottom();
|
||||
} else if (i == cell.row_span - 1) {
|
||||
span_height += row_state.border_box_top() + row_state.content_height();
|
||||
} else {
|
||||
span_height += row_state.border_box_height();
|
||||
}
|
||||
}
|
||||
return span_height;
|
||||
}
|
||||
|
||||
void TableFormattingContext::run(Box const& box, LayoutMode layout_mode, AvailableSpace const& available_space)
|
||||
{
|
||||
m_available_space = available_space;
|
||||
|
|
|
@ -78,6 +78,8 @@ private:
|
|||
CSSPixels max_width { 0 };
|
||||
};
|
||||
|
||||
CSSPixels compute_row_content_height(Cell const& cell) const;
|
||||
|
||||
Vector<Cell> m_cells;
|
||||
Vector<Column> m_columns;
|
||||
Vector<Row> m_rows;
|
||||
|
|
Loading…
Add table
Reference in a new issue