Coverage Report

Created: 2025-12-18 07:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2021-2024, Andreas Kling <kling@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <LibWeb/Layout/Box.h>
10
#include <LibWeb/Layout/FormattingContext.h>
11
12
namespace Web::Layout {
13
14
class FlexFormattingContext final : public FormattingContext {
15
public:
16
    FlexFormattingContext(LayoutState&, LayoutMode, Box const& flex_container, FormattingContext* parent);
17
    ~FlexFormattingContext();
18
19
0
    virtual bool inhibits_floating() const override { return true; }
20
21
    virtual void run(AvailableSpace const&) override;
22
    virtual CSSPixels automatic_content_width() const override;
23
    virtual CSSPixels automatic_content_height() const override;
24
25
0
    Box const& flex_container() const { return context_box(); }
26
27
    virtual CSSPixelPoint calculate_static_position(Box const&) const override;
28
29
private:
30
    [[nodiscard]] bool should_treat_main_size_as_auto(Box const&) const;
31
    [[nodiscard]] bool should_treat_cross_size_as_auto(Box const&) const;
32
33
    [[nodiscard]] bool should_treat_main_max_size_as_none(Box const&) const;
34
    [[nodiscard]] bool should_treat_cross_max_size_as_none(Box const&) const;
35
36
    [[nodiscard]] CSSPixels adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints(Box const&, CSSPixels main_size, CSS::Size const& min_cross_size, CSS::Size const& max_cross_size) const;
37
    [[nodiscard]] CSSPixels adjust_cross_size_through_aspect_ratio_for_main_size_min_max_constraints(Box const&, CSSPixels cross_size, CSS::Size const& min_main_size, CSS::Size const& max_main_size) const;
38
39
    [[nodiscard]] CSSPixels calculate_main_size_from_cross_size_and_aspect_ratio(CSSPixels cross_size, CSSPixelFraction aspect_ratio) const;
40
    [[nodiscard]] CSSPixels calculate_cross_size_from_main_size_and_aspect_ratio(CSSPixels main_size, CSSPixelFraction aspect_ratio) const;
41
42
    void dump_items() const;
43
44
    struct DirectionAgnosticMargins {
45
        CSSPixels main_before { 0 };
46
        CSSPixels main_after { 0 };
47
        CSSPixels cross_before { 0 };
48
        CSSPixels cross_after { 0 };
49
50
        bool main_before_is_auto { false };
51
        bool main_after_is_auto { false };
52
        bool cross_before_is_auto { false };
53
        bool cross_after_is_auto { false };
54
    };
55
56
    struct FlexItem {
57
        JS::NonnullGCPtr<Box> box;
58
        LayoutState::UsedValues& used_values;
59
        Optional<CSS::FlexBasis> used_flex_basis {};
60
        bool used_flex_basis_is_definite { false };
61
        CSSPixels flex_base_size { 0 };
62
        CSSPixels hypothetical_main_size { 0 };
63
        CSSPixels hypothetical_cross_size { 0 };
64
0
        CSSPixels hypothetical_cross_size_with_margins() { return hypothetical_cross_size + margins.cross_before + margins.cross_after + borders.cross_after + borders.cross_before + padding.cross_after + padding.cross_before; }
65
        CSSPixels target_main_size { 0 };
66
        bool frozen { false };
67
        Optional<double> flex_factor {};
68
        double scaled_flex_shrink_factor { 0 };
69
        double desired_flex_fraction { 0 };
70
71
        CSSPixels outer_hypothetical_main_size() const
72
0
        {
73
0
            return hypothetical_main_size + margins.main_before + margins.main_after + borders.main_before + borders.main_after + padding.main_before + padding.main_after;
74
0
        }
75
76
        CSSPixels outer_target_main_size() const
77
0
        {
78
0
            return target_main_size + margins.main_before + margins.main_after + borders.main_before + borders.main_after + padding.main_before + padding.main_after;
79
0
        }
80
81
        CSSPixels outer_flex_base_size() const
82
0
        {
83
0
            return flex_base_size + margins.main_before + margins.main_after + borders.main_before + borders.main_after + padding.main_before + padding.main_after;
84
0
        }
85
86
        // The used main size of this flex item. Empty until determined.
87
        Optional<CSSPixels> main_size {};
88
89
        // The used cross size of this flex item. Empty until determined.
90
        Optional<CSSPixels> cross_size {};
91
92
        CSSPixels main_offset { 0 };
93
        CSSPixels cross_offset { 0 };
94
        DirectionAgnosticMargins margins {};
95
        DirectionAgnosticMargins borders {};
96
        DirectionAgnosticMargins padding {};
97
        bool is_min_violation { false };
98
        bool is_max_violation { false };
99
100
        CSSPixels add_main_margin_box_sizes(CSSPixels content_size) const
101
0
        {
102
0
            return content_size + margins.main_before + margins.main_after + borders.main_before + borders.main_after + padding.main_before + padding.main_after;
103
0
        }
104
105
        CSSPixels add_cross_margin_box_sizes(CSSPixels content_size) const
106
0
        {
107
0
            return content_size + margins.cross_before + margins.cross_after + borders.cross_before + borders.cross_after + padding.cross_before + padding.cross_after;
108
0
        }
109
    };
110
111
    struct FlexLine {
112
        Vector<FlexItem&> items;
113
        CSSPixels cross_size { 0 };
114
        Optional<CSSPixels> remaining_free_space;
115
        double chosen_flex_fraction { 0 };
116
117
        double sum_of_flex_factor_of_unfrozen_items() const;
118
        double sum_of_scaled_flex_shrink_factor_of_unfrozen_items() const;
119
    };
120
121
    CSSPixels main_gap() const;
122
    CSSPixels cross_gap() const;
123
    [[nodiscard]] bool has_definite_main_size(LayoutState::UsedValues const&) const;
124
    [[nodiscard]] bool has_definite_cross_size(LayoutState::UsedValues const&) const;
125
0
    [[nodiscard]] bool has_definite_main_size(FlexItem const& item) const { return has_definite_main_size(item.used_values); }
126
0
    [[nodiscard]] bool has_definite_cross_size(FlexItem const& item) const { return has_definite_cross_size(item.used_values); }
127
128
    [[nodiscard]] CSSPixels inner_main_size(LayoutState::UsedValues const&) const;
129
    [[nodiscard]] CSSPixels inner_cross_size(LayoutState::UsedValues const&) const;
130
0
    [[nodiscard]] CSSPixels inner_main_size(FlexItem const& item) const { return inner_main_size(item.used_values); }
131
0
    [[nodiscard]] CSSPixels inner_cross_size(FlexItem const& item) const { return inner_cross_size(item.used_values); }
132
    bool has_main_min_size(Box const&) const;
133
    bool has_cross_min_size(Box const&) const;
134
    CSSPixels specified_main_max_size(Box const&) const;
135
    CSSPixels specified_cross_max_size(Box const&) const;
136
    bool is_cross_auto(Box const&) const;
137
    CSSPixels specified_main_min_size(Box const&) const;
138
    CSSPixels specified_cross_min_size(Box const&) const;
139
    bool has_main_max_size(Box const&) const;
140
    bool has_cross_max_size(Box const&) const;
141
    CSSPixels automatic_minimum_size(FlexItem const&) const;
142
    CSSPixels content_based_minimum_size(FlexItem const&) const;
143
    Optional<CSSPixels> specified_size_suggestion(FlexItem const&) const;
144
    Optional<CSSPixels> transferred_size_suggestion(FlexItem const&) const;
145
    CSSPixels content_size_suggestion(FlexItem const&) const;
146
    CSS::Size const& computed_main_size(Box const&) const;
147
    CSS::Size const& computed_main_min_size(Box const&) const;
148
    CSS::Size const& computed_main_max_size(Box const&) const;
149
    CSS::Size const& computed_cross_size(Box const&) const;
150
    CSS::Size const& computed_cross_min_size(Box const&) const;
151
    CSS::Size const& computed_cross_max_size(Box const&) const;
152
153
    CSSPixels get_pixel_width(Box const&, CSS::Size const&) const;
154
    CSSPixels get_pixel_height(Box const&, CSS::Size const&) const;
155
156
    bool flex_item_is_stretched(FlexItem const&) const;
157
158
    void set_main_size(Box const&, CSSPixels size);
159
    void set_cross_size(Box const&, CSSPixels size);
160
    void set_offset(Box const&, CSSPixels main_offset, CSSPixels cross_offset);
161
    void set_main_axis_first_margin(FlexItem&, CSSPixels margin);
162
    void set_main_axis_second_margin(FlexItem&, CSSPixels margin);
163
164
    void set_has_definite_main_size(FlexItem&);
165
    void set_has_definite_cross_size(FlexItem&);
166
167
    void copy_dimensions_from_flex_items_to_boxes();
168
169
    void generate_anonymous_flex_items();
170
171
    void determine_available_space_for_items(AvailableSpace const&);
172
173
    void determine_flex_base_size_and_hypothetical_main_size(FlexItem&);
174
175
    void collect_flex_items_into_flex_lines();
176
177
    void resolve_flexible_lengths();
178
    void resolve_flexible_lengths_for_line(FlexLine&);
179
180
    void resolve_cross_axis_auto_margins();
181
182
    void determine_hypothetical_cross_size_of_item(FlexItem&, bool resolve_percentage_min_max_sizes);
183
184
    void calculate_cross_size_of_each_flex_line();
185
186
    void handle_align_content_stretch();
187
188
    CSS::AlignItems alignment_for_item(Box const&) const;
189
190
    void determine_used_cross_size_of_each_flex_item();
191
192
    void distribute_any_remaining_free_space();
193
194
    void align_all_flex_items_along_the_cross_axis();
195
196
    void align_all_flex_lines();
197
198
0
    bool is_row_layout() const { return m_flex_direction == CSS::FlexDirection::Row || m_flex_direction == CSS::FlexDirection::RowReverse; }
199
0
    bool is_single_line() const { return flex_container().computed_values().flex_wrap() == CSS::FlexWrap::Nowrap; }
200
    bool is_direction_reverse() const;
201
    void populate_specified_margins(FlexItem&, CSS::FlexDirection) const;
202
203
    void determine_intrinsic_size_of_flex_container();
204
    [[nodiscard]] CSSPixels calculate_intrinsic_main_size_of_flex_container();
205
    [[nodiscard]] CSSPixels calculate_intrinsic_cross_size_of_flex_container();
206
207
    [[nodiscard]] CSSPixels calculate_cross_min_content_contribution(FlexItem const&, bool resolve_percentage_min_max_sizes) const;
208
    [[nodiscard]] CSSPixels calculate_cross_max_content_contribution(FlexItem const&, bool resolve_percentage_min_max_sizes) const;
209
    [[nodiscard]] CSSPixels calculate_main_min_content_contribution(FlexItem const&) const;
210
    [[nodiscard]] CSSPixels calculate_main_max_content_contribution(FlexItem const&) const;
211
212
    [[nodiscard]] CSSPixels calculate_min_content_main_size(FlexItem const&) const;
213
    [[nodiscard]] CSSPixels calculate_max_content_main_size(FlexItem const&) const;
214
    [[nodiscard]] CSSPixels calculate_min_content_cross_size(FlexItem const&) const;
215
    [[nodiscard]] CSSPixels calculate_max_content_cross_size(FlexItem const&) const;
216
217
    [[nodiscard]] CSSPixels calculate_fit_content_main_size(FlexItem const&) const;
218
    [[nodiscard]] CSSPixels calculate_fit_content_cross_size(FlexItem const&) const;
219
220
    [[nodiscard]] CSSPixels calculate_width_to_use_when_determining_intrinsic_height_of_item(FlexItem const&) const;
221
222
    virtual void parent_context_did_dimension_child_root_box() override;
223
224
    CSS::FlexBasis used_flex_basis_for_item(FlexItem const&) const;
225
226
    LayoutState::UsedValues& m_flex_container_state;
227
228
    Vector<FlexLine> m_flex_lines;
229
    Vector<FlexItem> m_flex_items;
230
    CSS::FlexDirection m_flex_direction {};
231
232
    struct AxisAgnosticAvailableSpace {
233
        AvailableSize main;
234
        AvailableSize cross;
235
        AvailableSpace space;
236
    };
237
    Optional<AxisAgnosticAvailableSpace> m_available_space_for_items;
238
};
239
240
}