Coverage Report

Created: 2025-11-16 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <AK/Forward.h>
10
#include <LibWeb/Layout/FormattingContext.h>
11
#include <LibWeb/Layout/TableGrid.h>
12
#include <LibWeb/Layout/TableWrapper.h>
13
14
namespace Web::Layout {
15
16
enum class TableDimension {
17
    Row,
18
    Column
19
};
20
21
class TableFormattingContext final : public FormattingContext {
22
public:
23
    explicit TableFormattingContext(LayoutState&, LayoutMode, Box const&, FormattingContext* parent);
24
    ~TableFormattingContext();
25
26
    void run_until_width_calculation(AvailableSpace const& available_space);
27
28
    virtual void run(AvailableSpace const&) override;
29
    virtual CSSPixels automatic_content_width() const override;
30
    virtual CSSPixels automatic_content_height() const override;
31
32
0
    Box const& table_box() const { return context_box(); }
33
    TableWrapper const& table_wrapper() const
34
0
    {
35
0
        return verify_cast<TableWrapper>(*table_box().containing_block());
36
0
    }
37
38
    static bool border_is_less_specific(const CSS::BorderData& a, const CSS::BorderData& b);
39
40
private:
41
    CSSPixels run_caption_layout(CSS::CaptionSide);
42
    CSSPixels compute_capmin();
43
    void compute_constrainedness();
44
    void compute_cell_measures();
45
    void compute_outer_content_sizes();
46
    template<class RowOrColumn>
47
    void initialize_table_measures();
48
    template<class RowOrColumn>
49
    void compute_table_measures();
50
    template<class RowOrColumn>
51
    void compute_intrinsic_percentage(size_t max_cell_span);
52
    void compute_table_width();
53
    void distribute_width_to_columns();
54
    void distribute_excess_width_to_columns(CSSPixels available_width);
55
    void distribute_excess_width_to_columns_fixed_mode(CSSPixels excess_width);
56
    void compute_table_height();
57
    void distribute_height_to_rows();
58
    void position_row_boxes();
59
    void position_cell_boxes();
60
    void border_conflict_resolution();
61
    CSSPixels border_spacing_horizontal() const;
62
    CSSPixels border_spacing_vertical() const;
63
    void finish_grid_initialization(TableGrid const&);
64
65
    CSSPixels compute_columns_total_used_width() const;
66
    void commit_candidate_column_widths(Vector<CSSPixels> const& candidate_widths);
67
    void assign_columns_width_linear_combination(Vector<CSSPixels> const& candidate_widths, CSSPixels available_width);
68
69
    template<class ColumnFilter, class BaseWidthGetter>
70
    bool distribute_excess_width_proportionally_to_base_width(CSSPixels excess_width, ColumnFilter column_filter, BaseWidthGetter base_width_getter);
71
    template<class ColumnFilter>
72
    bool distribute_excess_width_equally(CSSPixels excess_width, ColumnFilter column_filter);
73
    template<class ColumnFilter>
74
    bool distribute_excess_width_by_intrinsic_percentage(CSSPixels excess_width, ColumnFilter column_filter);
75
76
    bool use_fixed_mode_layout() const;
77
78
    CSSPixels m_table_height { 0 };
79
    CSSPixels m_automatic_content_height { 0 };
80
81
    Optional<AvailableSpace> m_available_space;
82
83
    struct Column {
84
        CSSPixels left_offset { 0 };
85
        CSSPixels min_size { 0 };
86
        CSSPixels max_size { 0 };
87
        CSSPixels used_width { 0 };
88
        bool has_intrinsic_percentage { false };
89
        double intrinsic_percentage { 0 };
90
        // Store whether the column is constrained: https://www.w3.org/TR/css-tables-3/#constrainedness
91
        bool is_constrained { false };
92
        // Store whether the column has originating cells, defined in https://www.w3.org/TR/css-tables-3/#terminology.
93
        bool has_originating_cells { false };
94
    };
95
96
    using Cell = TableGrid::Cell;
97
    using Row = TableGrid::Row;
98
99
    // Accessors to enable direction-agnostic table measurement.
100
101
    template<class RowOrColumn>
102
    static size_t cell_span(Cell const& cell);
103
104
    template<class RowOrColumn>
105
    static size_t cell_index(Cell const& cell);
106
107
    template<class RowOrColumn>
108
    static CSSPixels cell_min_size(Cell const& cell);
109
110
    template<class RowOrColumn>
111
    static CSSPixels cell_max_size(Cell const& cell);
112
113
    template<class RowOrColumn>
114
    static double cell_percentage_contribution(Cell const& cell);
115
116
    template<class RowOrColumn>
117
    static bool cell_has_intrinsic_percentage(Cell const& cell);
118
119
    template<class RowOrColumn>
120
    void initialize_intrinsic_percentages_from_rows_or_columns();
121
122
    template<class RowOrColumn>
123
    void initialize_intrinsic_percentages_from_cells();
124
125
    template<class RowOrColumn>
126
    CSSPixels border_spacing();
127
128
    template<class RowOrColumn>
129
    Vector<RowOrColumn>& table_rows_or_columns();
130
131
    CSSPixels compute_row_content_height(Cell const& cell) const;
132
133
    enum class ConflictingSide {
134
        Top,
135
        Bottom,
136
        Left,
137
        Right,
138
    };
139
140
    struct ConflictingEdge {
141
        JS::GCPtr<Node const> element;
142
        Painting::PaintableBox::ConflictingElementKind element_kind;
143
        ConflictingSide side;
144
        Optional<size_t> row;
145
        Optional<size_t> column;
146
    };
147
148
    static TableFormattingContext::ConflictingEdge const& winning_conflicting_edge(TableFormattingContext::ConflictingEdge const& a, TableFormattingContext::ConflictingEdge const& b);
149
150
    static const CSS::BorderData& border_data_conflicting_edge(ConflictingEdge const& conflicting_edge);
151
    static Painting::PaintableBox::BorderDataWithElementKind const border_data_with_element_kind_from_conflicting_edge(ConflictingEdge const& conflicting_edge);
152
153
    class BorderConflictFinder {
154
    public:
155
        BorderConflictFinder(TableFormattingContext const* context);
156
        Vector<ConflictingEdge> conflicting_edges(Cell const&, ConflictingSide) const;
157
158
    private:
159
        void collect_conflicting_col_elements();
160
        void collect_conflicting_row_group_elements();
161
162
        void collect_cell_conflicting_edges(Vector<ConflictingEdge>&, Cell const&, ConflictingSide) const;
163
        void collect_row_conflicting_edges(Vector<ConflictingEdge>&, Cell const&, ConflictingSide) const;
164
        void collect_row_group_conflicting_edges(Vector<ConflictingEdge>&, Cell const&, ConflictingSide) const;
165
        void collect_column_group_conflicting_edges(Vector<ConflictingEdge>&, Cell const&, ConflictingSide) const;
166
        void collect_table_box_conflicting_edges(Vector<ConflictingEdge>&, Cell const&, ConflictingSide) const;
167
168
        struct RowGroupInfo {
169
            JS::GCPtr<Node const> row_group;
170
            size_t start_index;
171
            size_t row_count;
172
        };
173
174
        Vector<JS::GCPtr<Node const>> m_col_elements_by_index;
175
        Vector<Optional<RowGroupInfo>> m_row_group_elements_by_index;
176
        TableFormattingContext const* m_context;
177
    };
178
179
    Vector<Cell> m_cells;
180
    Vector<Vector<Optional<Cell const&>>> m_cells_by_coordinate;
181
    Vector<Column> m_columns;
182
    Vector<Row> m_rows;
183
};
184
185
}