Coverage Report

Created: 2026-02-16 07:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/Painting/TableBordersPainting.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2023, the SerenityOS developers.
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <AK/HashMap.h>
8
#include <AK/QuickSort.h>
9
#include <AK/Traits.h>
10
#include <LibWeb/Layout/TableFormattingContext.h>
11
#include <LibWeb/Painting/PaintableBox.h>
12
#include <LibWeb/Painting/TableBordersPainting.h>
13
14
struct CellCoordinates {
15
    size_t row_index;
16
    size_t column_index;
17
18
    bool operator==(CellCoordinates const& other) const
19
0
    {
20
0
        return row_index == other.row_index && column_index == other.column_index;
21
0
    }
22
};
23
24
namespace AK {
25
template<>
26
struct Traits<CellCoordinates> : public DefaultTraits<CellCoordinates> {
27
0
    static unsigned hash(CellCoordinates const& key) { return pair_int_hash(key.row_index, key.column_index); }
28
};
29
}
30
31
namespace Web::Painting {
32
33
static void collect_cell_boxes(Vector<PaintableBox const&>& cell_boxes, PaintableBox const& table_paintable)
34
0
{
35
0
    table_paintable.for_each_child_of_type<PaintableBox>([&](auto& child) {
36
0
        if (child.display().is_table_cell()) {
37
0
            cell_boxes.append(child);
38
0
        } else {
39
0
            collect_cell_boxes(cell_boxes, child);
40
0
        }
41
0
        return IterationDecision::Continue;
42
0
    });
43
0
}
44
45
enum class EdgeDirection {
46
    Horizontal,
47
    Vertical,
48
};
49
50
struct DeviceBorderData {
51
    Color color { Color::Transparent };
52
    CSS::LineStyle line_style { CSS::LineStyle::None };
53
    DevicePixels width { 0 };
54
};
55
56
struct DeviceBorderDataWithElementKind {
57
    DeviceBorderData border_data;
58
    Painting::PaintableBox::ConflictingElementKind element_kind { Painting::PaintableBox::ConflictingElementKind::Cell };
59
};
60
61
struct DeviceBordersDataWithElementKind {
62
    DeviceBorderDataWithElementKind top;
63
    DeviceBorderDataWithElementKind right;
64
    DeviceBorderDataWithElementKind bottom;
65
    DeviceBorderDataWithElementKind left;
66
};
67
68
struct BorderEdgePaintingInfo {
69
    DevicePixelRect rect;
70
    DeviceBorderDataWithElementKind border_data_with_element_kind;
71
    EdgeDirection direction;
72
    Optional<size_t> row;
73
    Optional<size_t> column;
74
};
75
76
static Optional<size_t> row_index_for_element_kind(size_t index, Painting::PaintableBox::ConflictingElementKind element_kind)
77
0
{
78
0
    switch (element_kind) {
79
0
    case Painting::PaintableBox::ConflictingElementKind::Cell:
80
0
    case Painting::PaintableBox::ConflictingElementKind::Row:
81
0
    case Painting::PaintableBox::ConflictingElementKind::RowGroup: {
82
0
        return index;
83
0
    }
84
0
    default:
85
0
        return {};
86
0
    }
87
0
}
88
89
static Optional<size_t> column_index_for_element_kind(size_t index, Painting::PaintableBox::ConflictingElementKind element_kind)
90
0
{
91
0
    switch (element_kind) {
92
0
    case Painting::PaintableBox::ConflictingElementKind::Cell:
93
0
    case Painting::PaintableBox::ConflictingElementKind::Column:
94
0
    case Painting::PaintableBox::ConflictingElementKind::ColumnGroup: {
95
0
        return index;
96
0
    }
97
0
    default:
98
0
        return {};
99
0
    }
100
0
}
101
102
static DevicePixels half_ceil(DevicePixels width)
103
0
{
104
0
    return ceil(static_cast<double>(width.value()) / 2);
105
0
}
106
107
static DevicePixels half_floor(DevicePixels width)
108
0
{
109
0
    return floor(static_cast<double>(width.value()) / 2);
110
0
}
111
112
static BorderEdgePaintingInfo make_right_cell_edge(
113
    DevicePixelRect const& right_cell_rect,
114
    DevicePixelRect const& cell_rect,
115
    DeviceBordersDataWithElementKind const& borders_data,
116
    CellCoordinates const& coordinates)
117
0
{
118
0
    auto connect_top_offset = half_ceil(borders_data.top.border_data.width);
119
0
    auto connect_excess_height = connect_top_offset + half_floor(borders_data.bottom.border_data.width);
120
0
    DevicePixelRect right_border_rect = {
121
0
        right_cell_rect.x() - half_ceil(borders_data.right.border_data.width),
122
0
        cell_rect.y() - connect_top_offset,
123
0
        borders_data.right.border_data.width,
124
0
        max(cell_rect.height(), right_cell_rect.height()) + connect_excess_height,
125
0
    };
126
0
    return BorderEdgePaintingInfo {
127
0
        .rect = right_border_rect,
128
0
        .border_data_with_element_kind = borders_data.right,
129
0
        .direction = EdgeDirection::Vertical,
130
0
        .row = row_index_for_element_kind(coordinates.row_index, borders_data.right.element_kind),
131
0
        .column = column_index_for_element_kind(coordinates.column_index, borders_data.right.element_kind),
132
0
    };
133
0
}
134
135
static BorderEdgePaintingInfo make_down_cell_edge(
136
    DevicePixelRect const& down_cell_rect,
137
    DevicePixelRect const& cell_rect,
138
    DeviceBordersDataWithElementKind const& borders_data,
139
    CellCoordinates const& coordinates)
140
0
{
141
0
    auto connect_left_offset = half_ceil(borders_data.left.border_data.width);
142
0
    auto connect_excess_width = connect_left_offset + half_floor(borders_data.right.border_data.width);
143
0
    DevicePixelRect down_border_rect = {
144
0
        cell_rect.x() - connect_left_offset,
145
0
        down_cell_rect.y() - half_ceil(borders_data.bottom.border_data.width),
146
0
        max(cell_rect.width(), down_cell_rect.width()) + connect_excess_width,
147
0
        borders_data.bottom.border_data.width,
148
0
    };
149
0
    return BorderEdgePaintingInfo {
150
0
        .rect = down_border_rect,
151
0
        .border_data_with_element_kind = borders_data.bottom,
152
0
        .direction = EdgeDirection::Horizontal,
153
0
        .row = row_index_for_element_kind(coordinates.row_index, borders_data.bottom.element_kind),
154
0
        .column = column_index_for_element_kind(coordinates.column_index, borders_data.bottom.element_kind),
155
0
    };
156
0
}
157
158
static BorderEdgePaintingInfo make_first_row_top_cell_edge(DevicePixelRect const& cell_rect, DeviceBordersDataWithElementKind const& borders_data, CellCoordinates const& coordinates)
159
0
{
160
0
    auto connect_left_offset = half_ceil(borders_data.left.border_data.width.value());
161
0
    auto connect_excess_width = connect_left_offset + half_floor(borders_data.right.border_data.width.value());
162
0
    DevicePixelRect top_border_rect = {
163
0
        cell_rect.x() - connect_left_offset,
164
0
        cell_rect.y() - half_ceil(borders_data.top.border_data.width.value()),
165
0
        cell_rect.width() + connect_excess_width,
166
0
        borders_data.top.border_data.width,
167
0
    };
168
0
    return BorderEdgePaintingInfo {
169
0
        .rect = top_border_rect,
170
0
        .border_data_with_element_kind = borders_data.top,
171
0
        .direction = EdgeDirection::Horizontal,
172
0
        .row = row_index_for_element_kind(coordinates.row_index, borders_data.top.element_kind),
173
0
        .column = column_index_for_element_kind(coordinates.column_index, borders_data.top.element_kind),
174
0
    };
175
0
}
176
177
static BorderEdgePaintingInfo make_last_row_bottom_cell_edge(DevicePixelRect const& cell_rect, DeviceBordersDataWithElementKind const& borders_data, CellCoordinates const& coordinates)
178
0
{
179
0
    auto connect_left_offset = half_ceil(borders_data.left.border_data.width);
180
0
    auto connect_excess_width = connect_left_offset + half_floor(borders_data.right.border_data.width);
181
0
    DevicePixelRect bottom_border_rect = {
182
0
        cell_rect.x() - connect_left_offset,
183
0
        cell_rect.y() + cell_rect.height() - half_ceil(borders_data.bottom.border_data.width),
184
0
        cell_rect.width() + connect_excess_width,
185
0
        borders_data.bottom.border_data.width,
186
0
    };
187
0
    return BorderEdgePaintingInfo {
188
0
        .rect = bottom_border_rect,
189
0
        .border_data_with_element_kind = borders_data.bottom,
190
0
        .direction = EdgeDirection::Horizontal,
191
0
        .row = row_index_for_element_kind(coordinates.row_index, borders_data.bottom.element_kind),
192
0
        .column = column_index_for_element_kind(coordinates.column_index, borders_data.bottom.element_kind),
193
0
    };
194
0
}
195
196
static BorderEdgePaintingInfo make_first_column_left_cell_edge(DevicePixelRect const& cell_rect, DeviceBordersDataWithElementKind const& borders_data, CellCoordinates const& coordinates)
197
0
{
198
0
    auto connect_top_offset = half_ceil(borders_data.top.border_data.width);
199
0
    auto connect_excess_height = connect_top_offset + half_floor(borders_data.bottom.border_data.width);
200
0
    DevicePixelRect left_border_rect = {
201
0
        cell_rect.x() - half_ceil(borders_data.left.border_data.width),
202
0
        cell_rect.y() - connect_top_offset,
203
0
        borders_data.left.border_data.width,
204
0
        cell_rect.height() + connect_excess_height,
205
0
    };
206
0
    return BorderEdgePaintingInfo {
207
0
        .rect = left_border_rect,
208
0
        .border_data_with_element_kind = borders_data.left,
209
0
        .direction = EdgeDirection::Vertical,
210
0
        .row = row_index_for_element_kind(coordinates.row_index, borders_data.left.element_kind),
211
0
        .column = column_index_for_element_kind(coordinates.column_index, borders_data.left.element_kind),
212
0
    };
213
0
}
214
215
static BorderEdgePaintingInfo make_last_column_right_cell_edge(DevicePixelRect const& cell_rect, DeviceBordersDataWithElementKind const& borders_data, CellCoordinates const& coordinates)
216
0
{
217
0
    auto connect_top_offset = half_ceil(borders_data.top.border_data.width);
218
0
    auto connect_excess_height = connect_top_offset + half_floor(borders_data.bottom.border_data.width);
219
0
    DevicePixelRect right_border_rect = {
220
0
        cell_rect.x() + cell_rect.width() - half_ceil(borders_data.right.border_data.width),
221
0
        cell_rect.y() - connect_top_offset,
222
0
        borders_data.right.border_data.width,
223
0
        cell_rect.height() + connect_excess_height,
224
0
    };
225
0
    return BorderEdgePaintingInfo {
226
0
        .rect = right_border_rect,
227
0
        .border_data_with_element_kind = borders_data.right,
228
0
        .direction = EdgeDirection::Vertical,
229
0
        .row = row_index_for_element_kind(coordinates.row_index, borders_data.right.element_kind),
230
0
        .column = column_index_for_element_kind(coordinates.column_index, borders_data.right.element_kind),
231
0
    };
232
0
}
233
234
static CSS::BorderData css_border_data_from_device_border_data(DeviceBorderData const& device_border_data)
235
0
{
236
0
    return CSS::BorderData {
237
0
        .color = device_border_data.color,
238
0
        .line_style = device_border_data.line_style,
239
0
        .width = device_border_data.width.value(),
240
0
    };
241
0
}
242
243
static void paint_collected_edges(PaintContext& context, Vector<BorderEdgePaintingInfo>& border_edge_painting_info_list)
244
0
{
245
    // This sorting step isn't part of the specification, but it matches the behavior of other browsers at border intersections, which aren't
246
    // part of border conflict resolution in the specification but it's still desirable to handle them in a way which is consistent with it.
247
    // See https://www.w3.org/TR/CSS22/tables.html#border-conflict-resolution for reference.
248
0
    quick_sort(border_edge_painting_info_list, [](auto const& a, auto const& b) {
249
0
        auto const& a_border_data = a.border_data_with_element_kind.border_data;
250
0
        auto const& b_border_data = b.border_data_with_element_kind.border_data;
251
0
        if (a_border_data.line_style == b_border_data.line_style && a_border_data.width == b_border_data.width) {
252
0
            if (b.border_data_with_element_kind.element_kind < a.border_data_with_element_kind.element_kind) {
253
0
                return true;
254
0
            } else if (b.border_data_with_element_kind.element_kind > a.border_data_with_element_kind.element_kind) {
255
0
                return false;
256
0
            }
257
            // Here the element kind is the same, thus the coordinates are either both set or not set.
258
0
            VERIFY(a.column.has_value() == b.column.has_value());
259
0
            VERIFY(a.row.has_value() == b.row.has_value());
260
0
            if (a.column.has_value()) {
261
0
                if (b.column.value() < a.column.value()) {
262
0
                    return true;
263
0
                } else if (b.column.value() > a.column.value()) {
264
0
                    return false;
265
0
                }
266
0
            }
267
0
            return a.row.has_value() ? b.row.value() < a.row.value() : false;
268
0
        }
269
0
        return Layout::TableFormattingContext::border_is_less_specific(
270
0
            css_border_data_from_device_border_data(a_border_data),
271
0
            css_border_data_from_device_border_data(b_border_data));
272
0
    });
273
274
0
    for (auto const& border_edge_painting_info : border_edge_painting_info_list) {
275
0
        auto const& border_data_with_element_kind = border_edge_painting_info.border_data_with_element_kind;
276
0
        auto width = border_data_with_element_kind.border_data.width;
277
0
        if (width <= 0)
278
0
            continue;
279
0
        auto color = border_data_with_element_kind.border_data.color;
280
0
        auto border_style = border_data_with_element_kind.border_data.line_style;
281
0
        auto p1 = border_edge_painting_info.rect.top_left();
282
0
        auto p2 = border_edge_painting_info.direction == EdgeDirection::Horizontal
283
0
            ? border_edge_painting_info.rect.top_right()
284
0
            : border_edge_painting_info.rect.bottom_left();
285
286
0
        if (border_style == CSS::LineStyle::Dotted) {
287
0
            context.display_list_recorder().draw_line(p1.to_type<int>(), p2.to_type<int>(), color, width.value(), Gfx::LineStyle::Dotted);
288
0
        } else if (border_style == CSS::LineStyle::Dashed) {
289
0
            context.display_list_recorder().draw_line(p1.to_type<int>(), p2.to_type<int>(), color, width.value(), Gfx::LineStyle::Dashed);
290
0
        } else {
291
            // FIXME: Support the remaining line styles instead of rendering them as solid.
292
0
            context.display_list_recorder().fill_rect(Gfx::IntRect(border_edge_painting_info.rect.location(), border_edge_painting_info.rect.size()), color);
293
0
        }
294
0
    }
295
0
}
296
297
static HashMap<CellCoordinates, DevicePixelRect> snap_cells_to_device_coordinates(HashMap<CellCoordinates, PaintableBox const*> const& cell_coordinates_to_box, size_t row_count, size_t column_count, PaintContext const& context)
298
0
{
299
0
    Vector<DevicePixels> y_line_start_coordinates;
300
0
    Vector<DevicePixels> y_line_end_coordinates;
301
0
    y_line_start_coordinates.resize(row_count + 1);
302
0
    y_line_end_coordinates.resize(row_count + 1);
303
0
    Vector<DevicePixels> x_line_start_coordinates;
304
0
    Vector<DevicePixels> x_line_end_coordinates;
305
0
    x_line_start_coordinates.resize(column_count + 1);
306
0
    x_line_end_coordinates.resize(column_count + 1);
307
0
    for (auto const& kv : cell_coordinates_to_box) {
308
0
        auto const& cell_box = kv.value;
309
0
        auto start_row_index = cell_box->table_cell_coordinates()->row_index;
310
0
        auto end_row_index = start_row_index + cell_box->table_cell_coordinates()->row_span;
311
0
        auto cell_rect = cell_box->absolute_border_box_rect();
312
0
        y_line_start_coordinates[start_row_index] = max(context.rounded_device_pixels(cell_rect.y()), y_line_start_coordinates[start_row_index]);
313
0
        y_line_end_coordinates[end_row_index] = max(context.rounded_device_pixels(cell_rect.y() + cell_rect.height()), y_line_end_coordinates[end_row_index]);
314
0
        auto start_column_index = cell_box->table_cell_coordinates()->column_index;
315
0
        auto end_column_index = start_column_index + cell_box->table_cell_coordinates()->column_span;
316
0
        x_line_start_coordinates[start_column_index] = max(context.rounded_device_pixels(cell_rect.x()), x_line_start_coordinates[start_column_index]);
317
0
        x_line_end_coordinates[end_column_index] = max(context.rounded_device_pixels(cell_rect.x() + cell_rect.width()), x_line_end_coordinates[end_column_index]);
318
0
    }
319
0
    HashMap<CellCoordinates, DevicePixelRect> cell_coordinates_to_device_rect;
320
0
    for (auto const& kv : cell_coordinates_to_box) {
321
0
        auto const& cell_box = kv.value;
322
0
        auto start_row_index = cell_box->table_cell_coordinates()->row_index;
323
0
        auto end_row_index = start_row_index + cell_box->table_cell_coordinates()->row_span;
324
0
        auto height = y_line_end_coordinates[end_row_index] - y_line_start_coordinates[start_row_index];
325
0
        auto start_column_index = cell_box->table_cell_coordinates()->column_index;
326
0
        auto end_column_index = start_column_index + cell_box->table_cell_coordinates()->column_span;
327
0
        auto width = x_line_end_coordinates[end_column_index] - x_line_start_coordinates[start_column_index];
328
0
        cell_coordinates_to_device_rect.set(kv.key, DevicePixelRect { x_line_start_coordinates[start_column_index], y_line_start_coordinates[start_row_index], width, height });
329
0
    }
330
0
    return cell_coordinates_to_device_rect;
331
0
}
332
333
static DeviceBorderDataWithElementKind device_border_data_from_css_border_data(Painting::PaintableBox::BorderDataWithElementKind const& border_data_with_element_kind, PaintContext const& context)
334
0
{
335
0
    return DeviceBorderDataWithElementKind {
336
0
        .border_data = {
337
0
            .color = border_data_with_element_kind.border_data.color,
338
0
            .line_style = border_data_with_element_kind.border_data.line_style,
339
0
            .width = context.rounded_device_pixels(border_data_with_element_kind.border_data.width),
340
0
        },
341
0
        .element_kind = border_data_with_element_kind.element_kind,
342
0
    };
343
0
}
344
345
static void paint_separate_cell_borders(PaintableBox const& cell_box, HashMap<CellCoordinates, DevicePixelRect> const& cell_coordinates_to_device_rect, PaintContext& context)
346
0
{
347
0
    auto borders_data = cell_box.override_borders_data().has_value() ? PaintableBox::remove_element_kind_from_borders_data(cell_box.override_borders_data().value()) : BordersData {
348
0
        .top = cell_box.box_model().border.top == 0 ? CSS::BorderData() : cell_box.computed_values().border_top(),
349
0
        .right = cell_box.box_model().border.right == 0 ? CSS::BorderData() : cell_box.computed_values().border_right(),
350
0
        .bottom = cell_box.box_model().border.bottom == 0 ? CSS::BorderData() : cell_box.computed_values().border_bottom(),
351
0
        .left = cell_box.box_model().border.left == 0 ? CSS::BorderData() : cell_box.computed_values().border_left(),
352
0
    };
353
0
    auto cell_rect = cell_coordinates_to_device_rect.get({ cell_box.table_cell_coordinates()->row_index, cell_box.table_cell_coordinates()->column_index }).value();
354
0
    paint_all_borders(context.display_list_recorder(), cell_rect, cell_box.normalized_border_radii_data().as_corners(context), borders_data.to_device_pixels(context));
355
0
}
356
357
void paint_table_borders(PaintContext& context, PaintableBox const& table_paintable)
358
0
{
359
    // Partial implementation of painting according to the collapsing border model:
360
    // https://www.w3.org/TR/CSS22/tables.html#collapsing-borders
361
0
    Vector<PaintableBox const&> cell_boxes;
362
0
    collect_cell_boxes(cell_boxes, table_paintable);
363
0
    Vector<BorderEdgePaintingInfo> border_edge_painting_info_list;
364
0
    HashMap<CellCoordinates, PaintableBox const*> cell_coordinates_to_box;
365
0
    size_t row_count = 0;
366
0
    size_t column_count = 0;
367
0
    for (auto const& cell_box : cell_boxes) {
368
0
        cell_coordinates_to_box.set(CellCoordinates {
369
0
                                        .row_index = cell_box.table_cell_coordinates()->row_index,
370
0
                                        .column_index = cell_box.table_cell_coordinates()->column_index },
371
0
            &cell_box);
372
0
        row_count = max(row_count, cell_box.table_cell_coordinates()->row_index + cell_box.table_cell_coordinates()->row_span);
373
0
        column_count = max(column_count, cell_box.table_cell_coordinates()->column_index + cell_box.table_cell_coordinates()->column_span);
374
0
    }
375
0
    auto cell_coordinates_to_device_rect = snap_cells_to_device_coordinates(cell_coordinates_to_box, row_count, column_count, context);
376
0
    for (auto const& cell_box : cell_boxes) {
377
0
        if (cell_box.computed_values().border_collapse() == CSS::BorderCollapse::Separate) {
378
0
            paint_separate_cell_borders(cell_box, cell_coordinates_to_device_rect, context);
379
0
            continue;
380
0
        }
381
0
        auto css_borders_data = cell_box.override_borders_data().has_value() ? cell_box.override_borders_data().value() : PaintableBox::BordersDataWithElementKind {
382
0
            .top = { .border_data = cell_box.box_model().border.top == 0 ? CSS::BorderData() : cell_box.computed_values().border_top(), .element_kind = PaintableBox::ConflictingElementKind::Cell },
383
0
            .right = { .border_data = cell_box.box_model().border.right == 0 ? CSS::BorderData() : cell_box.computed_values().border_right(), .element_kind = PaintableBox::ConflictingElementKind::Cell },
384
0
            .bottom = { .border_data = cell_box.box_model().border.bottom == 0 ? CSS::BorderData() : cell_box.computed_values().border_bottom(), .element_kind = PaintableBox::ConflictingElementKind::Cell },
385
0
            .left = { .border_data = cell_box.box_model().border.left == 0 ? CSS::BorderData() : cell_box.computed_values().border_left(), .element_kind = PaintableBox::ConflictingElementKind::Cell },
386
0
        };
387
0
        DeviceBordersDataWithElementKind borders_data = {
388
0
            .top = device_border_data_from_css_border_data(css_borders_data.top, context),
389
0
            .right = device_border_data_from_css_border_data(css_borders_data.right, context),
390
0
            .bottom = device_border_data_from_css_border_data(css_borders_data.bottom, context),
391
0
            .left = device_border_data_from_css_border_data(css_borders_data.left, context),
392
0
        };
393
0
        auto cell_rect = cell_coordinates_to_device_rect.get({ cell_box.table_cell_coordinates()->row_index, cell_box.table_cell_coordinates()->column_index }).value();
394
0
        CellCoordinates right_cell_coordinates {
395
0
            .row_index = cell_box.table_cell_coordinates()->row_index,
396
0
            .column_index = cell_box.table_cell_coordinates()->column_index + cell_box.table_cell_coordinates()->column_span
397
0
        };
398
0
        auto maybe_right_cell = cell_coordinates_to_device_rect.get(right_cell_coordinates);
399
0
        CellCoordinates down_cell_coordinates {
400
0
            .row_index = cell_box.table_cell_coordinates()->row_index + cell_box.table_cell_coordinates()->row_span,
401
0
            .column_index = cell_box.table_cell_coordinates()->column_index
402
0
        };
403
0
        auto maybe_down_cell = cell_coordinates_to_device_rect.get(down_cell_coordinates);
404
0
        if (maybe_right_cell.has_value())
405
0
            border_edge_painting_info_list.append(make_right_cell_edge(maybe_right_cell.value(), cell_rect, borders_data, right_cell_coordinates));
406
0
        if (maybe_down_cell.has_value())
407
0
            border_edge_painting_info_list.append(make_down_cell_edge(maybe_down_cell.value(), cell_rect, borders_data, down_cell_coordinates));
408
0
        if (cell_box.table_cell_coordinates()->row_index == 0)
409
0
            border_edge_painting_info_list.append(make_first_row_top_cell_edge(cell_rect, borders_data,
410
0
                { .row_index = 0, .column_index = cell_box.table_cell_coordinates()->column_index }));
411
0
        if (cell_box.table_cell_coordinates()->row_index + cell_box.table_cell_coordinates()->row_span == row_count)
412
0
            border_edge_painting_info_list.append(make_last_row_bottom_cell_edge(cell_rect, borders_data,
413
0
                { .row_index = row_count - 1, .column_index = cell_box.table_cell_coordinates()->column_index }));
414
0
        if (cell_box.table_cell_coordinates()->column_index == 0)
415
0
            border_edge_painting_info_list.append(make_first_column_left_cell_edge(cell_rect, borders_data,
416
0
                { .row_index = cell_box.table_cell_coordinates()->row_index, .column_index = 0 }));
417
0
        if (cell_box.table_cell_coordinates()->column_index + cell_box.table_cell_coordinates()->column_span == column_count)
418
0
            border_edge_painting_info_list.append(make_last_column_right_cell_edge(cell_rect, borders_data,
419
0
                { .row_index = cell_box.table_cell_coordinates()->row_index, .column_index = column_count - 1 }));
420
0
    }
421
422
0
    paint_collected_edges(context, border_edge_painting_info_list);
423
424
0
    for (auto const& cell_box : cell_boxes) {
425
0
        auto const& border_radii_data = cell_box.normalized_border_radii_data();
426
0
        auto top_left = border_radii_data.top_left.as_corner(context);
427
0
        auto top_right = border_radii_data.top_right.as_corner(context);
428
0
        auto bottom_right = border_radii_data.bottom_right.as_corner(context);
429
0
        auto bottom_left = border_radii_data.bottom_left.as_corner(context);
430
0
        if (!top_left && !top_right && !bottom_left && !bottom_right) {
431
0
            continue;
432
0
        } else {
433
0
            auto borders_data = cell_box.override_borders_data().has_value() ? PaintableBox::remove_element_kind_from_borders_data(cell_box.override_borders_data().value()) : BordersData {
434
0
                .top = cell_box.box_model().border.top == 0 ? CSS::BorderData() : cell_box.computed_values().border_top(),
435
0
                .right = cell_box.box_model().border.right == 0 ? CSS::BorderData() : cell_box.computed_values().border_right(),
436
0
                .bottom = cell_box.box_model().border.bottom == 0 ? CSS::BorderData() : cell_box.computed_values().border_bottom(),
437
0
                .left = cell_box.box_model().border.left == 0 ? CSS::BorderData() : cell_box.computed_values().border_left(),
438
0
            };
439
0
            paint_all_borders(context.display_list_recorder(), context.rounded_device_rect(cell_box.absolute_border_box_rect()), cell_box.normalized_border_radii_data().as_corners(context), borders_data.to_device_pixels(context));
440
0
        }
441
0
    }
442
0
}
443
}