1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-29 16:25:07 +00:00

LibWeb: Complete table border conflict resolution

Add the element type and grid position to the algorithm and change the
table borders painting to apply the new criteria to corners as well.
This commit is contained in:
Andi Gallo 2023-07-12 04:20:37 +00:00 committed by Andreas Kling
parent 849cf894d8
commit a7166eb103
6 changed files with 317 additions and 127 deletions

View file

@ -119,7 +119,7 @@ struct LayoutState {
void add_floating_descendant(Box const& box) { m_floating_descendants.set(&box); } void add_floating_descendant(Box const& box) { m_floating_descendants.set(&box); }
auto const& floating_descendants() const { return m_floating_descendants; } auto const& floating_descendants() const { return m_floating_descendants; }
void set_override_borders_data(Painting::BordersData const& override_borders_data) { m_override_borders_data = override_borders_data; } void set_override_borders_data(Painting::PaintableBox::BordersDataWithElementKind const& override_borders_data) { m_override_borders_data = override_borders_data; }
auto const& override_borders_data() const { return m_override_borders_data; } auto const& override_borders_data() const { return m_override_borders_data; }
void set_table_cell_coordinates(Painting::PaintableBox::TableCellCoordinates const& table_cell_coordinates) { m_table_cell_coordinates = table_cell_coordinates; } void set_table_cell_coordinates(Painting::PaintableBox::TableCellCoordinates const& table_cell_coordinates) { m_table_cell_coordinates = table_cell_coordinates; }
@ -146,7 +146,7 @@ struct LayoutState {
HashTable<JS::GCPtr<Box const>> m_floating_descendants; HashTable<JS::GCPtr<Box const>> m_floating_descendants;
Optional<Painting::BordersData> m_override_borders_data; Optional<Painting::PaintableBox::BordersDataWithElementKind> m_override_borders_data;
Optional<Painting::PaintableBox::TableCellCoordinates> m_table_cell_coordinates; Optional<Painting::PaintableBox::TableCellCoordinates> m_table_cell_coordinates;
}; };

View file

@ -1182,7 +1182,8 @@ void TableFormattingContext::position_cell_boxes()
bool TableFormattingContext::border_is_less_specific(const CSS::BorderData& a, const CSS::BorderData& b) bool TableFormattingContext::border_is_less_specific(const CSS::BorderData& a, const CSS::BorderData& b)
{ {
// Implements criteria for steps 1, 2 and 3 of border conflict resolution algorithm. // Implements criteria for steps 1, 2 and 3 of border conflict resolution algorithm, as described in
// https://www.w3.org/TR/CSS22/tables.html#border-conflict-resolution.
static HashMap<CSS::LineStyle, unsigned> const line_style_score = { static HashMap<CSS::LineStyle, unsigned> const line_style_score = {
{ CSS::LineStyle::Inset, 0 }, { CSS::LineStyle::Inset, 0 },
{ CSS::LineStyle::Groove, 1 }, { CSS::LineStyle::Groove, 1 },
@ -1194,6 +1195,8 @@ bool TableFormattingContext::border_is_less_specific(const CSS::BorderData& a, c
{ CSS::LineStyle::Double, 7 }, { CSS::LineStyle::Double, 7 },
}; };
// 1. Borders with the 'border-style' of 'hidden' take precedence over all other conflicting borders. Any border with this
// value suppresses all borders at this location.
if (a.line_style == CSS::LineStyle::Hidden) { if (a.line_style == CSS::LineStyle::Hidden) {
return false; return false;
} }
@ -1202,12 +1205,17 @@ bool TableFormattingContext::border_is_less_specific(const CSS::BorderData& a, c
return true; return true;
} }
// 2. Borders with a style of 'none' have the lowest priority. Only if the border properties of all the elements meeting
// at this edge are 'none' will the border be omitted (but note that 'none' is the default value for the border style.)
if (a.line_style == CSS::LineStyle::None) { if (a.line_style == CSS::LineStyle::None) {
return true; return true;
} }
if (b.line_style == CSS::LineStyle::None) { if (b.line_style == CSS::LineStyle::None) {
return false; return false;
} }
// 3. If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders are discarded in favor
// of wider ones. If several have the same 'border-width' then styles are preferred in this order: 'double', 'solid',
// 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'.
if (a.width > b.width) { if (a.width > b.width) {
return false; return false;
} else if (a.width < b.width) { } else if (a.width < b.width) {
@ -1221,11 +1229,6 @@ bool TableFormattingContext::border_is_less_specific(const CSS::BorderData& a, c
return false; return false;
} }
static const CSS::BorderData& winning_border_style(const CSS::BorderData& a, const CSS::BorderData& b)
{
return TableFormattingContext::border_is_less_specific(a, b) ? b : a;
}
const CSS::BorderData& TableFormattingContext::border_data_conflicting_edge(TableFormattingContext::ConflictingEdge const& conflicting_edge) const CSS::BorderData& TableFormattingContext::border_data_conflicting_edge(TableFormattingContext::ConflictingEdge const& conflicting_edge)
{ {
auto const& style = conflicting_edge.element->computed_values(); auto const& style = conflicting_edge.element->computed_values();
@ -1248,10 +1251,52 @@ const CSS::BorderData& TableFormattingContext::border_data_conflicting_edge(Tabl
} }
} }
const Painting::PaintableBox::BorderDataWithElementKind TableFormattingContext::border_data_with_element_kind_from_conflicting_edge(ConflictingEdge const& conflicting_edge)
{
auto const& border_data = border_data_conflicting_edge(conflicting_edge);
return { .border_data = border_data, .element_kind = conflicting_edge.element_kind };
}
TableFormattingContext::ConflictingEdge const& TableFormattingContext::winning_conflicting_edge(TableFormattingContext::ConflictingEdge const& a, TableFormattingContext::ConflictingEdge const& b)
{
auto a_border_data = border_data_conflicting_edge(a);
auto b_border_data = border_data_conflicting_edge(b);
// First check if step 4 of border conflict resolution applies, as described in https://www.w3.org/TR/CSS22/tables.html#border-conflict-resolution.
if (a_border_data.line_style == b_border_data.line_style && a_border_data.width == b_border_data.width) {
// 4. If border styles differ only in color, then a style set on a cell wins over one on a row, which wins over a
// row group, column, column group and, lastly, table. When two elements of the same type conflict, then the one
// further to the left (if the table's 'direction' is 'ltr'; right, if it is 'rtl') and further to the top wins.
if (static_cast<unsigned>(a.element_kind) < static_cast<unsigned>(b.element_kind)) {
return a;
} else if (static_cast<unsigned>(a.element_kind) > static_cast<unsigned>(b.element_kind)) {
return b;
}
// Here the element kind is the same, thus the coordinates are either both set or not set.
VERIFY(a.column.has_value() == b.column.has_value());
VERIFY(a.row.has_value() == b.row.has_value());
if (a.column.has_value()) {
if (a.column.value() < b.column.value()) {
return a;
} else if (a.column.value() > b.column.value()) {
return b;
}
}
if (a.row.has_value()) {
if (a.row.value() < b.row.value()) {
return a;
} else if (a.row.value() > b.row.value()) {
return b;
}
}
return a;
}
// Apply steps 1, 2 and 3 of the border conflict resolution algorithm.
return border_is_less_specific(a_border_data, b_border_data) ? b : a;
}
void TableFormattingContext::border_conflict_resolution() void TableFormattingContext::border_conflict_resolution()
{ {
// Partially implements border conflict resolution, as described in // Implements border conflict resolution, as described in https://www.w3.org/TR/CSS22/tables.html#border-conflict-resolution.
// https://www.w3.org/TR/CSS22/tables.html#border-conflict-resolution
BorderConflictFinder finder(this); BorderConflictFinder finder(this);
for (auto& cell : m_cells) { for (auto& cell : m_cells) {
auto& cell_state = m_state.get_mutable(cell.box); auto& cell_state = m_state.get_mutable(cell.box);
@ -1264,36 +1309,55 @@ void TableFormattingContext::border_conflict_resolution()
if (cell.box->computed_values().border_collapse() == CSS::BorderCollapse::Separate) { if (cell.box->computed_values().border_collapse() == CSS::BorderCollapse::Separate) {
continue; continue;
} }
// Execute steps 1, 2 and 3 of the algorithm for each edge. Painting::PaintableBox::BordersDataWithElementKind override_borders_data;
Painting::BordersData override_borders_data; ConflictingEdge winning_edge_left {
auto const& cell_style = cell.box->computed_values(); .element = cell.box,
auto winning_border_left = cell_style.border_left(); .element_kind = Painting::PaintableBox::ConflictingElementKind::Cell,
for (auto const conflicting_edge : finder.conflicting_edges(cell, ConflictingSide::Left)) { .side = ConflictingSide::Left,
winning_border_left = winning_border_style(winning_border_left, border_data_conflicting_edge(conflicting_edge)); .row = cell.row_index,
.column = cell.column_index,
};
for (auto const& conflicting_edge : finder.conflicting_edges(cell, ConflictingSide::Left)) {
winning_edge_left = winning_conflicting_edge(winning_edge_left, conflicting_edge);
} }
override_borders_data.left = winning_border_left; override_borders_data.left = border_data_with_element_kind_from_conflicting_edge(winning_edge_left);
cell_state.border_left = winning_border_left.width; cell_state.border_left = override_borders_data.left.border_data.width;
auto winning_border_right = cell_style.border_right(); ConflictingEdge winning_edge_right {
for (auto const conflicting_edge : finder.conflicting_edges(cell, ConflictingSide::Right)) { .element = cell.box,
winning_border_right = winning_border_style(winning_border_right, border_data_conflicting_edge(conflicting_edge)); .element_kind = Painting::PaintableBox::ConflictingElementKind::Cell,
.side = ConflictingSide::Right,
.row = cell.row_index,
.column = cell.column_index,
};
for (auto const& conflicting_edge : finder.conflicting_edges(cell, ConflictingSide::Right)) {
winning_edge_right = winning_conflicting_edge(winning_edge_right, conflicting_edge);
} }
override_borders_data.right = winning_border_right; override_borders_data.right = border_data_with_element_kind_from_conflicting_edge(winning_edge_right);
cell_state.border_right = winning_border_right.width; cell_state.border_right = override_borders_data.right.border_data.width;
auto winning_border_top = cell_style.border_top(); ConflictingEdge winning_edge_top {
for (auto const conflicting_edge : finder.conflicting_edges(cell, ConflictingSide::Top)) { .element = cell.box,
winning_border_top = winning_border_style(winning_border_top, border_data_conflicting_edge(conflicting_edge)); .element_kind = Painting::PaintableBox::ConflictingElementKind::Cell,
.side = ConflictingSide::Top,
.row = cell.row_index,
.column = cell.column_index,
};
for (auto const& conflicting_edge : finder.conflicting_edges(cell, ConflictingSide::Top)) {
winning_edge_top = winning_conflicting_edge(winning_edge_top, conflicting_edge);
} }
override_borders_data.top = winning_border_top; override_borders_data.top = border_data_with_element_kind_from_conflicting_edge(winning_edge_top);
cell_state.border_top = winning_border_top.width; cell_state.border_top = override_borders_data.top.border_data.width;
auto winning_border_bottom = cell_style.border_bottom(); ConflictingEdge winning_edge_bottom {
for (auto const conflicting_edge : finder.conflicting_edges(cell, ConflictingSide::Bottom)) { .element = cell.box,
winning_border_bottom = winning_border_style(winning_border_bottom, border_data_conflicting_edge(conflicting_edge)); .element_kind = Painting::PaintableBox::ConflictingElementKind::Cell,
.side = ConflictingSide::Bottom,
.row = cell.row_index,
.column = cell.column_index,
};
for (auto const& conflicting_edge : finder.conflicting_edges(cell, ConflictingSide::Bottom)) {
winning_edge_bottom = winning_conflicting_edge(winning_edge_bottom, conflicting_edge);
} }
override_borders_data.bottom = winning_border_bottom; override_borders_data.bottom = border_data_with_element_kind_from_conflicting_edge(winning_edge_bottom);
cell_state.border_bottom = override_borders_data.bottom.width; cell_state.border_bottom = override_borders_data.bottom.border_data.width;
// FIXME: 4. If border styles differ only in color, then a style set on a cell wins over one on a row, which wins over a
// row group, column, column group and, lastly, table. When two elements of the same type conflict, then the one
// further to the left (if the table's 'direction' is 'ltr'; right, if it is 'rtl') and further to the top wins.
cell_state.set_override_borders_data(override_borders_data); cell_state.set_override_borders_data(override_borders_data);
} }
} }
@ -1384,97 +1448,105 @@ Vector<TableFormattingContext::ConflictingEdge> TableFormattingContext::BorderCo
{ {
Vector<ConflictingEdge> result = {}; Vector<ConflictingEdge> result = {};
if (cell.column_index >= cell.column_span && edge == ConflictingSide::Left) { if (cell.column_index >= cell.column_span && edge == ConflictingSide::Left) {
auto maybe_cell_to_left = m_context->m_cells_by_coordinate[cell.row_index][cell.column_index - cell.column_span]; auto left_cell_column_index = cell.column_index - cell.column_span;
auto maybe_cell_to_left = m_context->m_cells_by_coordinate[cell.row_index][left_cell_column_index];
if (maybe_cell_to_left.has_value()) { if (maybe_cell_to_left.has_value()) {
result.append({ maybe_cell_to_left->box, ConflictingSide::Right }); result.append({ maybe_cell_to_left->box, Painting::PaintableBox::ConflictingElementKind::Cell, ConflictingSide::Right, cell.row_index, left_cell_column_index });
} }
} }
if (cell.column_index + cell.column_span < m_context->m_cells_by_coordinate[cell.row_index].size() && edge == ConflictingSide::Right) { if (cell.column_index + cell.column_span < m_context->m_cells_by_coordinate[cell.row_index].size() && edge == ConflictingSide::Right) {
auto maybe_cell_to_right = m_context->m_cells_by_coordinate[cell.row_index][cell.column_index + cell.column_span]; auto right_cell_column_index = cell.column_index + cell.column_span;
auto maybe_cell_to_right = m_context->m_cells_by_coordinate[cell.row_index][right_cell_column_index];
if (maybe_cell_to_right.has_value()) { if (maybe_cell_to_right.has_value()) {
result.append({ maybe_cell_to_right->box, ConflictingSide::Left }); result.append({ maybe_cell_to_right->box, Painting::PaintableBox::ConflictingElementKind::Cell, ConflictingSide::Left, cell.row_index, right_cell_column_index });
} }
} }
if (cell.row_index >= cell.row_span && edge == ConflictingSide::Top) { if (cell.row_index >= cell.row_span && edge == ConflictingSide::Top) {
auto maybe_cell_above = m_context->m_cells_by_coordinate[cell.row_index - cell.row_span][cell.column_index]; auto above_cell_row_index = cell.row_index - cell.row_span;
auto maybe_cell_above = m_context->m_cells_by_coordinate[above_cell_row_index][cell.column_index];
if (maybe_cell_above.has_value()) { if (maybe_cell_above.has_value()) {
result.append({ maybe_cell_above->box, ConflictingSide::Bottom }); result.append({ maybe_cell_above->box, Painting::PaintableBox::ConflictingElementKind::Cell, ConflictingSide::Bottom, above_cell_row_index, cell.column_index });
} }
} }
if (cell.row_index + cell.row_span < m_context->m_cells_by_coordinate.size() && edge == ConflictingSide::Bottom) { if (cell.row_index + cell.row_span < m_context->m_cells_by_coordinate.size() && edge == ConflictingSide::Bottom) {
auto maybe_cell_below = m_context->m_cells_by_coordinate[cell.row_index + cell.row_span][cell.column_index]; auto below_cell_row_index = cell.row_index + cell.row_span;
auto maybe_cell_below = m_context->m_cells_by_coordinate[below_cell_row_index][cell.column_index];
if (maybe_cell_below.has_value()) { if (maybe_cell_below.has_value()) {
result.append({ maybe_cell_below->box, ConflictingSide::Top }); result.append({ maybe_cell_below->box, Painting::PaintableBox::ConflictingElementKind::Cell, ConflictingSide::Top, below_cell_row_index, cell.column_index });
} }
} }
if (edge == ConflictingSide::Top) { if (edge == ConflictingSide::Top) {
result.append({ m_context->m_rows[cell.row_index].box, ConflictingSide::Top }); result.append({ m_context->m_rows[cell.row_index].box, Painting::PaintableBox::ConflictingElementKind::Row, ConflictingSide::Top, cell.row_index, {} });
} }
if (edge == ConflictingSide::Bottom) { if (edge == ConflictingSide::Bottom) {
result.append({ m_context->m_rows[cell.row_index].box, ConflictingSide::Bottom }); result.append({ m_context->m_rows[cell.row_index].box, Painting::PaintableBox::ConflictingElementKind::Row, ConflictingSide::Bottom, cell.row_index, {} });
} }
if (cell.row_index >= cell.row_span && edge == ConflictingSide::Top) { if (cell.row_index >= cell.row_span && edge == ConflictingSide::Top) {
result.append({ m_context->m_rows[cell.row_index - cell.row_span].box, ConflictingSide::Bottom }); auto above_row_index = cell.row_index - cell.row_span;
result.append({ m_context->m_rows[above_row_index].box, Painting::PaintableBox::ConflictingElementKind::Row, ConflictingSide::Bottom, above_row_index, {} });
} }
if (cell.row_index + cell.row_span < m_context->m_rows.size() && edge == ConflictingSide::Bottom) { if (cell.row_index + cell.row_span < m_context->m_rows.size() && edge == ConflictingSide::Bottom) {
result.append({ m_context->m_rows[cell.row_index + cell.row_span].box, ConflictingSide::Top }); auto below_row_index = cell.row_index + cell.row_span;
result.append({ m_context->m_rows[below_row_index].box, Painting::PaintableBox::ConflictingElementKind::Row, ConflictingSide::Top, below_row_index, {} });
} }
auto const& maybe_row_group = m_row_group_elements_by_index[cell.row_index]; auto const& maybe_row_group = m_row_group_elements_by_index[cell.row_index];
if (maybe_row_group.has_value() && cell.row_index == maybe_row_group->start_index && edge == ConflictingSide::Top) { if (maybe_row_group.has_value() && cell.row_index == maybe_row_group->start_index && edge == ConflictingSide::Top) {
result.append({ maybe_row_group->row_group, ConflictingSide::Top }); result.append({ maybe_row_group->row_group, Painting::PaintableBox::ConflictingElementKind::RowGroup, ConflictingSide::Top, maybe_row_group->start_index, {} });
} }
if (cell.row_index >= cell.row_span) { if (cell.row_index >= cell.row_span) {
auto const& maybe_row_group_above = m_row_group_elements_by_index[cell.row_index - cell.row_span]; auto const& maybe_row_group_above = m_row_group_elements_by_index[cell.row_index - cell.row_span];
if (maybe_row_group_above.has_value() && cell.row_index == maybe_row_group_above->start_index + maybe_row_group_above->row_count && edge == ConflictingSide::Top) { if (maybe_row_group_above.has_value() && cell.row_index == maybe_row_group_above->start_index + maybe_row_group_above->row_count && edge == ConflictingSide::Top) {
result.append({ maybe_row_group_above->row_group, ConflictingSide::Bottom }); result.append({ maybe_row_group_above->row_group, Painting::PaintableBox::ConflictingElementKind::RowGroup, ConflictingSide::Bottom, maybe_row_group_above->start_index, {} });
} }
} }
if (maybe_row_group.has_value() && cell.row_index == maybe_row_group->start_index + maybe_row_group->row_count - 1 && edge == ConflictingSide::Bottom) { if (maybe_row_group.has_value() && cell.row_index == maybe_row_group->start_index + maybe_row_group->row_count - 1 && edge == ConflictingSide::Bottom) {
result.append({ maybe_row_group->row_group, ConflictingSide::Bottom }); result.append({ maybe_row_group->row_group, Painting::PaintableBox::ConflictingElementKind::RowGroup, ConflictingSide::Bottom, maybe_row_group->start_index, {} });
} }
if (cell.row_index + cell.row_span < m_row_group_elements_by_index.size()) { if (cell.row_index + cell.row_span < m_row_group_elements_by_index.size()) {
auto const& maybe_row_group_below = m_row_group_elements_by_index[cell.row_index + cell.row_span]; auto const& maybe_row_group_below = m_row_group_elements_by_index[cell.row_index + cell.row_span];
if (maybe_row_group_below.has_value() && cell.row_index + cell.row_span == maybe_row_group_below->start_index && edge == ConflictingSide::Bottom) { if (maybe_row_group_below.has_value() && cell.row_index + cell.row_span == maybe_row_group_below->start_index && edge == ConflictingSide::Bottom) {
result.append({ maybe_row_group_below->row_group, ConflictingSide::Top }); result.append({ maybe_row_group_below->row_group, Painting::PaintableBox::ConflictingElementKind::RowGroup, ConflictingSide::Top, maybe_row_group_below->start_index, {} });
} }
} }
if (m_col_elements_by_index[cell.column_index] && edge == ConflictingSide::Left) { if (m_col_elements_by_index[cell.column_index] && edge == ConflictingSide::Left) {
result.append({ m_col_elements_by_index[cell.column_index], ConflictingSide::Left }); result.append({ m_col_elements_by_index[cell.column_index], Painting::PaintableBox::ConflictingElementKind::ColumnGroup, ConflictingSide::Left, {}, cell.column_index });
} }
if (cell.column_index >= cell.column_span && m_col_elements_by_index[cell.column_index - cell.column_span] && edge == ConflictingSide::Left) { if (cell.column_index >= cell.column_span && m_col_elements_by_index[cell.column_index - cell.column_span] && edge == ConflictingSide::Left) {
result.append({ m_col_elements_by_index[cell.column_index - cell.column_span], ConflictingSide::Right }); auto left_column_index = cell.column_index - cell.column_span;
result.append({ m_col_elements_by_index[left_column_index], Painting::PaintableBox::ConflictingElementKind::ColumnGroup, ConflictingSide::Right, {}, left_column_index });
} }
if (m_col_elements_by_index[cell.column_index] && edge == ConflictingSide::Right) { if (m_col_elements_by_index[cell.column_index] && edge == ConflictingSide::Right) {
result.append({ m_col_elements_by_index[cell.column_index], ConflictingSide::Right }); result.append({ m_col_elements_by_index[cell.column_index], Painting::PaintableBox::ConflictingElementKind::ColumnGroup, ConflictingSide::Right, {}, cell.column_index });
} }
if (cell.column_index + cell.column_span < m_col_elements_by_index.size() && m_col_elements_by_index[cell.column_index + cell.column_span] && edge == ConflictingSide::Right) { if (cell.column_index + cell.column_span < m_col_elements_by_index.size() && m_col_elements_by_index[cell.column_index + cell.column_span] && edge == ConflictingSide::Right) {
result.append({ m_col_elements_by_index[cell.column_index + cell.column_span], ConflictingSide::Left }); auto right_column_index = cell.column_index + cell.column_span;
result.append({ m_col_elements_by_index[right_column_index], Painting::PaintableBox::ConflictingElementKind::ColumnGroup, ConflictingSide::Left, {}, right_column_index });
} }
if (cell.row_index == 0 && edge == ConflictingSide::Top) { if (cell.row_index == 0 && edge == ConflictingSide::Top) {
if (m_col_elements_by_index[cell.column_index]) { if (m_col_elements_by_index[cell.column_index]) {
result.append({ m_col_elements_by_index[cell.column_index], ConflictingSide::Top }); result.append({ m_col_elements_by_index[cell.column_index], Painting::PaintableBox::ConflictingElementKind::ColumnGroup, ConflictingSide::Top, {}, cell.column_index });
} }
result.append({ &m_context->table_box(), ConflictingSide::Top }); result.append({ &m_context->table_box(), Painting::PaintableBox::ConflictingElementKind::Table, ConflictingSide::Top, {}, {} });
} }
if (cell.row_index == m_context->m_rows.size() - 1 && edge == ConflictingSide::Bottom) { if (cell.row_index == m_context->m_rows.size() - 1 && edge == ConflictingSide::Bottom) {
if (m_col_elements_by_index[cell.column_index]) { if (m_col_elements_by_index[cell.column_index]) {
result.append({ m_col_elements_by_index[cell.column_index], ConflictingSide::Bottom }); result.append({ m_col_elements_by_index[cell.column_index], Painting::PaintableBox::ConflictingElementKind::ColumnGroup, ConflictingSide::Bottom, {}, cell.column_index });
} }
result.append({ &m_context->table_box(), ConflictingSide::Bottom }); result.append({ &m_context->table_box(), Painting::PaintableBox::ConflictingElementKind::Table, ConflictingSide::Bottom, {}, {} });
} }
if (cell.column_index == 0 && edge == ConflictingSide::Left) { if (cell.column_index == 0 && edge == ConflictingSide::Left) {
result.append({ m_context->m_rows[cell.row_index].box, ConflictingSide::Left }); result.append({ m_context->m_rows[cell.row_index].box, Painting::PaintableBox::ConflictingElementKind::Row, ConflictingSide::Left, cell.row_index, {} });
if (m_row_group_elements_by_index[cell.row_index].has_value()) { if (m_row_group_elements_by_index[cell.row_index].has_value()) {
result.append({ m_row_group_elements_by_index[cell.row_index]->row_group, ConflictingSide::Left }); result.append({ m_row_group_elements_by_index[cell.row_index]->row_group, Painting::PaintableBox::ConflictingElementKind::RowGroup, ConflictingSide::Left, cell.row_index, {} });
} }
result.append({ &m_context->table_box(), ConflictingSide::Left }); result.append({ &m_context->table_box(), Painting::PaintableBox::ConflictingElementKind::Table, ConflictingSide::Left, {}, {} });
} }
if (cell.column_index == m_context->m_columns.size() - 1 && edge == ConflictingSide::Right) { if (cell.column_index == m_context->m_columns.size() - 1 && edge == ConflictingSide::Right) {
result.append({ m_context->m_rows[cell.row_index].box, ConflictingSide::Right }); result.append({ m_context->m_rows[cell.row_index].box, Painting::PaintableBox::ConflictingElementKind::Row, ConflictingSide::Right, cell.row_index, {} });
if (m_row_group_elements_by_index[cell.row_index].has_value()) { if (m_row_group_elements_by_index[cell.row_index].has_value()) {
result.append({ m_row_group_elements_by_index[cell.row_index]->row_group, ConflictingSide::Right }); result.append({ m_row_group_elements_by_index[cell.row_index]->row_group, Painting::PaintableBox::ConflictingElementKind::RowGroup, ConflictingSide::Right, cell.row_index, {} });
} }
result.append({ &m_context->table_box(), ConflictingSide::Right }); result.append({ &m_context->table_box(), Painting::PaintableBox::ConflictingElementKind::Table, ConflictingSide::Right, {}, {} });
} }
return result; return result;
} }

View file

@ -157,10 +157,16 @@ private:
struct ConflictingEdge { struct ConflictingEdge {
Node const* element; Node const* element;
Painting::PaintableBox::ConflictingElementKind element_kind;
ConflictingSide side; ConflictingSide side;
Optional<size_t> row;
Optional<size_t> column;
}; };
static TableFormattingContext::ConflictingEdge const& winning_conflicting_edge(TableFormattingContext::ConflictingEdge const& a, TableFormattingContext::ConflictingEdge const& b);
static const CSS::BorderData& border_data_conflicting_edge(ConflictingEdge const& conflicting_edge); static const CSS::BorderData& border_data_conflicting_edge(ConflictingEdge const& conflicting_edge);
static const Painting::PaintableBox::BorderDataWithElementKind border_data_with_element_kind_from_conflicting_edge(ConflictingEdge const& conflicting_edge);
class BorderConflictFinder { class BorderConflictFinder {
public: public:

View file

@ -224,9 +224,19 @@ void PaintableBox::paint(PaintContext& context, PaintPhase phase) const
} }
} }
BordersData PaintableBox::remove_element_kind_from_borders_data(PaintableBox::BordersDataWithElementKind borders_data)
{
return {
.top = borders_data.top.border_data,
.right = borders_data.right.border_data,
.bottom = borders_data.bottom.border_data,
.left = borders_data.left.border_data,
};
}
void PaintableBox::paint_border(PaintContext& context) const void PaintableBox::paint_border(PaintContext& context) const
{ {
auto borders_data = m_override_borders_data.has_value() ? m_override_borders_data.value() : BordersData { auto borders_data = m_override_borders_data.has_value() ? remove_element_kind_from_borders_data(m_override_borders_data.value()) : BordersData {
.top = box_model().border.top == 0 ? CSS::BorderData() : computed_values().border_top(), .top = box_model().border.top == 0 ? CSS::BorderData() : computed_values().border_top(),
.right = box_model().border.right == 0 ? CSS::BorderData() : computed_values().border_right(), .right = box_model().border.right == 0 ? CSS::BorderData() : computed_values().border_right(),
.bottom = box_model().border.bottom == 0 ? CSS::BorderData() : computed_values().border_bottom(), .bottom = box_model().border.bottom == 0 ? CSS::BorderData() : computed_values().border_bottom(),

View file

@ -130,8 +130,31 @@ public:
bool is_out_of_view(PaintContext&) const; bool is_out_of_view(PaintContext&) const;
void set_override_borders_data(BordersData const& override_borders_data) { m_override_borders_data = override_borders_data; } enum class ConflictingElementKind {
Optional<BordersData> const& override_borders_data() const { return m_override_borders_data; } Cell,
Row,
RowGroup,
Column,
ColumnGroup,
Table,
};
struct BorderDataWithElementKind {
CSS::BorderData border_data;
ConflictingElementKind element_kind;
};
struct BordersDataWithElementKind {
BorderDataWithElementKind top;
BorderDataWithElementKind right;
BorderDataWithElementKind bottom;
BorderDataWithElementKind left;
};
void set_override_borders_data(BordersDataWithElementKind const& override_borders_data) { m_override_borders_data = override_borders_data; }
Optional<BordersDataWithElementKind> const& override_borders_data() const { return m_override_borders_data; }
static BordersData remove_element_kind_from_borders_data(PaintableBox::BordersDataWithElementKind borders_data);
struct TableCellCoordinates { struct TableCellCoordinates {
size_t row_index; size_t row_index;
@ -182,7 +205,7 @@ private:
mutable bool m_clipping_overflow { false }; mutable bool m_clipping_overflow { false };
Optional<BorderRadiusCornerClipper> mutable m_overflow_corner_radius_clipper; Optional<BorderRadiusCornerClipper> mutable m_overflow_corner_radius_clipper;
Optional<BordersData> m_override_borders_data; Optional<BordersDataWithElementKind> m_override_borders_data;
Optional<TableCellCoordinates> m_table_cell_coordinates; Optional<TableCellCoordinates> m_table_cell_coordinates;
}; };

View file

@ -49,97 +49,147 @@ enum class EdgeDirection {
struct BorderEdgePaintingInfo { struct BorderEdgePaintingInfo {
DevicePixelRect rect; DevicePixelRect rect;
CSS::BorderData border_data; PaintableBox::BorderDataWithElementKind border_data_with_element_kind;
EdgeDirection direction; EdgeDirection direction;
Optional<size_t> row;
Optional<size_t> column;
}; };
static BorderEdgePaintingInfo make_right_cell_edge(PaintContext& context, CSSPixelRect const& right_cell_rect, CSSPixelRect const& cell_rect, BordersData const& borders_data) static Optional<size_t> row_index_for_element_kind(size_t index, Painting::PaintableBox::ConflictingElementKind element_kind)
{
switch (element_kind) {
case Painting::PaintableBox::ConflictingElementKind::Cell:
case Painting::PaintableBox::ConflictingElementKind::Row:
case Painting::PaintableBox::ConflictingElementKind::RowGroup: {
return index;
}
default:
return {};
}
}
static Optional<size_t> column_index_for_element_kind(size_t index, Painting::PaintableBox::ConflictingElementKind element_kind)
{
switch (element_kind) {
case Painting::PaintableBox::ConflictingElementKind::Cell:
case Painting::PaintableBox::ConflictingElementKind::Column:
case Painting::PaintableBox::ConflictingElementKind::ColumnGroup: {
return index;
}
default:
return {};
}
}
static BorderEdgePaintingInfo make_right_cell_edge(
PaintContext& context,
CSSPixelRect const& right_cell_rect,
CSSPixelRect const& cell_rect,
PaintableBox::BordersDataWithElementKind const& borders_data,
CellCoordinates const& coordinates)
{ {
DevicePixelRect right_border_rect = { DevicePixelRect right_border_rect = {
context.rounded_device_pixels(right_cell_rect.x() - round(borders_data.right.width / 2)), context.rounded_device_pixels(right_cell_rect.x() - round(borders_data.right.border_data.width / 2)),
context.rounded_device_pixels(cell_rect.y() - round(borders_data.top.width / 2)), context.rounded_device_pixels(cell_rect.y() - round(borders_data.top.border_data.width / 2)),
context.rounded_device_pixels(borders_data.right.width), context.rounded_device_pixels(borders_data.right.border_data.width),
context.rounded_device_pixels(max(cell_rect.height(), right_cell_rect.height()) + round(borders_data.top.width / 2) + round(borders_data.bottom.width / 2)), context.rounded_device_pixels(max(cell_rect.height(), right_cell_rect.height()) + round(borders_data.top.border_data.width / 2) + round(borders_data.bottom.border_data.width / 2)),
}; };
return BorderEdgePaintingInfo { return BorderEdgePaintingInfo {
.rect = right_border_rect, .rect = right_border_rect,
.border_data = borders_data.right, .border_data_with_element_kind = borders_data.right,
.direction = EdgeDirection::Vertical, .direction = EdgeDirection::Vertical,
.row = row_index_for_element_kind(coordinates.row_index, borders_data.right.element_kind),
.column = column_index_for_element_kind(coordinates.column_index, borders_data.right.element_kind),
}; };
} }
static BorderEdgePaintingInfo make_down_cell_edge(PaintContext& context, CSSPixelRect const& down_cell_rect, CSSPixelRect const& cell_rect, BordersData const& borders_data) static BorderEdgePaintingInfo make_down_cell_edge(
PaintContext& context,
CSSPixelRect const& down_cell_rect,
CSSPixelRect const& cell_rect,
PaintableBox::BordersDataWithElementKind const& borders_data,
CellCoordinates const& coordinates)
{ {
DevicePixelRect down_border_rect = { DevicePixelRect down_border_rect = {
context.rounded_device_pixels(cell_rect.x() - round(borders_data.left.width / 2)), context.rounded_device_pixels(cell_rect.x() - round(borders_data.left.border_data.width / 2)),
context.rounded_device_pixels(down_cell_rect.y() - round(borders_data.bottom.width / 2)), context.rounded_device_pixels(down_cell_rect.y() - round(borders_data.bottom.border_data.width / 2)),
context.rounded_device_pixels(max(cell_rect.width(), down_cell_rect.width()) + round(borders_data.left.width / 2) + round(borders_data.right.width / 2)), context.rounded_device_pixels(max(cell_rect.width(), down_cell_rect.width()) + round(borders_data.left.border_data.width / 2) + round(borders_data.right.border_data.width / 2)),
context.rounded_device_pixels(borders_data.bottom.width), context.rounded_device_pixels(borders_data.bottom.border_data.width),
}; };
return BorderEdgePaintingInfo { return BorderEdgePaintingInfo {
.rect = down_border_rect, .rect = down_border_rect,
.border_data = borders_data.bottom, .border_data_with_element_kind = borders_data.bottom,
.direction = EdgeDirection::Horizontal, .direction = EdgeDirection::Horizontal,
.row = row_index_for_element_kind(coordinates.row_index, borders_data.bottom.element_kind),
.column = column_index_for_element_kind(coordinates.column_index, borders_data.bottom.element_kind),
}; };
} }
static BorderEdgePaintingInfo make_first_row_top_cell_edge(PaintContext& context, CSSPixelRect const& cell_rect, BordersData const& borders_data) static BorderEdgePaintingInfo make_first_row_top_cell_edge(PaintContext& context, CSSPixelRect const& cell_rect, PaintableBox::BordersDataWithElementKind const& borders_data, CellCoordinates const& coordinates)
{ {
DevicePixelRect top_border_rect = { DevicePixelRect top_border_rect = {
context.rounded_device_pixels(cell_rect.x() - round(borders_data.left.width / 2)), context.rounded_device_pixels(cell_rect.x() - round(borders_data.left.border_data.width / 2)),
context.rounded_device_pixels(cell_rect.y() - round(borders_data.top.width / 2)), context.rounded_device_pixels(cell_rect.y() - round(borders_data.top.border_data.width / 2)),
context.rounded_device_pixels(cell_rect.width()), context.rounded_device_pixels(cell_rect.width()),
context.rounded_device_pixels(borders_data.top.width), context.rounded_device_pixels(borders_data.top.border_data.width),
}; };
return BorderEdgePaintingInfo { return BorderEdgePaintingInfo {
.rect = top_border_rect, .rect = top_border_rect,
.border_data = borders_data.top, .border_data_with_element_kind = borders_data.top,
.direction = EdgeDirection::Horizontal, .direction = EdgeDirection::Horizontal,
.row = row_index_for_element_kind(coordinates.row_index, borders_data.top.element_kind),
.column = column_index_for_element_kind(coordinates.column_index, borders_data.top.element_kind),
}; };
} }
static BorderEdgePaintingInfo make_last_row_bottom_cell_edge(PaintContext& context, CSSPixelRect const& cell_rect, BordersData const& borders_data) static BorderEdgePaintingInfo make_last_row_bottom_cell_edge(PaintContext& context, CSSPixelRect const& cell_rect, PaintableBox::BordersDataWithElementKind const& borders_data, CellCoordinates const& coordinates)
{ {
DevicePixelRect bottom_border_rect = { DevicePixelRect bottom_border_rect = {
context.rounded_device_pixels(cell_rect.x() - round(borders_data.left.width / 2)), context.rounded_device_pixels(cell_rect.x() - round(borders_data.left.border_data.width / 2)),
context.rounded_device_pixels(cell_rect.y() + cell_rect.height() - round(borders_data.bottom.width / 2)), context.rounded_device_pixels(cell_rect.y() + cell_rect.height() - round(borders_data.bottom.border_data.width / 2)),
context.rounded_device_pixels(cell_rect.width() + round(borders_data.left.width / 2) + round(borders_data.right.width / 2)), context.rounded_device_pixels(cell_rect.width() + round(borders_data.left.border_data.width / 2) + round(borders_data.right.border_data.width / 2)),
context.rounded_device_pixels(borders_data.bottom.width), context.rounded_device_pixels(borders_data.bottom.border_data.width),
}; };
return BorderEdgePaintingInfo { return BorderEdgePaintingInfo {
.rect = bottom_border_rect, .rect = bottom_border_rect,
.border_data = borders_data.bottom, .border_data_with_element_kind = borders_data.bottom,
.direction = EdgeDirection::Horizontal, .direction = EdgeDirection::Horizontal,
.row = row_index_for_element_kind(coordinates.row_index, borders_data.bottom.element_kind),
.column = column_index_for_element_kind(coordinates.column_index, borders_data.bottom.element_kind),
}; };
} }
static BorderEdgePaintingInfo make_first_column_left_cell_edge(PaintContext& context, CSSPixelRect const& cell_rect, BordersData const& borders_data) static BorderEdgePaintingInfo make_first_column_left_cell_edge(PaintContext& context, CSSPixelRect const& cell_rect, PaintableBox::BordersDataWithElementKind const& borders_data, CellCoordinates const& coordinates)
{ {
DevicePixelRect left_border_rect = { DevicePixelRect left_border_rect = {
context.rounded_device_pixels(cell_rect.x() - round(borders_data.left.width / 2)), context.rounded_device_pixels(cell_rect.x() - round(borders_data.left.border_data.width / 2)),
context.rounded_device_pixels(cell_rect.y() - round(borders_data.top.width / 2)), context.rounded_device_pixels(cell_rect.y() - round(borders_data.top.border_data.width / 2)),
context.rounded_device_pixels(borders_data.left.width), context.rounded_device_pixels(borders_data.left.border_data.width),
context.rounded_device_pixels(cell_rect.height() + round(borders_data.top.width / 2)), context.rounded_device_pixels(cell_rect.height() + round(borders_data.top.border_data.width / 2)),
}; };
return BorderEdgePaintingInfo { return BorderEdgePaintingInfo {
.rect = left_border_rect, .rect = left_border_rect,
.border_data = borders_data.left, .border_data_with_element_kind = borders_data.left,
.direction = EdgeDirection::Vertical, .direction = EdgeDirection::Vertical,
.row = row_index_for_element_kind(coordinates.row_index, borders_data.left.element_kind),
.column = column_index_for_element_kind(coordinates.column_index, borders_data.left.element_kind),
}; };
} }
static BorderEdgePaintingInfo make_last_column_right_cell_edge(PaintContext& context, CSSPixelRect const& cell_rect, BordersData const& borders_data) static BorderEdgePaintingInfo make_last_column_right_cell_edge(PaintContext& context, CSSPixelRect const& cell_rect, PaintableBox::BordersDataWithElementKind const& borders_data, CellCoordinates const& coordinates)
{ {
DevicePixelRect right_border_rect = { DevicePixelRect right_border_rect = {
context.rounded_device_pixels(cell_rect.x() + cell_rect.width() - round(borders_data.right.width / 2)), context.rounded_device_pixels(cell_rect.x() + cell_rect.width() - round(borders_data.right.border_data.width / 2)),
context.rounded_device_pixels(cell_rect.y() - round(borders_data.top.width / 2)), context.rounded_device_pixels(cell_rect.y() - round(borders_data.top.border_data.width / 2)),
context.rounded_device_pixels(borders_data.right.width), context.rounded_device_pixels(borders_data.right.border_data.width),
context.rounded_device_pixels(cell_rect.height() + round(borders_data.top.width / 2) + round(borders_data.bottom.width / 2)), context.rounded_device_pixels(cell_rect.height() + round(borders_data.top.border_data.width / 2) + round(borders_data.bottom.border_data.width / 2)),
}; };
return BorderEdgePaintingInfo { return BorderEdgePaintingInfo {
.rect = right_border_rect, .rect = right_border_rect,
.border_data = borders_data.right, .border_data_with_element_kind = borders_data.right,
.direction = EdgeDirection::Vertical, .direction = EdgeDirection::Vertical,
.row = row_index_for_element_kind(coordinates.row_index, borders_data.right.element_kind),
.column = column_index_for_element_kind(coordinates.column_index, borders_data.right.element_kind),
}; };
} }
@ -147,17 +197,38 @@ static void paint_collected_edges(PaintContext& context, Vector<BorderEdgePainti
{ {
// This sorting step isn't part of the specification, but it matches the behavior of other browsers at border intersections, which aren't // This sorting step isn't part of the specification, but it matches the behavior of other browsers at border intersections, which aren't
// part of border conflict resolution in the specification but it's still desirable to handle them in a way which is consistent with it. // part of border conflict resolution in the specification but it's still desirable to handle them in a way which is consistent with it.
// See https://www.w3.org/TR/CSS22/tables.html#border-conflict-resolution for reference.
quick_sort(border_edge_painting_info_list, [](auto const& a, auto const& b) { quick_sort(border_edge_painting_info_list, [](auto const& a, auto const& b) {
return Layout::TableFormattingContext::border_is_less_specific(a.border_data, b.border_data); auto const& a_border_data = a.border_data_with_element_kind.border_data;
auto const& b_border_data = b.border_data_with_element_kind.border_data;
if (a_border_data.line_style == b_border_data.line_style && a_border_data.width == b_border_data.width) {
if (b.border_data_with_element_kind.element_kind < a.border_data_with_element_kind.element_kind) {
return true;
} else if (b.border_data_with_element_kind.element_kind > a.border_data_with_element_kind.element_kind) {
return false;
}
// Here the element kind is the same, thus the coordinates are either both set or not set.
VERIFY(a.column.has_value() == b.column.has_value());
VERIFY(a.row.has_value() == b.row.has_value());
if (a.column.has_value()) {
if (b.column.value() < a.column.value()) {
return true;
} else if (b.column.value() > a.column.value()) {
return false;
}
}
return a.row.has_value() ? b.row.value() < a.row.value() : false;
}
return Layout::TableFormattingContext::border_is_less_specific(a_border_data, b_border_data);
}); });
for (auto const& border_edge_painting_info : border_edge_painting_info_list) { for (auto const& border_edge_painting_info : border_edge_painting_info_list) {
auto const& border_data = border_edge_painting_info.border_data; auto const& border_data_with_element_kind = border_edge_painting_info.border_data_with_element_kind;
CSSPixels width = border_data.width; CSSPixels width = border_data_with_element_kind.border_data.width;
if (width <= 0) if (width <= 0)
continue; continue;
auto color = border_data.color; auto color = border_data_with_element_kind.border_data.color;
auto border_style = border_data.line_style; auto border_style = border_data_with_element_kind.border_data.line_style;
auto p1 = border_edge_painting_info.rect.top_left(); auto p1 = border_edge_painting_info.rect.top_left();
auto p2 = border_edge_painting_info.direction == EdgeDirection::Horizontal auto p2 = border_edge_painting_info.direction == EdgeDirection::Horizontal
? border_edge_painting_info.rect.top_right() ? border_edge_painting_info.rect.top_right()
@ -194,31 +265,39 @@ void paint_table_collapsed_borders(PaintContext& context, Layout::Node const& bo
column_count = max(column_count, cell_box->table_cell_coordinates()->column_index + cell_box->table_cell_coordinates()->column_span); column_count = max(column_count, cell_box->table_cell_coordinates()->column_index + cell_box->table_cell_coordinates()->column_span);
} }
for (auto const cell_box : cell_boxes) { for (auto const cell_box : cell_boxes) {
auto borders_data = cell_box->override_borders_data().has_value() ? cell_box->override_borders_data().value() : BordersData { auto borders_data = cell_box->override_borders_data().has_value() ? cell_box->override_borders_data().value() : PaintableBox::BordersDataWithElementKind {
.top = cell_box->box_model().border.top == 0 ? CSS::BorderData() : cell_box->computed_values().border_top(), .top = { .border_data = cell_box->box_model().border.top == 0 ? CSS::BorderData() : cell_box->computed_values().border_top(), .element_kind = PaintableBox::ConflictingElementKind::Cell },
.right = cell_box->box_model().border.right == 0 ? CSS::BorderData() : cell_box->computed_values().border_right(), .right = { .border_data = cell_box->box_model().border.right == 0 ? CSS::BorderData() : cell_box->computed_values().border_right(), .element_kind = PaintableBox::ConflictingElementKind::Cell },
.bottom = cell_box->box_model().border.bottom == 0 ? CSS::BorderData() : cell_box->computed_values().border_bottom(), .bottom = { .border_data = cell_box->box_model().border.bottom == 0 ? CSS::BorderData() : cell_box->computed_values().border_bottom(), .element_kind = PaintableBox::ConflictingElementKind::Cell },
.left = cell_box->box_model().border.left == 0 ? CSS::BorderData() : cell_box->computed_values().border_left(), .left = { .border_data = cell_box->box_model().border.left == 0 ? CSS::BorderData() : cell_box->computed_values().border_left(), .element_kind = PaintableBox::ConflictingElementKind::Cell },
}; };
auto cell_rect = cell_box->absolute_border_box_rect(); auto cell_rect = cell_box->absolute_border_box_rect();
auto maybe_right_cell = cell_coordinates_to_box.get(CellCoordinates { CellCoordinates right_cell_coordinates {
.row_index = cell_box->table_cell_coordinates()->row_index, .row_index = cell_box->table_cell_coordinates()->row_index,
.column_index = cell_box->table_cell_coordinates()->column_index + cell_box->table_cell_coordinates()->column_span }); .column_index = cell_box->table_cell_coordinates()->column_index + cell_box->table_cell_coordinates()->column_span
auto maybe_down_cell = cell_coordinates_to_box.get(CellCoordinates { };
auto maybe_right_cell = cell_coordinates_to_box.get(right_cell_coordinates);
CellCoordinates down_cell_coordinates {
.row_index = cell_box->table_cell_coordinates()->row_index + cell_box->table_cell_coordinates()->row_span, .row_index = cell_box->table_cell_coordinates()->row_index + cell_box->table_cell_coordinates()->row_span,
.column_index = cell_box->table_cell_coordinates()->column_index }); .column_index = cell_box->table_cell_coordinates()->column_index
};
auto maybe_down_cell = cell_coordinates_to_box.get(down_cell_coordinates);
if (maybe_right_cell.has_value()) if (maybe_right_cell.has_value())
border_edge_painting_info_list.append(make_right_cell_edge(context, maybe_right_cell.value()->absolute_border_box_rect(), cell_rect, borders_data)); border_edge_painting_info_list.append(make_right_cell_edge(context, maybe_right_cell.value()->absolute_border_box_rect(), cell_rect, borders_data, right_cell_coordinates));
if (maybe_down_cell.has_value()) if (maybe_down_cell.has_value())
border_edge_painting_info_list.append(make_down_cell_edge(context, maybe_down_cell.value()->absolute_border_box_rect(), cell_rect, borders_data)); border_edge_painting_info_list.append(make_down_cell_edge(context, maybe_down_cell.value()->absolute_border_box_rect(), cell_rect, borders_data, down_cell_coordinates));
if (cell_box->table_cell_coordinates()->row_index == 0) if (cell_box->table_cell_coordinates()->row_index == 0)
border_edge_painting_info_list.append(make_first_row_top_cell_edge(context, cell_rect, borders_data)); border_edge_painting_info_list.append(make_first_row_top_cell_edge(context, cell_rect, borders_data,
{ .row_index = 0, .column_index = cell_box->table_cell_coordinates()->column_index }));
if (cell_box->table_cell_coordinates()->row_index + cell_box->table_cell_coordinates()->row_span == row_count) if (cell_box->table_cell_coordinates()->row_index + cell_box->table_cell_coordinates()->row_span == row_count)
border_edge_painting_info_list.append(make_last_row_bottom_cell_edge(context, cell_rect, borders_data)); border_edge_painting_info_list.append(make_last_row_bottom_cell_edge(context, cell_rect, borders_data,
{ .row_index = row_count - 1, .column_index = cell_box->table_cell_coordinates()->column_index }));
if (cell_box->table_cell_coordinates()->column_index == 0) if (cell_box->table_cell_coordinates()->column_index == 0)
border_edge_painting_info_list.append(make_first_column_left_cell_edge(context, cell_rect, borders_data)); border_edge_painting_info_list.append(make_first_column_left_cell_edge(context, cell_rect, borders_data,
{ .row_index = cell_box->table_cell_coordinates()->row_index, .column_index = 0 }));
if (cell_box->table_cell_coordinates()->column_index + cell_box->table_cell_coordinates()->column_span == column_count) if (cell_box->table_cell_coordinates()->column_index + cell_box->table_cell_coordinates()->column_span == column_count)
border_edge_painting_info_list.append(make_last_column_right_cell_edge(context, cell_rect, borders_data)); border_edge_painting_info_list.append(make_last_column_right_cell_edge(context, cell_rect, borders_data,
{ .row_index = cell_box->table_cell_coordinates()->row_index, .column_index = column_count - 1 }));
} }
paint_collected_edges(context, border_edge_painting_info_list); paint_collected_edges(context, border_edge_painting_info_list);
@ -232,7 +311,7 @@ void paint_table_collapsed_borders(PaintContext& context, Layout::Node const& bo
if (!top_left && !top_right && !bottom_left && !bottom_right) { if (!top_left && !top_right && !bottom_left && !bottom_right) {
continue; continue;
} else { } else {
auto borders_data = cell_box->override_borders_data().has_value() ? cell_box->override_borders_data().value() : BordersData { auto borders_data = cell_box->override_borders_data().has_value() ? PaintableBox::remove_element_kind_from_borders_data(cell_box->override_borders_data().value()) : BordersData {
.top = cell_box->box_model().border.top == 0 ? CSS::BorderData() : cell_box->computed_values().border_top(), .top = cell_box->box_model().border.top == 0 ? CSS::BorderData() : cell_box->computed_values().border_top(),
.right = cell_box->box_model().border.right == 0 ? CSS::BorderData() : cell_box->computed_values().border_right(), .right = cell_box->box_model().border.right == 0 ? CSS::BorderData() : cell_box->computed_values().border_right(),
.bottom = cell_box->box_model().border.bottom == 0 ? CSS::BorderData() : cell_box->computed_values().border_bottom(), .bottom = cell_box->box_model().border.bottom == 0 ? CSS::BorderData() : cell_box->computed_values().border_bottom(),