/src/serenity/Userland/Libraries/LibWeb/Layout/FormattingContext.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/OwnPtr.h> |
10 | | #include <LibWeb/Forward.h> |
11 | | #include <LibWeb/Layout/AvailableSpace.h> |
12 | | #include <LibWeb/Layout/LayoutState.h> |
13 | | |
14 | | namespace Web::Layout { |
15 | | |
16 | | // NOTE: We use a custom clamping function here instead of AK::clamp(), since the AK version |
17 | | // will VERIFY(max >= min) and CSS explicitly allows that (see css-values-4.) |
18 | | template<typename T> |
19 | | [[nodiscard]] constexpr T css_clamp(T const& value, T const& min, T const& max) |
20 | 0 | { |
21 | 0 | return ::max(min, ::min(value, max)); |
22 | 0 | } |
23 | | |
24 | | class FormattingContext { |
25 | | public: |
26 | | virtual ~FormattingContext(); |
27 | | |
28 | | enum class Type { |
29 | | Block, |
30 | | Inline, |
31 | | Flex, |
32 | | Grid, |
33 | | Table, |
34 | | SVG, |
35 | | InternalReplaced, // Internal hack formatting context for replaced elements. FIXME: Get rid of this. |
36 | | InternalDummy, // Internal hack formatting context for unimplemented things. FIXME: Get rid of this. |
37 | | }; |
38 | | |
39 | | virtual void run(AvailableSpace const&) = 0; |
40 | | |
41 | | // This function returns the automatic content height of the context's root box. |
42 | | virtual CSSPixels automatic_content_width() const = 0; |
43 | | |
44 | | // This function returns the automatic content height of the context's root box. |
45 | | virtual CSSPixels automatic_content_height() const = 0; |
46 | | |
47 | 0 | Box const& context_box() const { return m_context_box; } |
48 | | |
49 | 0 | FormattingContext* parent() { return m_parent; } |
50 | 0 | FormattingContext const* parent() const { return m_parent; } |
51 | | |
52 | 0 | Type type() const { return m_type; } |
53 | 0 | bool is_block_formatting_context() const { return type() == Type::Block; } |
54 | | |
55 | 0 | virtual bool inhibits_floating() const { return false; } |
56 | | |
57 | | [[nodiscard]] static Optional<Type> formatting_context_type_created_by_box(Box const&); |
58 | | |
59 | | static bool creates_block_formatting_context(Box const&); |
60 | | |
61 | | CSSPixels compute_table_box_width_inside_table_wrapper(Box const&, AvailableSpace const&); |
62 | | CSSPixels compute_table_box_height_inside_table_wrapper(Box const&, AvailableSpace const&); |
63 | | |
64 | | CSSPixels compute_width_for_replaced_element(Box const&, AvailableSpace const&) const; |
65 | | CSSPixels compute_height_for_replaced_element(Box const&, AvailableSpace const&) const; |
66 | | |
67 | | OwnPtr<FormattingContext> create_independent_formatting_context_if_needed(LayoutState&, LayoutMode, Box const& child_box); |
68 | | |
69 | 0 | virtual void parent_context_did_dimension_child_root_box() { } |
70 | | |
71 | | CSSPixels calculate_min_content_width(Layout::Box const&) const; |
72 | | CSSPixels calculate_max_content_width(Layout::Box const&) const; |
73 | | CSSPixels calculate_min_content_height(Layout::Box const&, CSSPixels width) const; |
74 | | CSSPixels calculate_max_content_height(Layout::Box const&, CSSPixels width) const; |
75 | | |
76 | | CSSPixels calculate_fit_content_height(Layout::Box const&, AvailableSpace const&) const; |
77 | | CSSPixels calculate_fit_content_width(Layout::Box const&, AvailableSpace const&) const; |
78 | | |
79 | | CSSPixels calculate_inner_width(Layout::Box const&, AvailableSize const&, CSS::Size const& width) const; |
80 | | CSSPixels calculate_inner_height(Layout::Box const&, AvailableSize const&, CSS::Size const& height) const; |
81 | | |
82 | | virtual CSSPixels greatest_child_width(Box const&) const; |
83 | | |
84 | | [[nodiscard]] CSSPixelRect absolute_content_rect(Box const&) const; |
85 | | [[nodiscard]] CSSPixelRect absolute_content_rect(LayoutState::UsedValues const&) const; |
86 | | [[nodiscard]] CSSPixelRect margin_box_rect(Box const&) const; |
87 | | [[nodiscard]] CSSPixelRect margin_box_rect_in_ancestor_coordinate_space(Box const&, Box const& ancestor_box) const; |
88 | | [[nodiscard]] CSSPixelRect margin_box_rect(LayoutState::UsedValues const&) const; |
89 | | [[nodiscard]] CSSPixelRect margin_box_rect_in_ancestor_coordinate_space(LayoutState::UsedValues const&, Box const& ancestor_box) const; |
90 | | [[nodiscard]] CSSPixelRect border_box_rect(Box const&) const; |
91 | | [[nodiscard]] CSSPixelRect border_box_rect(LayoutState::UsedValues const&) const; |
92 | | [[nodiscard]] CSSPixelRect border_box_rect_in_ancestor_coordinate_space(Box const&, Box const& ancestor_box) const; |
93 | | [[nodiscard]] CSSPixelRect border_box_rect_in_ancestor_coordinate_space(LayoutState::UsedValues const&, Box const& ancestor_box) const; |
94 | | [[nodiscard]] CSSPixelRect content_box_rect(Box const&) const; |
95 | | [[nodiscard]] CSSPixelRect content_box_rect(LayoutState::UsedValues const&) const; |
96 | | [[nodiscard]] CSSPixelRect content_box_rect_in_ancestor_coordinate_space(Box const&, Box const& ancestor_box) const; |
97 | | [[nodiscard]] CSSPixelRect content_box_rect_in_ancestor_coordinate_space(LayoutState::UsedValues const&, Box const& ancestor_box) const; |
98 | | [[nodiscard]] CSSPixels box_baseline(Box const&) const; |
99 | | [[nodiscard]] CSSPixelRect content_box_rect_in_static_position_ancestor_coordinate_space(Box const&, Box const& ancestor_box) const; |
100 | | |
101 | | [[nodiscard]] CSSPixels containing_block_width_for(NodeWithStyleAndBoxModelMetrics const&) const; |
102 | | [[nodiscard]] CSSPixels containing_block_height_for(NodeWithStyleAndBoxModelMetrics const&) const; |
103 | | |
104 | | [[nodiscard]] AvailableSize containing_block_width_as_available_size(NodeWithStyleAndBoxModelMetrics const&) const; |
105 | | [[nodiscard]] AvailableSize containing_block_height_as_available_size(NodeWithStyleAndBoxModelMetrics const&) const; |
106 | | |
107 | | [[nodiscard]] CSSPixels calculate_stretch_fit_width(Box const&, AvailableSize const&) const; |
108 | | [[nodiscard]] CSSPixels calculate_stretch_fit_height(Box const&, AvailableSize const&) const; |
109 | | |
110 | | virtual CSSPixelPoint calculate_static_position(Box const&) const; |
111 | | bool can_skip_is_anonymous_text_run(Box&); |
112 | | |
113 | | void compute_inset(NodeWithStyleAndBoxModelMetrics const&); |
114 | | |
115 | | protected: |
116 | | FormattingContext(Type, LayoutMode, LayoutState&, Box const&, FormattingContext* parent = nullptr); |
117 | | |
118 | | static bool should_treat_width_as_auto(Box const&, AvailableSpace const&); |
119 | | static bool should_treat_height_as_auto(Box const&, AvailableSpace const&); |
120 | | |
121 | | [[nodiscard]] bool should_treat_max_width_as_none(Box const&, AvailableSize const&) const; |
122 | | [[nodiscard]] bool should_treat_max_height_as_none(Box const&, AvailableSize const&) const; |
123 | | |
124 | | OwnPtr<FormattingContext> layout_inside(Box const&, LayoutMode, AvailableSpace const&); |
125 | | |
126 | | struct SpaceUsedByFloats { |
127 | | CSSPixels left { 0 }; |
128 | | CSSPixels right { 0 }; |
129 | | }; |
130 | | |
131 | | struct SpaceUsedAndContainingMarginForFloats { |
132 | | // Width for left / right floats, including their own margins. |
133 | | CSSPixels left_used_space; |
134 | | CSSPixels right_used_space; |
135 | | // Left / right total margins from the outermost containing block to the floating element. |
136 | | // Each block in the containing chain adds its own margin and we store the total here. |
137 | | CSSPixels left_total_containing_margin; |
138 | | CSSPixels right_total_containing_margin; |
139 | | JS::GCPtr<Box const> matching_left_float_box; |
140 | | }; |
141 | | |
142 | | struct ShrinkToFitResult { |
143 | | CSSPixels preferred_width { 0 }; |
144 | | CSSPixels preferred_minimum_width { 0 }; |
145 | | }; |
146 | | |
147 | | CSSPixels tentative_width_for_replaced_element(Box const&, CSS::Size const& computed_width, AvailableSpace const&) const; |
148 | | CSSPixels tentative_height_for_replaced_element(Box const&, CSS::Size const& computed_height, AvailableSpace const&) const; |
149 | | CSSPixels compute_auto_height_for_block_formatting_context_root(Box const&) const; |
150 | | |
151 | | [[nodiscard]] CSSPixelSize solve_replaced_size_constraint(CSSPixels input_width, CSSPixels input_height, Box const&, AvailableSpace const&) const; |
152 | | |
153 | | ShrinkToFitResult calculate_shrink_to_fit_widths(Box const&); |
154 | | |
155 | | void layout_absolutely_positioned_element(Box const&, AvailableSpace const&); |
156 | | void compute_width_for_absolutely_positioned_element(Box const&, AvailableSpace const&); |
157 | | void compute_width_for_absolutely_positioned_non_replaced_element(Box const&, AvailableSpace const&); |
158 | | void compute_width_for_absolutely_positioned_replaced_element(Box const&, AvailableSpace const&); |
159 | | |
160 | | enum class BeforeOrAfterInsideLayout { |
161 | | Before, |
162 | | After, |
163 | | }; |
164 | | void compute_height_for_absolutely_positioned_element(Box const&, AvailableSpace const&, BeforeOrAfterInsideLayout); |
165 | | void compute_height_for_absolutely_positioned_non_replaced_element(Box const&, AvailableSpace const&, BeforeOrAfterInsideLayout); |
166 | | void compute_height_for_absolutely_positioned_replaced_element(Box const&, AvailableSpace const&, BeforeOrAfterInsideLayout); |
167 | | |
168 | | [[nodiscard]] Optional<CSSPixels> compute_auto_height_for_absolutely_positioned_element(Box const&, AvailableSpace const&, BeforeOrAfterInsideLayout) const; |
169 | | |
170 | | [[nodiscard]] Box const* box_child_to_derive_baseline_from(Box const&) const; |
171 | | |
172 | | Type m_type {}; |
173 | | LayoutMode m_layout_mode; |
174 | | |
175 | | FormattingContext* m_parent { nullptr }; |
176 | | JS::NonnullGCPtr<Box const> m_context_box; |
177 | | |
178 | | LayoutState& m_state; |
179 | | }; |
180 | | |
181 | | bool box_is_sized_as_replaced_element(Box const&); |
182 | | |
183 | | } |