Serenity Operating System
1/*
2 * Copyright (c) 2022-2023, Martin Falisse <mfalisse@outlook.com>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <LibWeb/DOM/Node.h>
8#include <LibWeb/Layout/Box.h>
9#include <LibWeb/Layout/GridFormattingContext.h>
10
11namespace Web::Layout {
12
13GridFormattingContext::GridFormattingContext(LayoutState& state, Box const& grid_container, FormattingContext* parent)
14 : FormattingContext(Type::Grid, state, grid_container, parent)
15{
16}
17
18GridFormattingContext::~GridFormattingContext() = default;
19
20CSSPixels GridFormattingContext::resolve_definite_track_size(CSS::GridSize const& grid_size, AvailableSpace const& available_space, Box const& box)
21{
22 VERIFY(grid_size.is_definite());
23 switch (grid_size.type()) {
24 case CSS::GridSize::Type::Length:
25 if (grid_size.length().is_auto())
26 break;
27 return grid_size.length().to_px(box);
28 case CSS::GridSize::Type::Percentage:
29 if (available_space.width.is_definite())
30 return grid_size.percentage().as_fraction() * available_space.width.to_px().value();
31 break;
32 default:
33 VERIFY_NOT_REACHED();
34 }
35 return 0;
36}
37
38size_t GridFormattingContext::count_of_gap_columns()
39{
40 size_t count = 0;
41 for (auto& grid_column : m_grid_columns) {
42 if (grid_column.is_gap)
43 count++;
44 }
45 return count;
46}
47
48size_t GridFormattingContext::count_of_gap_rows()
49{
50 size_t count = 0;
51 for (auto& grid_row : m_grid_rows) {
52 if (grid_row.is_gap)
53 count++;
54 }
55 return count;
56}
57
58CSSPixels GridFormattingContext::resolve_size(CSS::Size const& size, AvailableSize const& available_size, Box const& box)
59{
60 if (size.is_length() && size.length().is_calculated()) {
61 if (size.length().calculated_style_value()->contains_percentage()) {
62 if (!available_size.is_definite())
63 return 0;
64 auto& calc_value = *size.length().calculated_style_value();
65 return calc_value.resolve_length_percentage(box, CSS::Length::make_px(available_size.to_px())).value_or(CSS::Length::make_auto()).to_px(box);
66 }
67 return size.length().to_px(box);
68 }
69 if (size.is_length()) {
70 return size.length().to_px(box);
71 }
72 if (size.is_percentage()) {
73 if (!available_size.is_definite())
74 return 0;
75 return available_size.to_px() * size.percentage().as_fraction();
76 }
77 return 0;
78}
79
80int GridFormattingContext::get_count_of_tracks(Vector<CSS::ExplicitGridTrack> const& track_list, AvailableSpace const& available_space, Box const& box)
81{
82 auto track_count = 0;
83 for (auto const& explicit_grid_track : track_list) {
84 if (explicit_grid_track.is_repeat() && explicit_grid_track.repeat().is_default())
85 track_count += explicit_grid_track.repeat().repeat_count() * explicit_grid_track.repeat().grid_track_size_list().track_list().size();
86 else
87 track_count += 1;
88 }
89
90 if (track_list.size() == 1
91 && track_list.first().is_repeat()
92 && (track_list.first().repeat().is_auto_fill() || track_list.first().repeat().is_auto_fit())) {
93 track_count = count_of_repeated_auto_fill_or_fit_tracks(track_list, available_space, box);
94 }
95
96 return track_count;
97}
98
99int GridFormattingContext::count_of_repeated_auto_fill_or_fit_tracks(Vector<CSS::ExplicitGridTrack> const& track_list, AvailableSpace const& available_space, Box const& box)
100{
101 // https://www.w3.org/TR/css-grid-2/#auto-repeat
102 // 7.2.3.2. Repeat-to-fill: auto-fill and auto-fit repetitions
103 // On a subgridded axis, the auto-fill keyword is only valid once per <line-name-list>, and repeats
104 // enough times for the name list to match the subgrid’s specified grid span (falling back to 0 if
105 // the span is already fulfilled).
106
107 // Otherwise on a standalone axis, when auto-fill is given as the repetition number
108 // If the grid container has a definite size or max size in the relevant axis, then the number of
109 // repetitions is the largest possible positive integer that does not cause the grid to overflow the
110 // content box of its grid container
111
112 CSSPixels sum_of_grid_track_sizes = 0;
113 // (treating each track as its max track sizing function if that is definite or its minimum track sizing
114 // function otherwise, flooring the max track sizing function by the min track sizing function if both
115 // are definite, and taking gap into account)
116 // FIXME: take gap into account
117 for (auto& explicit_grid_track : track_list.first().repeat().grid_track_size_list().track_list()) {
118 auto track_sizing_function = explicit_grid_track;
119 if (track_sizing_function.is_minmax()) {
120 if (track_sizing_function.minmax().max_grid_size().is_definite() && !track_sizing_function.minmax().min_grid_size().is_definite())
121 sum_of_grid_track_sizes += resolve_definite_track_size(track_sizing_function.minmax().max_grid_size(), available_space, box);
122 else if (track_sizing_function.minmax().min_grid_size().is_definite() && !track_sizing_function.minmax().max_grid_size().is_definite())
123 sum_of_grid_track_sizes += resolve_definite_track_size(track_sizing_function.minmax().min_grid_size(), available_space, box);
124 else if (track_sizing_function.minmax().min_grid_size().is_definite() && track_sizing_function.minmax().max_grid_size().is_definite())
125 sum_of_grid_track_sizes += min(resolve_definite_track_size(track_sizing_function.minmax().min_grid_size(), available_space, box), resolve_definite_track_size(track_sizing_function.minmax().max_grid_size(), available_space, box));
126 } else {
127 sum_of_grid_track_sizes += min(resolve_definite_track_size(track_sizing_function.grid_size(), available_space, box), resolve_definite_track_size(track_sizing_function.grid_size(), available_space, box));
128 }
129 }
130 return max(1, static_cast<int>((get_free_space_x(available_space) / sum_of_grid_track_sizes).value()));
131
132 // For the purpose of finding the number of auto-repeated tracks in a standalone axis, the UA must
133 // floor the track size to a UA-specified value to avoid division by zero. It is suggested that this
134 // floor be 1px.
135}
136
137void GridFormattingContext::place_item_with_row_and_column_position(Box const& box, Box const& child_box)
138{
139 int row_start = child_box.computed_values().grid_row_start().raw_value() - 1;
140 int row_end = child_box.computed_values().grid_row_end().raw_value() - 1;
141 int column_start = child_box.computed_values().grid_column_start().raw_value() - 1;
142 int column_end = child_box.computed_values().grid_column_end().raw_value() - 1;
143
144 // https://www.w3.org/TR/css-grid-2/#line-placement
145 // 8.3. Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties
146
147 // https://www.w3.org/TR/css-grid-2/#grid-placement-slot
148 // First attempt to match the grid area’s edge to a named grid area: if there is a grid line whose
149 // line name is <custom-ident>-start (for grid-*-start) / <custom-ident>-end (for grid-*-end),
150 // contributes the first such line to the grid item’s placement.
151
152 // Otherwise, treat this as if the integer 1 had been specified along with the <custom-ident>.
153
154 // https://www.w3.org/TR/css-grid-2/#grid-placement-int
155 // Contributes the Nth grid line to the grid item’s placement. If a negative integer is given, it
156 // instead counts in reverse, starting from the end edge of the explicit grid.
157 if (row_end < 0)
158 row_end = m_occupation_grid.row_count() + row_end + 2;
159 if (column_end < 0)
160 column_end = m_occupation_grid.column_count() + column_end + 2;
161
162 // If a name is given as a <custom-ident>, only lines with that name are counted. If not enough
163 // lines with that name exist, all implicit grid lines are assumed to have that name for the purpose
164 // of finding this position.
165
166 // https://www.w3.org/TR/css-grid-2/#grid-placement-span-int
167 // Contributes a grid span to the grid item’s placement such that the corresponding edge of the grid
168 // item’s grid area is N lines from its opposite edge in the corresponding direction. For example,
169 // grid-column-end: span 2 indicates the second grid line in the endward direction from the
170 // grid-column-start line.
171 int row_span = 1;
172 int column_span = 1;
173 if (child_box.computed_values().grid_row_start().is_position() && child_box.computed_values().grid_row_end().is_span())
174 row_span = child_box.computed_values().grid_row_end().raw_value();
175 if (child_box.computed_values().grid_column_start().is_position() && child_box.computed_values().grid_column_end().is_span())
176 column_span = child_box.computed_values().grid_column_end().raw_value();
177 if (child_box.computed_values().grid_row_end().is_position() && child_box.computed_values().grid_row_start().is_span()) {
178 row_span = child_box.computed_values().grid_row_start().raw_value();
179 row_start = row_end - row_span;
180 }
181 if (child_box.computed_values().grid_column_end().is_position() && child_box.computed_values().grid_column_start().is_span()) {
182 column_span = child_box.computed_values().grid_column_start().raw_value();
183 column_start = column_end - column_span;
184 }
185
186 // If a name is given as a <custom-ident>, only lines with that name are counted. If not enough
187 // lines with that name exist, all implicit grid lines on the side of the explicit grid
188 // corresponding to the search direction are assumed to have that name for the purpose of counting
189 // this span.
190
191 // https://drafts.csswg.org/css-grid/#grid-placement-auto
192 // auto
193 // The property contributes nothing to the grid item’s placement, indicating auto-placement or a
194 // default span of one. (See § 8 Placing Grid Items, above.)
195
196 // https://www.w3.org/TR/css-grid-2/#common-uses-named-lines
197 // 8.1.3. Named Lines and Spans
198 // Instead of counting lines by number, lines can be referenced by their line name:
199 if (child_box.computed_values().grid_column_end().has_line_name()) {
200 if (auto grid_area_index = find_valid_grid_area(child_box.computed_values().grid_column_end().line_name()); grid_area_index > -1)
201 column_end = m_valid_grid_areas[grid_area_index].column_end;
202 else if (auto line_name_index = get_line_index_by_line_name(child_box.computed_values().grid_column_end().line_name(), box.computed_values().grid_template_columns()); line_name_index > -1)
203 column_end = line_name_index;
204 else
205 column_end = 1;
206 column_start = column_end - 1;
207 }
208 if (child_box.computed_values().grid_column_start().has_line_name()) {
209 if (auto grid_area_index = find_valid_grid_area(child_box.computed_values().grid_column_end().line_name()); grid_area_index > -1)
210 column_start = m_valid_grid_areas[grid_area_index].column_start;
211 else if (auto line_name_index = get_line_index_by_line_name(child_box.computed_values().grid_column_start().line_name(), box.computed_values().grid_template_columns()); line_name_index > -1)
212 column_start = line_name_index;
213 else
214 column_start = 0;
215 }
216 if (child_box.computed_values().grid_row_end().has_line_name()) {
217 if (auto grid_area_index = find_valid_grid_area(child_box.computed_values().grid_row_end().line_name()); grid_area_index > -1)
218 row_end = m_valid_grid_areas[grid_area_index].row_end;
219 else if (auto line_name_index = get_line_index_by_line_name(child_box.computed_values().grid_row_end().line_name(), box.computed_values().grid_template_rows()); line_name_index > -1)
220 row_end = line_name_index;
221 else
222 row_end = 1;
223 row_start = row_end - 1;
224 }
225 if (child_box.computed_values().grid_row_start().has_line_name()) {
226 if (auto grid_area_index = find_valid_grid_area(child_box.computed_values().grid_row_end().line_name()); grid_area_index > -1)
227 row_start = m_valid_grid_areas[grid_area_index].row_start;
228 else if (auto line_name_index = get_line_index_by_line_name(child_box.computed_values().grid_row_start().line_name(), box.computed_values().grid_template_rows()); line_name_index > -1)
229 row_start = line_name_index;
230 else
231 row_start = 0;
232 }
233
234 // If there are multiple lines of the same name, they effectively establish a named set of grid
235 // lines, which can be exclusively indexed by filtering the placement by name:
236
237 // https://drafts.csswg.org/css-grid/#grid-placement-errors
238 // 8.3.1. Grid Placement Conflict Handling
239 // If the placement for a grid item contains two lines, and the start line is further end-ward than
240 // the end line, swap the two lines. If the start line is equal to the end line, remove the end
241 // line.
242 if (child_box.computed_values().grid_row_start().is_position() && child_box.computed_values().grid_row_end().is_position()) {
243 if (row_start > row_end)
244 swap(row_start, row_end);
245 if (row_start != row_end)
246 row_span = row_end - row_start;
247 }
248 if (child_box.computed_values().grid_column_start().is_position() && child_box.computed_values().grid_column_end().is_position()) {
249 if (column_start > column_end)
250 swap(column_start, column_end);
251 if (column_start != column_end)
252 column_span = column_end - column_start;
253 }
254
255 // If the placement contains two spans, remove the one contributed by the end grid-placement
256 // property.
257 if (child_box.computed_values().grid_row_start().is_span() && child_box.computed_values().grid_row_end().is_span())
258 row_span = child_box.computed_values().grid_row_start().raw_value();
259 if (child_box.computed_values().grid_column_start().is_span() && child_box.computed_values().grid_column_end().is_span())
260 column_span = child_box.computed_values().grid_column_start().raw_value();
261
262 // FIXME: If the placement contains only a span for a named line, replace it with a span of 1.
263
264 m_positioned_boxes.append(PositionedBox(child_box, row_start, row_span, column_start, column_span));
265
266 m_occupation_grid.maybe_add_row(row_start + 1);
267 m_occupation_grid.maybe_add_column(column_start + 1);
268 m_occupation_grid.set_occupied(column_start, column_start + column_span, row_start, row_start + row_span);
269}
270
271void GridFormattingContext::place_item_with_row_position(Box const& box, Box const& child_box)
272{
273 int row_start = child_box.computed_values().grid_row_start().raw_value() - 1;
274 int row_end = child_box.computed_values().grid_row_end().raw_value() - 1;
275
276 // https://www.w3.org/TR/css-grid-2/#line-placement
277 // 8.3. Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties
278
279 // https://www.w3.org/TR/css-grid-2/#grid-placement-slot
280 // First attempt to match the grid area’s edge to a named grid area: if there is a grid line whose
281 // line name is <custom-ident>-start (for grid-*-start) / <custom-ident>-end (for grid-*-end),
282 // contributes the first such line to the grid item’s placement.
283
284 // Otherwise, treat this as if the integer 1 had been specified along with the <custom-ident>.
285
286 // https://www.w3.org/TR/css-grid-2/#grid-placement-int
287 // Contributes the Nth grid line to the grid item’s placement. If a negative integer is given, it
288 // instead counts in reverse, starting from the end edge of the explicit grid.
289 if (row_end < 0)
290 row_end = m_occupation_grid.row_count() + row_end + 2;
291
292 // If a name is given as a <custom-ident>, only lines with that name are counted. If not enough
293 // lines with that name exist, all implicit grid lines are assumed to have that name for the purpose
294 // of finding this position.
295
296 // https://www.w3.org/TR/css-grid-2/#grid-placement-span-int
297 // Contributes a grid span to the grid item’s placement such that the corresponding edge of the grid
298 // item’s grid area is N lines from its opposite edge in the corresponding direction. For example,
299 // grid-column-end: span 2 indicates the second grid line in the endward direction from the
300 // grid-column-start line.
301 int row_span = 1;
302 if (child_box.computed_values().grid_row_start().is_position() && child_box.computed_values().grid_row_end().is_span())
303 row_span = child_box.computed_values().grid_row_end().raw_value();
304 if (child_box.computed_values().grid_row_end().is_position() && child_box.computed_values().grid_row_start().is_span()) {
305 row_span = child_box.computed_values().grid_row_start().raw_value();
306 row_start = row_end - row_span;
307 // FIXME: Remove me once have implemented spans overflowing into negative indexes, e.g., grid-row: span 2 / 1
308 if (row_start < 0)
309 row_start = 0;
310 }
311
312 // If a name is given as a <custom-ident>, only lines with that name are counted. If not enough
313 // lines with that name exist, all implicit grid lines on the side of the explicit grid
314 // corresponding to the search direction are assumed to have that name for the purpose of counting
315 // this span.
316
317 // https://drafts.csswg.org/css-grid/#grid-placement-auto
318 // auto
319 // The property contributes nothing to the grid item’s placement, indicating auto-placement or a
320 // default span of one. (See § 8 Placing Grid Items, above.)
321
322 // https://www.w3.org/TR/css-grid-2/#common-uses-named-lines
323 // 8.1.3. Named Lines and Spans
324 // Instead of counting lines by number, lines can be referenced by their line name:
325 if (child_box.computed_values().grid_row_end().has_line_name()) {
326 if (auto grid_area_index = find_valid_grid_area(child_box.computed_values().grid_row_end().line_name()); grid_area_index > -1)
327 row_end = m_valid_grid_areas[grid_area_index].row_end;
328 else if (auto line_name_index = get_line_index_by_line_name(child_box.computed_values().grid_row_end().line_name(), box.computed_values().grid_template_rows()); line_name_index > -1)
329 row_end = line_name_index;
330 else
331 row_end = 1;
332 row_start = row_end - 1;
333 }
334 if (child_box.computed_values().grid_row_start().has_line_name()) {
335 if (auto grid_area_index = find_valid_grid_area(child_box.computed_values().grid_row_end().line_name()); grid_area_index > -1)
336 row_start = m_valid_grid_areas[grid_area_index].row_start;
337 else if (auto line_name_index = get_line_index_by_line_name(child_box.computed_values().grid_row_start().line_name(), box.computed_values().grid_template_rows()); line_name_index > -1)
338 row_start = line_name_index;
339 else
340 row_start = 0;
341 }
342
343 // If there are multiple lines of the same name, they effectively establish a named set of grid
344 // lines, which can be exclusively indexed by filtering the placement by name:
345
346 // https://drafts.csswg.org/css-grid/#grid-placement-errors
347 // 8.3.1. Grid Placement Conflict Handling
348 // If the placement for a grid item contains two lines, and the start line is further end-ward than
349 // the end line, swap the two lines. If the start line is equal to the end line, remove the end
350 // line.
351 if (child_box.computed_values().grid_row_start().is_position() && child_box.computed_values().grid_row_end().is_position()) {
352 if (row_start > row_end)
353 swap(row_start, row_end);
354 if (row_start != row_end)
355 row_span = row_end - row_start;
356 }
357 // FIXME: Have yet to find the spec for this.
358 if (!child_box.computed_values().grid_row_start().is_position() && child_box.computed_values().grid_row_end().is_position() && row_end == 0)
359 row_start = 0;
360
361 // If the placement contains two spans, remove the one contributed by the end grid-placement
362 // property.
363 if (child_box.computed_values().grid_row_start().is_span() && child_box.computed_values().grid_row_end().is_span())
364 row_span = child_box.computed_values().grid_row_start().raw_value();
365
366 // FIXME: If the placement contains only a span for a named line, replace it with a span of 1.
367
368 m_occupation_grid.maybe_add_row(row_start + row_span);
369
370 int column_start = 0;
371 auto column_span = child_box.computed_values().grid_column_start().is_span() ? child_box.computed_values().grid_column_start().raw_value() : 1;
372 // https://drafts.csswg.org/css-grid/#auto-placement-algo
373 // 8.5. Grid Item Placement Algorithm
374 // 3.3. If the largest column span among all the items without a definite column position is larger
375 // than the width of the implicit grid, add columns to the end of the implicit grid to accommodate
376 // that column span.
377 m_occupation_grid.maybe_add_column(column_span);
378 bool found_available_column = false;
379 for (int column_index = column_start; column_index < m_occupation_grid.column_count(); column_index++) {
380 if (!m_occupation_grid.is_occupied(column_index, row_start)) {
381 found_available_column = true;
382 column_start = column_index;
383 break;
384 }
385 }
386 if (!found_available_column) {
387 column_start = m_occupation_grid.column_count();
388 m_occupation_grid.maybe_add_column(column_start + column_span);
389 }
390 m_occupation_grid.set_occupied(column_start, column_start + column_span, row_start, row_start + row_span);
391
392 m_positioned_boxes.append(PositionedBox(child_box, row_start, row_span, column_start, column_span));
393}
394
395void GridFormattingContext::place_item_with_column_position(Box const& box, Box const& child_box, int& auto_placement_cursor_x, int& auto_placement_cursor_y)
396{
397 int column_start = child_box.computed_values().grid_column_start().raw_value() - 1;
398 int column_end = child_box.computed_values().grid_column_end().raw_value() - 1;
399
400 // https://www.w3.org/TR/css-grid-2/#line-placement
401 // 8.3. Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties
402
403 // https://www.w3.org/TR/css-grid-2/#grid-placement-slot
404 // First attempt to match the grid area’s edge to a named grid area: if there is a grid line whose
405 // line name is <custom-ident>-start (for grid-*-start) / <custom-ident>-end (for grid-*-end),
406 // contributes the first such line to the grid item’s placement.
407
408 // Otherwise, treat this as if the integer 1 had been specified along with the <custom-ident>.
409
410 // https://www.w3.org/TR/css-grid-2/#grid-placement-int
411 // Contributes the Nth grid line to the grid item’s placement. If a negative integer is given, it
412 // instead counts in reverse, starting from the end edge of the explicit grid.
413 if (column_end < 0)
414 column_end = m_occupation_grid.column_count() + column_end + 2;
415
416 // If a name is given as a <custom-ident>, only lines with that name are counted. If not enough
417 // lines with that name exist, all implicit grid lines are assumed to have that name for the purpose
418 // of finding this position.
419
420 // https://www.w3.org/TR/css-grid-2/#grid-placement-span-int
421 // Contributes a grid span to the grid item’s placement such that the corresponding edge of the grid
422 // item’s grid area is N lines from its opposite edge in the corresponding direction. For example,
423 // grid-column-end: span 2 indicates the second grid line in the endward direction from the
424 // grid-column-start line.
425 int column_span = 1;
426 auto row_span = child_box.computed_values().grid_row_start().is_span() ? child_box.computed_values().grid_row_start().raw_value() : 1;
427 if (child_box.computed_values().grid_column_start().is_position() && child_box.computed_values().grid_column_end().is_span())
428 column_span = child_box.computed_values().grid_column_end().raw_value();
429 if (child_box.computed_values().grid_column_end().is_position() && child_box.computed_values().grid_column_start().is_span()) {
430 column_span = child_box.computed_values().grid_column_start().raw_value();
431 column_start = column_end - column_span;
432 // FIXME: Remove me once have implemented spans overflowing into negative indexes, e.g., grid-column: span 2 / 1
433 if (column_start < 0)
434 column_start = 0;
435 }
436 // FIXME: Have yet to find the spec for this.
437 if (!child_box.computed_values().grid_column_start().is_position() && child_box.computed_values().grid_column_end().is_position() && column_end == 0)
438 column_start = 0;
439
440 // If a name is given as a <custom-ident>, only lines with that name are counted. If not enough
441 // lines with that name exist, all implicit grid lines on the side of the explicit grid
442 // corresponding to the search direction are assumed to have that name for the purpose of counting
443 // this span.
444
445 // https://drafts.csswg.org/css-grid/#grid-placement-auto
446 // auto
447 // The property contributes nothing to the grid item’s placement, indicating auto-placement or a
448 // default span of one. (See § 8 Placing Grid Items, above.)
449
450 // https://www.w3.org/TR/css-grid-2/#common-uses-named-lines
451 // 8.1.3. Named Lines and Spans
452 // Instead of counting lines by number, lines can be referenced by their line name:
453 if (child_box.computed_values().grid_column_end().has_line_name()) {
454 if (auto grid_area_index = find_valid_grid_area(child_box.computed_values().grid_column_end().line_name()); grid_area_index > -1)
455 column_end = m_valid_grid_areas[grid_area_index].column_end;
456 else if (auto line_name_index = get_line_index_by_line_name(child_box.computed_values().grid_column_end().line_name(), box.computed_values().grid_template_columns()); line_name_index > -1)
457 column_end = line_name_index;
458 else
459 column_end = 1;
460 column_start = column_end - 1;
461 }
462 if (child_box.computed_values().grid_column_start().has_line_name()) {
463 if (auto grid_area_index = find_valid_grid_area(child_box.computed_values().grid_column_end().line_name()); grid_area_index > -1)
464 column_start = m_valid_grid_areas[grid_area_index].column_start;
465 else if (auto line_name_index = get_line_index_by_line_name(child_box.computed_values().grid_column_start().line_name(), box.computed_values().grid_template_columns()); line_name_index > -1)
466 column_start = line_name_index;
467 else
468 column_start = 0;
469 }
470
471 // If there are multiple lines of the same name, they effectively establish a named set of grid
472 // lines, which can be exclusively indexed by filtering the placement by name:
473
474 // https://drafts.csswg.org/css-grid/#grid-placement-errors
475 // 8.3.1. Grid Placement Conflict Handling
476 // If the placement for a grid item contains two lines, and the start line is further end-ward than
477 // the end line, swap the two lines. If the start line is equal to the end line, remove the end
478 // line.
479 if (child_box.computed_values().grid_column_start().is_position() && child_box.computed_values().grid_column_end().is_position()) {
480 if (column_start > column_end)
481 swap(column_start, column_end);
482 if (column_start != column_end)
483 column_span = column_end - column_start;
484 }
485
486 // If the placement contains two spans, remove the one contributed by the end grid-placement
487 // property.
488 if (child_box.computed_values().grid_column_start().is_span() && child_box.computed_values().grid_column_end().is_span())
489 column_span = child_box.computed_values().grid_column_start().raw_value();
490
491 // FIXME: If the placement contains only a span for a named line, replace it with a span of 1.
492
493 // 4.1.1.1. Set the column position of the cursor to the grid item's column-start line. If this is
494 // less than the previous column position of the cursor, increment the row position by 1.
495 if (column_start < auto_placement_cursor_x)
496 auto_placement_cursor_y++;
497 auto_placement_cursor_x = column_start;
498
499 m_occupation_grid.maybe_add_column(auto_placement_cursor_x + 1);
500 m_occupation_grid.maybe_add_row(auto_placement_cursor_y + 1);
501
502 // 4.1.1.2. Increment the cursor's row position until a value is found where the grid item does not
503 // overlap any occupied grid cells (creating new rows in the implicit grid as necessary).
504 while (true) {
505 if (!m_occupation_grid.is_occupied(column_start, auto_placement_cursor_y)) {
506 break;
507 }
508 auto_placement_cursor_y++;
509 m_occupation_grid.maybe_add_row(auto_placement_cursor_y + row_span);
510 }
511 // 4.1.1.3. Set the item's row-start line to the cursor's row position, and set the item's row-end
512 // line according to its span from that position.
513 m_occupation_grid.set_occupied(column_start, column_start + column_span, auto_placement_cursor_y, auto_placement_cursor_y + row_span);
514
515 m_positioned_boxes.append(PositionedBox(child_box, auto_placement_cursor_y, row_span, column_start, column_span));
516}
517
518void GridFormattingContext::place_item_with_no_declared_position(Box const& child_box, int& auto_placement_cursor_x, int& auto_placement_cursor_y)
519{
520 // 4.1.2.1. Increment the column position of the auto-placement cursor until either this item's grid
521 // area does not overlap any occupied grid cells, or the cursor's column position, plus the item's
522 // column span, overflow the number of columns in the implicit grid, as determined earlier in this
523 // algorithm.
524 auto column_start = 0;
525 auto column_span = 1;
526 if (child_box.computed_values().grid_column_start().is_span())
527 column_span = child_box.computed_values().grid_column_start().raw_value();
528 else if (child_box.computed_values().grid_column_end().is_span())
529 column_span = child_box.computed_values().grid_column_end().raw_value();
530 // https://drafts.csswg.org/css-grid/#auto-placement-algo
531 // 8.5. Grid Item Placement Algorithm
532 // 3.3. If the largest column span among all the items without a definite column position is larger
533 // than the width of the implicit grid, add columns to the end of the implicit grid to accommodate
534 // that column span.
535 m_occupation_grid.maybe_add_column(column_span);
536 auto row_start = 0;
537 auto row_span = 1;
538 if (child_box.computed_values().grid_row_start().is_span())
539 row_span = child_box.computed_values().grid_row_start().raw_value();
540 else if (child_box.computed_values().grid_row_end().is_span())
541 row_span = child_box.computed_values().grid_row_end().raw_value();
542 auto found_unoccupied_area = false;
543 for (int row_index = auto_placement_cursor_y; row_index < m_occupation_grid.row_count(); row_index++) {
544 for (int column_index = auto_placement_cursor_x; column_index < m_occupation_grid.column_count(); column_index++) {
545 if (column_span + column_index <= m_occupation_grid.column_count()) {
546 auto found_all_available = true;
547 for (int span_index = 0; span_index < column_span; span_index++) {
548 if (m_occupation_grid.is_occupied(column_index + span_index, row_index))
549 found_all_available = false;
550 }
551 if (found_all_available) {
552 found_unoccupied_area = true;
553 column_start = column_index;
554 row_start = row_index;
555 goto finish;
556 }
557 }
558 }
559 auto_placement_cursor_x = 0;
560 auto_placement_cursor_y++;
561 }
562finish:
563
564 // 4.1.2.2. If a non-overlapping position was found in the previous step, set the item's row-start
565 // and column-start lines to the cursor's position. Otherwise, increment the auto-placement cursor's
566 // row position (creating new rows in the implicit grid as necessary), set its column position to the
567 // start-most column line in the implicit grid, and return to the previous step.
568 if (!found_unoccupied_area) {
569 row_start = m_occupation_grid.row_count();
570 m_occupation_grid.maybe_add_row(m_occupation_grid.row_count() + 1);
571 }
572
573 m_occupation_grid.set_occupied(column_start, column_start + column_span, row_start, row_start + row_span);
574 m_positioned_boxes.append(PositionedBox(child_box, row_start, row_span, column_start, column_span));
575}
576
577void GridFormattingContext::initialize_grid_tracks(Box const& box, AvailableSpace const& available_space, int column_count, int row_count)
578{
579 for (auto const& track_in_list : box.computed_values().grid_template_columns().track_list()) {
580 auto repeat_count = (track_in_list.is_repeat() && track_in_list.repeat().is_default()) ? track_in_list.repeat().repeat_count() : 1;
581 if (track_in_list.is_repeat()) {
582 if (track_in_list.repeat().is_auto_fill() || track_in_list.repeat().is_auto_fit())
583 repeat_count = column_count;
584 }
585 for (auto _ = 0; _ < repeat_count; _++) {
586 switch (track_in_list.type()) {
587 case CSS::ExplicitGridTrack::Type::MinMax:
588 m_grid_columns.append(TemporaryTrack(track_in_list.minmax().min_grid_size(), track_in_list.minmax().max_grid_size()));
589 break;
590 case CSS::ExplicitGridTrack::Type::Repeat:
591 for (auto& explicit_grid_track : track_in_list.repeat().grid_track_size_list().track_list()) {
592 auto track_sizing_function = explicit_grid_track;
593 if (track_sizing_function.is_minmax())
594 m_grid_columns.append(TemporaryTrack(track_sizing_function.minmax().min_grid_size(), track_sizing_function.minmax().max_grid_size()));
595 else
596 m_grid_columns.append(TemporaryTrack(track_sizing_function.grid_size()));
597 }
598 break;
599 case CSS::ExplicitGridTrack::Type::Default:
600 m_grid_columns.append(TemporaryTrack(track_in_list.grid_size()));
601 break;
602 default:
603 VERIFY_NOT_REACHED();
604 }
605 }
606 }
607 for (auto const& track_in_list : box.computed_values().grid_template_rows().track_list()) {
608 auto repeat_count = (track_in_list.is_repeat() && track_in_list.repeat().is_default()) ? track_in_list.repeat().repeat_count() : 1;
609 if (track_in_list.is_repeat()) {
610 if (track_in_list.repeat().is_auto_fill() || track_in_list.repeat().is_auto_fit())
611 repeat_count = row_count;
612 }
613 for (auto _ = 0; _ < repeat_count; _++) {
614 switch (track_in_list.type()) {
615 case CSS::ExplicitGridTrack::Type::MinMax:
616 m_grid_rows.append(TemporaryTrack(track_in_list.minmax().min_grid_size(), track_in_list.minmax().max_grid_size()));
617 break;
618 case CSS::ExplicitGridTrack::Type::Repeat:
619 for (auto& explicit_grid_track : track_in_list.repeat().grid_track_size_list().track_list()) {
620 auto track_sizing_function = explicit_grid_track;
621 if (track_sizing_function.is_minmax())
622 m_grid_rows.append(TemporaryTrack(track_sizing_function.minmax().min_grid_size(), track_sizing_function.minmax().max_grid_size()));
623 else
624 m_grid_rows.append(TemporaryTrack(track_sizing_function.grid_size()));
625 }
626 break;
627 case CSS::ExplicitGridTrack::Type::Default:
628 m_grid_rows.append(TemporaryTrack(track_in_list.grid_size()));
629 break;
630 default:
631 VERIFY_NOT_REACHED();
632 }
633 }
634 }
635
636 for (int column_index = m_grid_columns.size(); column_index < m_occupation_grid.column_count(); column_index++)
637 m_grid_columns.append(TemporaryTrack());
638 for (int row_index = m_grid_rows.size(); row_index < m_occupation_grid.row_count(); row_index++)
639 m_grid_rows.append(TemporaryTrack());
640
641 // https://www.w3.org/TR/css-grid-2/#gutters
642 // 11.1. Gutters: the row-gap, column-gap, and gap properties
643 // For the purpose of track sizing, each gutter is treated as an extra, empty, fixed-size track of
644 // the specified size, which is spanned by any grid items that span across its corresponding grid
645 // line.
646 if (!box.computed_values().column_gap().is_auto()) {
647 for (int column_index = 1; column_index < (m_occupation_grid.column_count() * 2) - 1; column_index += 2)
648 m_grid_columns.insert(column_index, TemporaryTrack(resolve_size(box.computed_values().column_gap(), available_space.width, box), true));
649 }
650 if (!box.computed_values().row_gap().is_auto()) {
651 for (int row_index = 1; row_index < (m_occupation_grid.row_count() * 2) - 1; row_index += 2)
652 m_grid_rows.insert(row_index, TemporaryTrack(resolve_size(box.computed_values().row_gap(), available_space.height, box), true));
653 }
654}
655
656void GridFormattingContext::calculate_sizes_of_columns(Box const& box, AvailableSpace const& available_space)
657{
658 // https://www.w3.org/TR/css-grid-2/#algo-init
659 // 12.4. Initialize Track Sizes
660 // Initialize each track’s base size and growth limit.
661 for (auto& grid_column : m_grid_columns) {
662 if (grid_column.is_gap)
663 continue;
664 // For each track, if the track’s min track sizing function is:
665 switch (grid_column.min_track_sizing_function.type()) {
666 // - A fixed sizing function
667 // Resolve to an absolute length and use that size as the track’s initial base size.
668 case CSS::GridSize::Type::Length:
669 if (!grid_column.min_track_sizing_function.length().is_auto())
670 grid_column.base_size = grid_column.min_track_sizing_function.length().to_px(box);
671 break;
672 case CSS::GridSize::Type::Percentage:
673 if (available_space.width.is_definite())
674 grid_column.base_size = grid_column.min_track_sizing_function.percentage().as_fraction() * available_space.width.to_px().value();
675 break;
676 // - An intrinsic sizing function
677 // Use an initial base size of zero.
678 case CSS::GridSize::Type::FlexibleLength:
679 case CSS::GridSize::Type::MaxContent:
680 case CSS::GridSize::Type::MinContent:
681 break;
682 default:
683 VERIFY_NOT_REACHED();
684 }
685
686 // For each track, if the track’s max track sizing function is:
687 switch (grid_column.max_track_sizing_function.type()) {
688 // - A fixed sizing function
689 // Resolve to an absolute length and use that size as the track’s initial growth limit.
690 case CSS::GridSize::Type::Length:
691 if (!grid_column.max_track_sizing_function.length().is_auto())
692 grid_column.growth_limit = grid_column.max_track_sizing_function.length().to_px(box);
693 else
694 // - An intrinsic sizing function
695 // Use an initial growth limit of infinity.
696 grid_column.growth_limit = -1;
697 break;
698 case CSS::GridSize::Type::Percentage:
699 if (available_space.width.is_definite())
700 grid_column.growth_limit = grid_column.max_track_sizing_function.percentage().as_fraction() * available_space.width.to_px().value();
701 break;
702 // - A flexible sizing function
703 // Use an initial growth limit of infinity.
704 case CSS::GridSize::Type::FlexibleLength:
705 grid_column.growth_limit = -1;
706 break;
707 // - An intrinsic sizing function
708 // Use an initial growth limit of infinity.
709 case CSS::GridSize::Type::MaxContent:
710 case CSS::GridSize::Type::MinContent:
711 grid_column.growth_limit = -1;
712 break;
713 default:
714 VERIFY_NOT_REACHED();
715 }
716
717 // In all cases, if the growth limit is less than the base size, increase the growth limit to match
718 // the base size.
719 if (grid_column.growth_limit != -1 && grid_column.growth_limit < grid_column.base_size)
720 grid_column.growth_limit = grid_column.base_size;
721 }
722
723 // https://www.w3.org/TR/css-grid-2/#algo-content
724 // 12.5. Resolve Intrinsic Track Sizes
725 // This step resolves intrinsic track sizing functions to absolute lengths. First it resolves those
726 // sizes based on items that are contained wholly within a single track. Then it gradually adds in
727 // the space requirements of items that span multiple tracks, evenly distributing the extra space
728 // across those tracks insofar as possible.
729
730 // FIXME: 1. Shim baseline-aligned items so their intrinsic size contributions reflect their baseline
731 // alignment. For the items in each baseline-sharing group, add a “shim” (effectively, additional
732 // margin) on the start/end side (for first/last-baseline alignment) of each item so that, when
733 // start/end-aligned together their baselines align as specified.
734
735 // Consider these “shims” as part of the items’ intrinsic size contribution for the purpose of track
736 // sizing, below. If an item uses multiple intrinsic size contributions, it can have different shims
737 // for each one.
738
739 // 2. Size tracks to fit non-spanning items: For each track with an intrinsic track sizing function and
740 // not a flexible sizing function, consider the items in it with a span of 1:
741 int index = 0;
742 for (auto& grid_column : m_grid_columns) {
743 if (grid_column.is_gap) {
744 ++index;
745 continue;
746 }
747 if (!grid_column.min_track_sizing_function.is_intrinsic_track_sizing()) {
748 ++index;
749 continue;
750 }
751
752 Vector<Box const&> boxes_of_column;
753 for (auto& positioned_box : m_positioned_boxes) {
754 if (positioned_box.gap_adjusted_column(box) == index && positioned_box.raw_column_span() == 1)
755 boxes_of_column.append(positioned_box.box());
756 }
757
758 switch (grid_column.min_track_sizing_function.type()) {
759 // - For min-content minimums:
760 // If the track has a min-content min track sizing function, set its base size to the maximum of the
761 // items’ min-content contributions, floored at zero.
762 case CSS::GridSize::Type::MinContent: {
763 CSSPixels column_width = 0;
764 for (auto& box_of_column : boxes_of_column)
765 column_width = max(column_width, calculate_min_content_width(box_of_column));
766 grid_column.base_size = column_width;
767 } break;
768 // - For max-content minimums:
769 // If the track has a max-content min track sizing function, set its base size to the maximum of the
770 // items’ max-content contributions, floored at zero.
771 case CSS::GridSize::Type::MaxContent: {
772 CSSPixels column_width = 0;
773 for (auto& box_of_column : boxes_of_column)
774 column_width = max(column_width, calculate_max_content_width(box_of_column));
775 grid_column.base_size = column_width;
776 } break;
777 // - For auto minimums:
778 // If the track has an auto min track sizing function and the grid container is being sized under a
779 // min-/max-content constraint, set the track’s base size to the maximum of its items’ limited
780 // min-/max-content contributions (respectively), floored at zero. The limited min-/max-content
781 // contribution of an item is (for this purpose) its min-/max-content contribution (accordingly),
782 // limited by the max track sizing function (which could be the argument to a fit-content() track
783 // sizing function) if that is fixed and ultimately floored by its minimum contribution (defined
784 // below).
785 // FIXME: Container min/max-content
786 case CSS::GridSize::Type::Length:
787 // Otherwise, set the track’s base size to the maximum of its items’ minimum contributions, floored
788 // at zero. The minimum contribution of an item is the smallest outer size it can have.
789 // Specifically, if the item’s computed preferred size behaves as auto or depends on the size of its
790 // containing block in the relevant axis, its minimum contribution is the outer size that would
791 // result from assuming the item’s used minimum size as its preferred size; else the item’s minimum
792 // contribution is its min-content contribution. Because the minimum contribution often depends on
793 // the size of the item’s content, it is considered a type of intrinsic size contribution.
794 case CSS::GridSize::Type::Percentage:
795 case CSS::GridSize::Type::FlexibleLength: {
796 CSSPixels grid_column_width = 0;
797 for (auto& box_of_column : boxes_of_column)
798 grid_column_width = max(grid_column_width, calculate_min_content_width(box_of_column).value());
799 grid_column.base_size = grid_column_width;
800 } break;
801 default:
802 VERIFY_NOT_REACHED();
803 }
804
805 switch (grid_column.max_track_sizing_function.type()) {
806 // - For min-content maximums:
807 // If the track has a min-content max track sizing function, set its growth limit to the maximum of
808 // the items’ min-content contributions.
809 case CSS::GridSize::Type::MinContent: {
810 CSSPixels column_width = 0;
811 for (auto& box_of_column : boxes_of_column)
812 column_width = max(column_width, calculate_min_content_width(box_of_column));
813 grid_column.growth_limit = column_width;
814 } break;
815 // - For max-content maximums:
816 // If the track has a max-content max track sizing function, set its growth limit to the maximum of
817 // the items’ max-content contributions. For fit-content() maximums, furthermore clamp this growth
818 // limit by the fit-content() argument.
819 case CSS::GridSize::Type::MaxContent: {
820 CSSPixels column_width = 0;
821 for (auto& box_of_column : boxes_of_column)
822 column_width = max(column_width, calculate_max_content_width(box_of_column));
823 grid_column.growth_limit = column_width;
824 } break;
825 case CSS::GridSize::Type::Length:
826 case CSS::GridSize::Type::Percentage:
827 case CSS::GridSize::Type::FlexibleLength:
828 break;
829 default:
830 VERIFY_NOT_REACHED();
831 }
832
833 // In all cases, if a track’s growth limit is now less than its base size, increase the growth limit
834 // to match the base size.
835 if (grid_column.growth_limit != -1 && grid_column.growth_limit < grid_column.base_size)
836 grid_column.growth_limit = grid_column.base_size;
837
838 ++index;
839 }
840
841 // https://www.w3.org/TR/css-grid-2/#auto-repeat
842 // The auto-fit keyword behaves the same as auto-fill, except that after grid item placement any
843 // empty repeated tracks are collapsed. An empty track is one with no in-flow grid items placed into
844 // or spanning across it. (This can result in all tracks being collapsed, if they’re all empty.)
845 if (box.computed_values().grid_template_columns().track_list().size() == 1
846 && box.computed_values().grid_template_columns().track_list().first().is_repeat()
847 && box.computed_values().grid_template_columns().track_list().first().repeat().is_auto_fit()) {
848 for (size_t idx = 0; idx < m_grid_columns.size(); idx++) {
849 auto column_to_check = box.computed_values().column_gap().is_auto() ? idx : idx / 2;
850 if (m_occupation_grid.is_occupied(column_to_check, 0))
851 continue;
852 if (!box.computed_values().column_gap().is_auto() && idx % 2 != 0)
853 continue;
854
855 // A collapsed track is treated as having a fixed track sizing function of 0px
856 m_grid_columns[idx].base_size = 0;
857 m_grid_columns[idx].growth_limit = 0;
858
859 // FIXME: And the gutters on either side of it—including any space allotted through distributed
860 // alignment—collapse.
861 }
862 }
863
864 // 3. Increase sizes to accommodate spanning items crossing content-sized tracks: Next, consider the
865 // items with a span of 2 that do not span a track with a flexible sizing function.
866 // FIXME: Content-sized tracks not implemented (min-content, etc.)
867
868 // 3.1. For intrinsic minimums: First increase the base size of tracks with an intrinsic min track sizing
869 // function by distributing extra space as needed to accommodate these items’ minimum contributions.
870
871 // If the grid container is being sized under a min- or max-content constraint, use the items’
872 // limited min-content contributions in place of their minimum contributions here. (For an item
873 // spanning multiple tracks, the upper limit used to calculate its limited min-/max-content
874 // contribution is the sum of the fixed max track sizing functions of any tracks it spans, and is
875 // applied if it only spans such tracks.)
876
877 // 3.2. For content-based minimums: Next continue to increase the base size of tracks with a min track
878 // sizing function of min-content or max-content by distributing extra space as needed to account
879 // for these items' min-content contributions.
880
881 // 3.3. For max-content minimums: Next, if the grid container is being sized under a max-content
882 // constraint, continue to increase the base size of tracks with a min track sizing function of auto
883 // or max-content by distributing extra space as needed to account for these items' limited
884 // max-content contributions.
885
886 // In all cases, continue to increase the base size of tracks with a min track sizing function of
887 // max-content by distributing extra space as needed to account for these items' max-content
888 // contributions.
889
890 // 3.4. If at this point any track’s growth limit is now less than its base size, increase its growth
891 // limit to match its base size.
892
893 // 3.5. For intrinsic maximums: Next increase the growth limit of tracks with an intrinsic max track
894 // sizing function by distributing extra space as needed to account for these items' min-content
895 // contributions. Mark any tracks whose growth limit changed from infinite to finite in this step as
896 // infinitely growable for the next step.
897
898 // 3.6. For max-content maximums: Lastly continue to increase the growth limit of tracks with a max track
899 // sizing function of max-content by distributing extra space as needed to account for these items'
900 // max-content contributions. However, limit the growth of any fit-content() tracks by their
901 // fit-content() argument.
902
903 // Repeat incrementally for items with greater spans until all items have been considered.
904
905 // FIXME: 4. Increase sizes to accommodate spanning items crossing flexible tracks: Next, repeat the previous
906 // step instead considering (together, rather than grouped by span size) all items that do span a
907 // track with a flexible sizing function while
908
909 // - distributing space only to flexible tracks (i.e. treating all other tracks as having a fixed
910 // sizing function)
911
912 // - if the sum of the flexible sizing functions of all flexible tracks spanned by the item is greater
913 // than zero, distributing space to such tracks according to the ratios of their flexible sizing
914 // functions rather than distributing space equally
915
916 // FIXME: 5. If any track still has an infinite growth limit (because, for example, it had no items placed in
917 // it or it is a flexible track), set its growth limit to its base size.
918
919 // https://www.w3.org/TR/css-grid-2/#extra-space
920 // 12.5.1. Distributing Extra Space Across Spanned Tracks
921 // To distribute extra space by increasing the affected sizes of a set of tracks as required by a
922 // set of intrinsic size contributions,
923 CSSPixels sum_of_track_sizes = 0;
924 for (auto& it : m_grid_columns)
925 sum_of_track_sizes += it.base_size;
926
927 // 1. Maintain separately for each affected base size or growth limit a planned increase, initially
928 // set to 0. (This prevents the size increases from becoming order-dependent.)
929
930 // 2. For each considered item,
931
932 // 2.1. Find the space to distribute: Subtract the corresponding size (base size or growth limit) of
933 // every spanned track from the item’s size contribution to find the item’s remaining size
934 // contribution. (For infinite growth limits, substitute the track’s base size.) This is the space
935 // to distribute. Floor it at zero.
936
937 // For base sizes, the limit is its growth limit. For growth limits, the limit is infinity if it is
938 // marked as infinitely growable, and equal to the growth limit otherwise. If the affected size was
939 // a growth limit and the track is not marked infinitely growable, then each item-incurred increase
940 // will be zero.
941 // extra-space = max(0, size-contribution - ∑track-sizes)
942 for (auto& grid_column : m_grid_columns) {
943 if (grid_column.is_gap)
944 continue;
945 grid_column.space_to_distribute = max(CSSPixels(0), (grid_column.growth_limit == -1 ? grid_column.base_size : grid_column.growth_limit) - grid_column.base_size);
946 }
947
948 auto remaining_free_space = available_space.width.is_definite() ? available_space.width.to_px() - sum_of_track_sizes : 0;
949 // 2.2. Distribute space up to limits: Find the item-incurred increase for each spanned track with an
950 // affected size by: distributing the space equally among such tracks, freezing a track’s
951 // item-incurred increase as its affected size + item-incurred increase reaches its limit (and
952 // continuing to grow the unfrozen tracks as needed).
953 auto count_of_unfrozen_tracks = 0;
954 for (auto& grid_column : m_grid_columns) {
955 if (grid_column.space_to_distribute > 0)
956 count_of_unfrozen_tracks++;
957 }
958 while (remaining_free_space > 0) {
959 if (count_of_unfrozen_tracks == 0)
960 break;
961 auto free_space_to_distribute_per_track = remaining_free_space / count_of_unfrozen_tracks;
962
963 for (auto& grid_column : m_grid_columns) {
964 if (grid_column.space_to_distribute == 0)
965 continue;
966 // 2.4. For each affected track, if the track’s item-incurred increase is larger than the track’s planned
967 // increase set the track’s planned increase to that value.
968 if (grid_column.space_to_distribute <= free_space_to_distribute_per_track) {
969 grid_column.planned_increase += grid_column.space_to_distribute;
970 remaining_free_space -= grid_column.space_to_distribute;
971 grid_column.space_to_distribute = 0;
972 } else {
973 grid_column.space_to_distribute -= free_space_to_distribute_per_track;
974 grid_column.planned_increase += free_space_to_distribute_per_track;
975 remaining_free_space -= free_space_to_distribute_per_track;
976 }
977 }
978
979 count_of_unfrozen_tracks = 0;
980 for (auto& grid_column : m_grid_columns) {
981 if (grid_column.space_to_distribute > 0)
982 count_of_unfrozen_tracks++;
983 }
984 if (remaining_free_space == 0)
985 break;
986 }
987
988 // 2.3. Distribute space beyond limits: If space remains after all tracks are frozen, unfreeze and
989 // continue to distribute space to the item-incurred increase of…
990
991 // - when accommodating minimum contributions or accommodating min-content contributions: any affected
992 // track that happens to also have an intrinsic max track sizing function; if there are no such
993 // tracks, then all affected tracks.
994
995 // - when accommodating max-content contributions: any affected track that happens to also have a
996 // max-content max track sizing function; if there are no such tracks, then all affected tracks.
997
998 // - when handling any intrinsic growth limit: all affected tracks.
999
1000 // For this purpose, the max track sizing function of a fit-content() track is treated as
1001 // max-content until it reaches the limit specified as the fit-content() argument, after which it is
1002 // treated as having a fixed sizing function of that argument.
1003
1004 // This step prioritizes the distribution of space for accommodating space required by the
1005 // tracks’ min track sizing functions beyond their current growth limits based on the types of their
1006 // max track sizing functions.
1007
1008 // 3. Update the tracks' affected sizes by adding in the planned increase so that the next round of
1009 // space distribution will account for the increase. (If the affected size is an infinite growth
1010 // limit, set it to the track’s base size plus the planned increase.)
1011 for (auto& grid_column : m_grid_columns)
1012 grid_column.base_size += grid_column.planned_increase;
1013
1014 // https://www.w3.org/TR/css-grid-2/#algo-grow-tracks
1015 // 12.6. Maximize Tracks
1016
1017 // If the free space is positive, distribute it equally to the base sizes of all tracks, freezing
1018 // tracks as they reach their growth limits (and continuing to grow the unfrozen tracks as needed).
1019 auto free_space = get_free_space_x(available_space);
1020 while (free_space > 0) {
1021 auto free_space_to_distribute_per_track = free_space / (m_grid_columns.size() - count_of_gap_columns());
1022 for (auto& grid_column : m_grid_columns) {
1023 if (grid_column.is_gap)
1024 continue;
1025 if (grid_column.growth_limit != -1)
1026 grid_column.base_size = min(grid_column.growth_limit, grid_column.base_size + free_space_to_distribute_per_track);
1027 else
1028 grid_column.base_size = grid_column.base_size + free_space_to_distribute_per_track;
1029 }
1030 if (get_free_space_x(available_space) == free_space)
1031 break;
1032 free_space = get_free_space_x(available_space);
1033 }
1034
1035 // For the purpose of this step: if sizing the grid container under a max-content constraint, the
1036 // free space is infinite; if sizing under a min-content constraint, the free space is zero.
1037
1038 // If this would cause the grid to be larger than the grid container’s inner size as limited by its
1039 // max-width/height, then redo this step, treating the available grid space as equal to the grid
1040 // container’s inner size when it’s sized to its max-width/height.
1041
1042 // https://drafts.csswg.org/css-grid/#algo-flex-tracks
1043 // 12.7. Expand Flexible Tracks
1044 // This step sizes flexible tracks using the largest value it can assign to an fr without exceeding
1045 // the available space.
1046
1047 // First, find the grid’s used flex fraction:
1048 auto column_flex_factor_sum = 0;
1049 for (auto& grid_column : m_grid_columns) {
1050 if (grid_column.min_track_sizing_function.is_flexible_length())
1051 column_flex_factor_sum++;
1052 }
1053 // See 12.7.1.
1054 // Let flex factor sum be the sum of the flex factors of the flexible tracks. If this value is less
1055 // than 1, set it to 1 instead.
1056 if (column_flex_factor_sum < 1)
1057 column_flex_factor_sum = 1;
1058
1059 // See 12.7.1.
1060 CSSPixels sized_column_widths = 0;
1061 for (auto& grid_column : m_grid_columns) {
1062 if (!grid_column.min_track_sizing_function.is_flexible_length())
1063 sized_column_widths += grid_column.base_size;
1064 }
1065 // Let leftover space be the space to fill minus the base sizes of the non-flexible grid tracks.
1066 CSSPixels free_horizontal_space = available_space.width.is_definite() ? available_space.width.to_px() - sized_column_widths : 0;
1067
1068 // If the free space is zero or if sizing the grid container under a min-content constraint:
1069 // The used flex fraction is zero.
1070 // FIXME: Add min-content constraint check.
1071
1072 // Otherwise, if the free space is a definite length:
1073 // The used flex fraction is the result of finding the size of an fr using all of the grid tracks
1074 // and a space to fill of the available grid space.
1075 if (free_horizontal_space > 0) {
1076 for (auto& grid_column : m_grid_columns) {
1077 if (grid_column.min_track_sizing_function.is_flexible_length()) {
1078 // See 12.7.1.
1079 // Let the hypothetical fr size be the leftover space divided by the flex factor sum.
1080 auto hypothetical_fr_size = free_horizontal_space / column_flex_factor_sum;
1081 // For each flexible track, if the product of the used flex fraction and the track’s flex factor is
1082 // greater than the track’s base size, set its base size to that product.
1083 grid_column.base_size = max(grid_column.base_size, hypothetical_fr_size);
1084 }
1085 }
1086 }
1087
1088 // Otherwise, if the free space is an indefinite length:
1089 // FIXME: No tracks will have indefinite length as per current implementation.
1090
1091 // The used flex fraction is the maximum of:
1092 // For each flexible track, if the flexible track’s flex factor is greater than one, the result of
1093 // dividing the track’s base size by its flex factor; otherwise, the track’s base size.
1094
1095 // For each grid item that crosses a flexible track, the result of finding the size of an fr using
1096 // all the grid tracks that the item crosses and a space to fill of the item’s max-content
1097 // contribution.
1098
1099 // If using this flex fraction would cause the grid to be smaller than the grid container’s
1100 // min-width/height (or larger than the grid container’s max-width/height), then redo this step,
1101 // treating the free space as definite and the available grid space as equal to the grid container’s
1102 // inner size when it’s sized to its min-width/height (max-width/height).
1103
1104 // For each flexible track, if the product of the used flex fraction and the track’s flex factor is
1105 // greater than the track’s base size, set its base size to that product.
1106
1107 // https://drafts.csswg.org/css-grid/#algo-find-fr-size
1108 // 12.7.1. Find the Size of an fr
1109
1110 // This algorithm finds the largest size that an fr unit can be without exceeding the target size.
1111 // It must be called with a set of grid tracks and some quantity of space to fill.
1112
1113 // 1. Let leftover space be the space to fill minus the base sizes of the non-flexible grid tracks.
1114
1115 // 2. Let flex factor sum be the sum of the flex factors of the flexible tracks. If this value is less
1116 // than 1, set it to 1 instead.
1117
1118 // 3. Let the hypothetical fr size be the leftover space divided by the flex factor sum.
1119
1120 // FIXME: 4. If the product of the hypothetical fr size and a flexible track’s flex factor is less than the
1121 // track’s base size, restart this algorithm treating all such tracks as inflexible.
1122
1123 // 5. Return the hypothetical fr size.
1124
1125 // https://drafts.csswg.org/css-grid/#algo-stretch
1126 // 12.8. Stretch auto Tracks
1127
1128 // When the content-distribution property of the grid container is normal or stretch in this axis,
1129 // this step expands tracks that have an auto max track sizing function by dividing any remaining
1130 // positive, definite free space equally amongst them. If the free space is indefinite, but the grid
1131 // container has a definite min-width/height, use that size to calculate the free space for this
1132 // step instead.
1133 CSSPixels used_horizontal_space = 0;
1134 for (auto& grid_column : m_grid_columns) {
1135 if (!(grid_column.max_track_sizing_function.is_length() && grid_column.max_track_sizing_function.length().is_auto()))
1136 used_horizontal_space += grid_column.base_size;
1137 }
1138
1139 CSSPixels remaining_horizontal_space = available_space.width.is_definite() ? available_space.width.to_px() - used_horizontal_space : 0;
1140 auto count_of_auto_max_column_tracks = 0;
1141 for (auto& grid_column : m_grid_columns) {
1142 if (grid_column.max_track_sizing_function.is_length() && grid_column.max_track_sizing_function.length().is_auto())
1143 count_of_auto_max_column_tracks++;
1144 }
1145 for (auto& grid_column : m_grid_columns) {
1146 if (grid_column.max_track_sizing_function.is_length() && grid_column.max_track_sizing_function.length().is_auto())
1147 grid_column.base_size = max(grid_column.base_size, remaining_horizontal_space / count_of_auto_max_column_tracks);
1148 }
1149
1150 // If calculating the layout of a grid item in this step depends on the available space in the block
1151 // axis, assume the available space that it would have if any row with a definite max track sizing
1152 // function had that size and all other rows were infinite. If both the grid container and all
1153 // tracks have definite sizes, also apply align-content to find the final effective size of any gaps
1154 // spanned by such items; otherwise ignore the effects of track alignment in this estimation.
1155}
1156
1157void GridFormattingContext::calculate_sizes_of_rows(Box const& box)
1158{
1159 // https://www.w3.org/TR/css-grid-2/#algo-init
1160 // 12.4. Initialize Track Sizes
1161 // Initialize each track’s base size and growth limit.
1162 auto& box_state = m_state.get_mutable(box);
1163 for (auto& grid_row : m_grid_rows) {
1164 if (grid_row.is_gap)
1165 continue;
1166 // For each track, if the track’s min track sizing function is:
1167 switch (grid_row.min_track_sizing_function.type()) {
1168 // - A fixed sizing function
1169 // Resolve to an absolute length and use that size as the track’s initial base size.
1170 case CSS::GridSize::Type::Length:
1171 if (!grid_row.min_track_sizing_function.length().is_auto())
1172 grid_row.base_size = grid_row.min_track_sizing_function.length().to_px(box);
1173 break;
1174 case CSS::GridSize::Type::Percentage:
1175 grid_row.base_size = grid_row.min_track_sizing_function.percentage().as_fraction() * box_state.content_height();
1176 break;
1177 // - An intrinsic sizing function
1178 // Use an initial base size of zero.
1179 case CSS::GridSize::Type::FlexibleLength:
1180 case CSS::GridSize::Type::MaxContent:
1181 case CSS::GridSize::Type::MinContent:
1182 break;
1183 default:
1184 VERIFY_NOT_REACHED();
1185 }
1186
1187 // For each track, if the track’s max track sizing function is:
1188 switch (grid_row.max_track_sizing_function.type()) {
1189 // - A fixed sizing function
1190 // Resolve to an absolute length and use that size as the track’s initial growth limit.
1191 case CSS::GridSize::Type::Length:
1192 if (!grid_row.max_track_sizing_function.length().is_auto())
1193 grid_row.growth_limit = grid_row.max_track_sizing_function.length().to_px(box);
1194 else
1195 // - An intrinsic sizing function
1196 // Use an initial growth limit of infinity.
1197 grid_row.growth_limit = -1;
1198 break;
1199 case CSS::GridSize::Type::Percentage:
1200 grid_row.growth_limit = grid_row.max_track_sizing_function.percentage().as_fraction() * box_state.content_height();
1201 break;
1202 // - A flexible sizing function
1203 // Use an initial growth limit of infinity.
1204 case CSS::GridSize::Type::FlexibleLength:
1205 grid_row.growth_limit = -1;
1206 break;
1207 // - An intrinsic sizing function
1208 // Use an initial growth limit of infinity.
1209 case CSS::GridSize::Type::MaxContent:
1210 case CSS::GridSize::Type::MinContent:
1211 grid_row.growth_limit = -1;
1212 break;
1213 default:
1214 VERIFY_NOT_REACHED();
1215 }
1216
1217 // In all cases, if the growth limit is less than the base size, increase the growth limit to match
1218 // the base size.
1219 if (grid_row.growth_limit != -1 && grid_row.growth_limit < grid_row.base_size)
1220 grid_row.growth_limit = grid_row.base_size;
1221 }
1222
1223 // https://www.w3.org/TR/css-grid-2/#algo-content
1224 // 12.5. Resolve Intrinsic Track Sizes
1225 // This step resolves intrinsic track sizing functions to absolute lengths. First it resolves those
1226 // sizes based on items that are contained wholly within a single track. Then it gradually adds in
1227 // the space requirements of items that span multiple tracks, evenly distributing the extra space
1228 // across those tracks insofar as possible.
1229
1230 // FIXME: 1. Shim baseline-aligned items so their intrinsic size contributions reflect their baseline
1231 // alignment. For the items in each baseline-sharing group, add a “shim” (effectively, additional
1232 // margin) on the start/end side (for first/last-baseline alignment) of each item so that, when
1233 // start/end-aligned together their baselines align as specified.
1234
1235 // Consider these “shims” as part of the items’ intrinsic size contribution for the purpose of track
1236 // sizing, below. If an item uses multiple intrinsic size contributions, it can have different shims
1237 // for each one.
1238
1239 // 2. Size tracks to fit non-spanning items: For each track with an intrinsic track sizing function and
1240 // not a flexible sizing function, consider the items in it with a span of 1:
1241 auto index = 0;
1242 for (auto& grid_row : m_grid_rows) {
1243 if (grid_row.is_gap) {
1244 ++index;
1245 continue;
1246 }
1247 if (!grid_row.min_track_sizing_function.is_intrinsic_track_sizing()) {
1248 ++index;
1249 continue;
1250 }
1251
1252 Vector<PositionedBox&> positioned_boxes_of_row;
1253 for (auto& positioned_box : m_positioned_boxes) {
1254 if (positioned_box.gap_adjusted_row(box) == index && positioned_box.raw_row_span() == 1)
1255 positioned_boxes_of_row.append(positioned_box);
1256 }
1257
1258 switch (grid_row.min_track_sizing_function.type()) {
1259 // - For min-content minimums:
1260 // If the track has a min-content min track sizing function, set its base size to the maximum of the
1261 // items’ min-content contributions, floored at zero.
1262 case CSS::GridSize::Type::MinContent: {
1263 CSSPixels row_height = 0;
1264 for (auto& positioned_box : positioned_boxes_of_row)
1265 row_height = max(row_height, calculate_min_content_height(positioned_box.box(), AvailableSize::make_definite(m_grid_columns[positioned_box.gap_adjusted_column(box)].base_size)));
1266 grid_row.base_size = row_height;
1267 } break;
1268 // - For max-content minimums:
1269 // If the track has a max-content min track sizing function, set its base size to the maximum of the
1270 // items’ max-content contributions, floored at zero.
1271 case CSS::GridSize::Type::MaxContent: {
1272 CSSPixels row_height = 0;
1273 for (auto& positioned_box : positioned_boxes_of_row)
1274 row_height = max(row_height, calculate_max_content_height(positioned_box.box(), AvailableSize::make_definite(m_grid_columns[positioned_box.gap_adjusted_column(box)].base_size)));
1275 grid_row.base_size = row_height;
1276 } break;
1277 // - For auto minimums:
1278 // If the track has an auto min track sizing function and the grid container is being sized under a
1279 // min-/max-content constraint, set the track’s base size to the maximum of its items’ limited
1280 // min-/max-content contributions (respectively), floored at zero. The limited min-/max-content
1281 // contribution of an item is (for this purpose) its min-/max-content contribution (accordingly),
1282 // limited by the max track sizing function (which could be the argument to a fit-content() track
1283 // sizing function) if that is fixed and ultimately floored by its minimum contribution (defined
1284 // below).
1285 // FIXME: Container min/max-content
1286 case CSS::GridSize::Type::Length:
1287 // Otherwise, set the track’s base size to the maximum of its items’ minimum contributions, floored
1288 // at zero. The minimum contribution of an item is the smallest outer size it can have.
1289 // Specifically, if the item’s computed preferred size behaves as auto or depends on the size of its
1290 // containing block in the relevant axis, its minimum contribution is the outer size that would
1291 // result from assuming the item’s used minimum size as its preferred size; else the item’s minimum
1292 // contribution is its min-content contribution. Because the minimum contribution often depends on
1293 // the size of the item’s content, it is considered a type of intrinsic size contribution.
1294 case CSS::GridSize::Type::Percentage:
1295 case CSS::GridSize::Type::FlexibleLength: {
1296 CSSPixels grid_row_height = 0;
1297 for (auto& positioned_box : positioned_boxes_of_row)
1298 grid_row_height = max(grid_row_height, calculate_min_content_height(positioned_box.box(), AvailableSize::make_definite(m_grid_columns[positioned_box.gap_adjusted_column(box)].base_size)));
1299 grid_row.base_size = grid_row_height;
1300 } break;
1301 default:
1302 VERIFY_NOT_REACHED();
1303 }
1304
1305 switch (grid_row.max_track_sizing_function.type()) {
1306 // - For min-content maximums:
1307 // If the track has a min-content max track sizing function, set its growth limit to the maximum of
1308 // the items’ min-content contributions.
1309 case CSS::GridSize::Type::MinContent: {
1310 CSSPixels row_height = 0;
1311 for (auto& positioned_box : positioned_boxes_of_row)
1312 row_height = max(row_height, calculate_max_content_height(positioned_box.box(), AvailableSize::make_definite(m_grid_columns[positioned_box.gap_adjusted_column(box)].base_size)));
1313 grid_row.base_size = row_height;
1314 } break;
1315 // - For max-content maximums:
1316 // If the track has a max-content max track sizing function, set its growth limit to the maximum of
1317 // the items’ max-content contributions. For fit-content() maximums, furthermore clamp this growth
1318 // limit by the fit-content() argument.
1319 case CSS::GridSize::Type::MaxContent: {
1320 CSSPixels row_height = 0;
1321 for (auto& positioned_box : positioned_boxes_of_row)
1322 row_height = max(row_height, calculate_max_content_height(positioned_box.box(), AvailableSize::make_definite(m_grid_columns[positioned_box.gap_adjusted_column(box)].base_size)));
1323 grid_row.base_size = row_height;
1324 } break;
1325 case CSS::GridSize::Type::Length:
1326 case CSS::GridSize::Type::Percentage:
1327 case CSS::GridSize::Type::FlexibleLength:
1328 break;
1329 default:
1330 VERIFY_NOT_REACHED();
1331 }
1332
1333 // In all cases, if a track’s growth limit is now less than its base size, increase the growth limit
1334 // to match the base size.
1335 if (grid_row.growth_limit != -1 && grid_row.growth_limit < grid_row.base_size)
1336 grid_row.growth_limit = grid_row.base_size;
1337 ++index;
1338 }
1339
1340 // https://www.w3.org/TR/css-grid-2/#auto-repeat
1341 // The auto-fit keyword behaves the same as auto-fill, except that after grid item placement any
1342 // empty repeated tracks are collapsed. An empty track is one with no in-flow grid items placed into
1343 // or spanning across it. (This can result in all tracks being collapsed, if they’re all empty.)
1344
1345 // 3. Increase sizes to accommodate spanning items crossing content-sized tracks: Next, consider the
1346 // items with a span of 2 that do not span a track with a flexible sizing function.
1347 // FIXME: Content-sized tracks not implemented (min-content, etc.)
1348
1349 // 3.1. For intrinsic minimums: First increase the base size of tracks with an intrinsic min track sizing
1350 // function by distributing extra space as needed to accommodate these items’ minimum contributions.
1351
1352 // If the grid container is being sized under a min- or max-content constraint, use the items’
1353 // limited min-content contributions in place of their minimum contributions here. (For an item
1354 // spanning multiple tracks, the upper limit used to calculate its limited min-/max-content
1355 // contribution is the sum of the fixed max track sizing functions of any tracks it spans, and is
1356 // applied if it only spans such tracks.)
1357
1358 // 3.2. For content-based minimums: Next continue to increase the base size of tracks with a min track
1359 // sizing function of min-content or max-content by distributing extra space as needed to account
1360 // for these items' min-content contributions.
1361
1362 // 3.3. For max-content minimums: Next, if the grid container is being sized under a max-content
1363 // constraint, continue to increase the base size of tracks with a min track sizing function of auto
1364 // or max-content by distributing extra space as needed to account for these items' limited
1365 // max-content contributions.
1366
1367 // In all cases, continue to increase the base size of tracks with a min track sizing function of
1368 // max-content by distributing extra space as needed to account for these items' max-content
1369 // contributions.
1370
1371 // 3.4. If at this point any track’s growth limit is now less than its base size, increase its growth
1372 // limit to match its base size.
1373
1374 // 3.5. For intrinsic maximums: Next increase the growth limit of tracks with an intrinsic max track
1375 // sizing function by distributing extra space as needed to account for these items' min-content
1376 // contributions. Mark any tracks whose growth limit changed from infinite to finite in this step as
1377 // infinitely growable for the next step.
1378
1379 // 3.6. For max-content maximums: Lastly continue to increase the growth limit of tracks with a max track
1380 // sizing function of max-content by distributing extra space as needed to account for these items'
1381 // max-content contributions. However, limit the growth of any fit-content() tracks by their
1382 // fit-content() argument.
1383
1384 // Repeat incrementally for items with greater spans until all items have been considered.
1385
1386 // FIXME: 4. Increase sizes to accommodate spanning items crossing flexible tracks: Next, repeat the previous
1387 // step instead considering (together, rather than grouped by span size) all items that do span a
1388 // track with a flexible sizing function while
1389
1390 // - distributing space only to flexible tracks (i.e. treating all other tracks as having a fixed
1391 // sizing function)
1392
1393 // - if the sum of the flexible sizing functions of all flexible tracks spanned by the item is greater
1394 // than zero, distributing space to such tracks according to the ratios of their flexible sizing
1395 // functions rather than distributing space equally
1396
1397 // FIXME: 5. If any track still has an infinite growth limit (because, for example, it had no items placed in
1398 // it or it is a flexible track), set its growth limit to its base size.
1399
1400 // https://www.w3.org/TR/css-grid-2/#extra-space
1401 // 12.5.1. Distributing Extra Space Across Spanned Tracks
1402 // To distribute extra space by increasing the affected sizes of a set of tracks as required by a
1403 // set of intrinsic size contributions,
1404
1405 // 1. Maintain separately for each affected base size or growth limit a planned increase, initially
1406 // set to 0. (This prevents the size increases from becoming order-dependent.)
1407
1408 // 2. For each considered item,
1409
1410 // 2.1. Find the space to distribute: Subtract the corresponding size (base size or growth limit) of
1411 // every spanned track from the item’s size contribution to find the item’s remaining size
1412 // contribution. (For infinite growth limits, substitute the track’s base size.) This is the space
1413 // to distribute. Floor it at zero.
1414
1415 // For base sizes, the limit is its growth limit. For growth limits, the limit is infinity if it is
1416 // marked as infinitely growable, and equal to the growth limit otherwise. If the affected size was
1417 // a growth limit and the track is not marked infinitely growable, then each item-incurred increase
1418 // will be zero.
1419 // extra-space = max(0, size-contribution - ∑track-sizes)
1420
1421 // 2.2. Distribute space up to limits: Find the item-incurred increase for each spanned track with an
1422 // affected size by: distributing the space equally among such tracks, freezing a track’s
1423 // item-incurred increase as its affected size + item-incurred increase reaches its limit (and
1424 // continuing to grow the unfrozen tracks as needed).
1425
1426 // 2.3. Distribute space beyond limits: If space remains after all tracks are frozen, unfreeze and
1427 // continue to distribute space to the item-incurred increase of…
1428
1429 // - when accommodating minimum contributions or accommodating min-content contributions: any affected
1430 // track that happens to also have an intrinsic max track sizing function; if there are no such
1431 // tracks, then all affected tracks.
1432
1433 // - when accommodating max-content contributions: any affected track that happens to also have a
1434 // max-content max track sizing function; if there are no such tracks, then all affected tracks.
1435
1436 // - when handling any intrinsic growth limit: all affected tracks.
1437
1438 // For this purpose, the max track sizing function of a fit-content() track is treated as
1439 // max-content until it reaches the limit specified as the fit-content() argument, after which it is
1440 // treated as having a fixed sizing function of that argument.
1441
1442 // This step prioritizes the distribution of space for accommodating space required by the
1443 // tracks’ min track sizing functions beyond their current growth limits based on the types of their
1444 // max track sizing functions.
1445
1446 // 3. Update the tracks' affected sizes by adding in the planned increase so that the next round of
1447 // space distribution will account for the increase. (If the affected size is an infinite growth
1448 // limit, set it to the track’s base size plus the planned increase.)
1449 // FIXME: Do for rows.
1450
1451 // https://www.w3.org/TR/css-grid-2/#algo-grow-tracks
1452 // 12.6. Maximize Tracks
1453
1454 // If the free space is positive, distribute it equally to the base sizes of all tracks, freezing
1455 // tracks as they reach their growth limits (and continuing to grow the unfrozen tracks as needed).
1456
1457 auto free_space = get_free_space_y(box);
1458 while (free_space > 0) {
1459 auto free_space_to_distribute_per_track = free_space / (m_grid_rows.size() - count_of_gap_rows());
1460 for (auto& grid_row : m_grid_rows) {
1461 if (grid_row.is_gap)
1462 continue;
1463 grid_row.base_size = min(grid_row.growth_limit, grid_row.base_size + free_space_to_distribute_per_track);
1464 }
1465 if (get_free_space_y(box) == free_space)
1466 break;
1467 free_space = get_free_space_y(box);
1468 }
1469 if (free_space == -1) {
1470 for (auto& grid_row : m_grid_rows) {
1471 if (grid_row.is_gap)
1472 continue;
1473 if (grid_row.growth_limit != -1)
1474 grid_row.base_size = grid_row.growth_limit;
1475 }
1476 }
1477
1478 // For the purpose of this step: if sizing the grid container under a max-content constraint, the
1479 // free space is infinite; if sizing under a min-content constraint, the free space is zero.
1480
1481 // If this would cause the grid to be larger than the grid container’s inner size as limited by its
1482 // max-width/height, then redo this step, treating the available grid space as equal to the grid
1483 // container’s inner size when it’s sized to its max-width/height.
1484
1485 // https://drafts.csswg.org/css-grid/#algo-flex-tracks
1486 // 12.7. Expand Flexible Tracks
1487 // This step sizes flexible tracks using the largest value it can assign to an fr without exceeding
1488 // the available space.
1489
1490 // First, find the grid’s used flex fraction:
1491 auto row_flex_factor_sum = 0;
1492 for (auto& grid_row : m_grid_rows) {
1493 if (grid_row.min_track_sizing_function.is_flexible_length())
1494 row_flex_factor_sum++;
1495 }
1496 // See 12.7.1.
1497 // Let flex factor sum be the sum of the flex factors of the flexible tracks. If this value is less
1498 // than 1, set it to 1 instead.
1499 if (row_flex_factor_sum < 1)
1500 row_flex_factor_sum = 1;
1501
1502 // See 12.7.1.
1503 CSSPixels sized_row_heights = 0;
1504 for (auto& grid_row : m_grid_rows) {
1505 if (!grid_row.min_track_sizing_function.is_flexible_length())
1506 sized_row_heights += grid_row.base_size;
1507 }
1508 // Let leftover space be the space to fill minus the base sizes of the non-flexible grid tracks.
1509 CSSPixels free_vertical_space = CSSPixels(box_state.content_height()) - sized_row_heights;
1510
1511 // If the free space is zero or if sizing the grid container under a min-content constraint:
1512 // The used flex fraction is zero.
1513 // FIXME: Add min-content constraint check.
1514
1515 // Otherwise, if the free space is a definite length:
1516 // The used flex fraction is the result of finding the size of an fr using all of the grid tracks
1517 // and a space to fill of the available grid space.
1518 if (free_vertical_space > 0) {
1519 for (auto& grid_row : m_grid_rows) {
1520 if (grid_row.min_track_sizing_function.is_flexible_length()) {
1521 // See 12.7.1.
1522 // Let the hypothetical fr size be the leftover space divided by the flex factor sum.
1523 auto hypothetical_fr_size = free_vertical_space / row_flex_factor_sum;
1524 // For each flexible track, if the product of the used flex fraction and the track’s flex factor is
1525 // greater than the track’s base size, set its base size to that product.
1526 grid_row.base_size = max(grid_row.base_size, hypothetical_fr_size);
1527 }
1528 }
1529 }
1530
1531 // Otherwise, if the free space is an indefinite length:
1532 // FIXME: No tracks will have indefinite length as per current implementation.
1533
1534 // The used flex fraction is the maximum of:
1535 // For each flexible track, if the flexible track’s flex factor is greater than one, the result of
1536 // dividing the track’s base size by its flex factor; otherwise, the track’s base size.
1537
1538 // For each grid item that crosses a flexible track, the result of finding the size of an fr using
1539 // all the grid tracks that the item crosses and a space to fill of the item’s max-content
1540 // contribution.
1541
1542 // If using this flex fraction would cause the grid to be smaller than the grid container’s
1543 // min-width/height (or larger than the grid container’s max-width/height), then redo this step,
1544 // treating the free space as definite and the available grid space as equal to the grid container’s
1545 // inner size when it’s sized to its min-width/height (max-width/height).
1546
1547 // For each flexible track, if the product of the used flex fraction and the track’s flex factor is
1548 // greater than the track’s base size, set its base size to that product.
1549
1550 // https://drafts.csswg.org/css-grid/#algo-find-fr-size
1551 // 12.7.1. Find the Size of an fr
1552
1553 // This algorithm finds the largest size that an fr unit can be without exceeding the target size.
1554 // It must be called with a set of grid tracks and some quantity of space to fill.
1555
1556 // 1. Let leftover space be the space to fill minus the base sizes of the non-flexible grid tracks.
1557
1558 // 2. Let flex factor sum be the sum of the flex factors of the flexible tracks. If this value is less
1559 // than 1, set it to 1 instead.
1560
1561 // 3. Let the hypothetical fr size be the leftover space divided by the flex factor sum.
1562
1563 // FIXME: 4. If the product of the hypothetical fr size and a flexible track’s flex factor is less than the
1564 // track’s base size, restart this algorithm treating all such tracks as inflexible.
1565
1566 // 5. Return the hypothetical fr size.
1567
1568 // https://drafts.csswg.org/css-grid/#algo-stretch
1569 // 12.8. Stretch auto Tracks
1570
1571 // When the content-distribution property of the grid container is normal or stretch in this axis,
1572 // this step expands tracks that have an auto max track sizing function by dividing any remaining
1573 // positive, definite free space equally amongst them. If the free space is indefinite, but the grid
1574 // container has a definite min-width/height, use that size to calculate the free space for this
1575 // step instead.
1576 CSSPixels used_vertical_space = 0;
1577 for (auto& grid_row : m_grid_rows) {
1578 if (!(grid_row.max_track_sizing_function.is_length() && grid_row.max_track_sizing_function.length().is_auto()))
1579 used_vertical_space += grid_row.base_size;
1580 }
1581
1582 CSSPixels remaining_vertical_space = CSSPixels(box_state.content_height()) - used_vertical_space;
1583 auto count_of_auto_max_row_tracks = 0;
1584 for (auto& grid_row : m_grid_rows) {
1585 if (grid_row.max_track_sizing_function.is_length() && grid_row.max_track_sizing_function.length().is_auto())
1586 count_of_auto_max_row_tracks++;
1587 }
1588 for (auto& grid_row : m_grid_rows) {
1589 if (grid_row.max_track_sizing_function.is_length() && grid_row.max_track_sizing_function.length().is_auto())
1590 grid_row.base_size = max(grid_row.base_size, remaining_vertical_space / count_of_auto_max_row_tracks);
1591 }
1592}
1593
1594void GridFormattingContext::build_valid_grid_areas(Box const& box)
1595{
1596 Vector<GridArea> found_grid_areas;
1597
1598 auto get_index_of_found_grid_area = [&](String needle) -> int {
1599 for (size_t x = 0; x < found_grid_areas.size(); x++) {
1600 if (found_grid_areas[x].name == needle)
1601 return static_cast<int>(x);
1602 }
1603 return -1;
1604 };
1605
1606 // https://www.w3.org/TR/css-grid-2/#grid-template-areas-property
1607 // If a named grid area spans multiple grid cells, but those cells do not form a single
1608 // filled-in rectangle, the declaration is invalid.
1609 for (int y = 0; y < static_cast<int>(box.computed_values().grid_template_areas().size()); y++) {
1610 for (int x = 0; x < static_cast<int>(box.computed_values().grid_template_areas()[y].size()); x++) {
1611 auto grid_area_idx = get_index_of_found_grid_area(box.computed_values().grid_template_areas()[y][x]);
1612 if (grid_area_idx == -1) {
1613 found_grid_areas.append({ box.computed_values().grid_template_areas()[y][x], y, y + 1, x, x + 1 });
1614 } else {
1615 auto& grid_area = found_grid_areas[grid_area_idx];
1616 if (grid_area.row_start == y) {
1617 if (grid_area.column_end == x)
1618 grid_area.column_end = grid_area.column_end + 1;
1619 else
1620 return;
1621 } else {
1622 if (grid_area.row_end == y) {
1623 if (grid_area.column_start != x)
1624 return;
1625 grid_area.row_end = grid_area.row_end + 1;
1626 } else if (grid_area.row_end == y + 1) {
1627 if (grid_area.column_end < x || grid_area.column_end > x + 1)
1628 return;
1629 } else {
1630 return;
1631 }
1632 }
1633 }
1634 }
1635 }
1636
1637 for (auto const& checked_grid_area : found_grid_areas)
1638 m_valid_grid_areas.append(checked_grid_area);
1639}
1640
1641int GridFormattingContext::find_valid_grid_area(String const& needle)
1642{
1643 for (size_t x = 0; x < m_valid_grid_areas.size(); x++) {
1644 if (m_valid_grid_areas[x].name == needle)
1645 return static_cast<int>(x);
1646 }
1647 return -1;
1648}
1649
1650void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const& available_space)
1651{
1652 auto grid_template_columns = box.computed_values().grid_template_columns();
1653 auto grid_template_rows = box.computed_values().grid_template_rows();
1654
1655 // https://drafts.csswg.org/css-grid/#overview-placement
1656 // 2.2. Placing Items
1657 // The contents of the grid container are organized into individual grid items (analogous to
1658 // flex items), which are then assigned to predefined areas in the grid. They can be explicitly
1659 // placed using coordinates through the grid-placement properties or implicitly placed into
1660 // empty areas using auto-placement.
1661 box.for_each_child_of_type<Box>([&](Box& child_box) {
1662 if (can_skip_is_anonymous_text_run(child_box))
1663 return IterationDecision::Continue;
1664 m_boxes_to_place.append(child_box);
1665 return IterationDecision::Continue;
1666 });
1667
1668 auto column_count = get_count_of_tracks(grid_template_columns.track_list(), available_space, box);
1669 auto row_count = get_count_of_tracks(grid_template_rows.track_list(), available_space, box);
1670
1671 m_occupation_grid = OccupationGrid(column_count, row_count);
1672
1673 build_valid_grid_areas(box);
1674
1675 // https://drafts.csswg.org/css-grid/#auto-placement-algo
1676 // 8.5. Grid Item Placement Algorithm
1677
1678 // FIXME: 0. Generate anonymous grid items
1679
1680 // 1. Position anything that's not auto-positioned.
1681 for (size_t i = 0; i < m_boxes_to_place.size(); i++) {
1682 auto const& child_box = m_boxes_to_place[i];
1683 if (is_auto_positioned_row(child_box.computed_values().grid_row_start(), child_box.computed_values().grid_row_end())
1684 || is_auto_positioned_column(child_box.computed_values().grid_column_start(), child_box.computed_values().grid_column_end()))
1685 continue;
1686 place_item_with_row_and_column_position(box, child_box);
1687 m_boxes_to_place.remove(i);
1688 i--;
1689 }
1690
1691 // 2. Process the items locked to a given row.
1692 // FIXME: Do "dense" packing
1693 for (size_t i = 0; i < m_boxes_to_place.size(); i++) {
1694 auto const& child_box = m_boxes_to_place[i];
1695 if (is_auto_positioned_row(child_box.computed_values().grid_row_start(), child_box.computed_values().grid_row_end()))
1696 continue;
1697 place_item_with_row_position(box, child_box);
1698 m_boxes_to_place.remove(i);
1699 i--;
1700 }
1701
1702 // 3. Determine the columns in the implicit grid.
1703 // NOTE: "implicit grid" here is the same as the m_occupation_grid
1704
1705 // 3.1. Start with the columns from the explicit grid.
1706 // NOTE: Done in step 1.
1707
1708 // 3.2. Among all the items with a definite column position (explicitly positioned items, items
1709 // positioned in the previous step, and items not yet positioned but with a definite column) add
1710 // columns to the beginning and end of the implicit grid as necessary to accommodate those items.
1711 // NOTE: "Explicitly positioned items" and "items positioned in the previous step" done in step 1
1712 // and 2, respectively. Adding columns for "items not yet positioned but with a definite column"
1713 // will be done in step 4.
1714
1715 // 4. Position the remaining grid items.
1716 // For each grid item that hasn't been positioned by the previous steps, in order-modified document
1717 // order:
1718 auto auto_placement_cursor_x = 0;
1719 auto auto_placement_cursor_y = 0;
1720 for (size_t i = 0; i < m_boxes_to_place.size(); i++) {
1721 auto const& child_box = m_boxes_to_place[i];
1722 // 4.1. For sparse packing:
1723 // FIXME: no distinction made. See #4.2
1724
1725 // 4.1.1. If the item has a definite column position:
1726 if (!is_auto_positioned_column(child_box.computed_values().grid_column_start(), child_box.computed_values().grid_column_end()))
1727 place_item_with_column_position(box, child_box, auto_placement_cursor_x, auto_placement_cursor_y);
1728
1729 // 4.1.2. If the item has an automatic grid position in both axes:
1730 else
1731 place_item_with_no_declared_position(child_box, auto_placement_cursor_x, auto_placement_cursor_y);
1732
1733 m_boxes_to_place.remove(i);
1734 i--;
1735
1736 // FIXME: 4.2. For dense packing:
1737 }
1738
1739 // https://drafts.csswg.org/css-grid/#overview-sizing
1740 // 2.3. Sizing the Grid
1741 // Once the grid items have been placed, the sizes of the grid tracks (rows and columns) are
1742 // calculated, accounting for the sizes of their contents and/or available space as specified in
1743 // the grid definition.
1744
1745 // https://www.w3.org/TR/css-grid-2/#layout-algorithm
1746 // 12. Grid Sizing
1747 // This section defines the grid sizing algorithm, which determines the size of all grid tracks and,
1748 // by extension, the entire grid.
1749
1750 // Each track has specified minimum and maximum sizing functions (which may be the same). Each
1751 // sizing function is either:
1752
1753 // - A fixed sizing function (<length> or resolvable <percentage>).
1754 // - An intrinsic sizing function (min-content, max-content, auto, fit-content()).
1755 // - A flexible sizing function (<flex>).
1756
1757 // The grid sizing algorithm defines how to resolve these sizing constraints into used track sizes.
1758 initialize_grid_tracks(box, available_space, column_count, row_count);
1759
1760 // https://www.w3.org/TR/css-grid-2/#algo-overview
1761 // 12.1. Grid Sizing Algorithm
1762
1763 // 1. First, the track sizing algorithm is used to resolve the sizes of the grid columns.
1764 // In this process, any grid item which is subgridded in the grid container’s inline axis is treated
1765 // as empty and its grid items (the grandchildren) are treated as direct children of the grid
1766 // container (their grandparent). This introspection is recursive.
1767
1768 // Items which are subgridded only in the block axis, and whose grid container size in the inline
1769 // axis depends on the size of its contents are also introspected: since the size of the item in
1770 // this dimension can be dependent on the sizing of its subgridded tracks in the other, the size
1771 // contribution of any such item to this grid’s column sizing (see Resolve Intrinsic Track Sizes) is
1772 // taken under the provision of having determined its track sizing only up to the same point in the
1773 // Grid Sizing Algorithm as this itself. E.g. for the first pass through this step, the item will
1774 // have its tracks sized only through this first step; if a second pass of this step is triggered
1775 // then the item will have completed a first pass through steps 1-3 as well as the second pass of
1776 // this step prior to returning its size for consideration in this grid’s column sizing. Again, this
1777 // introspection is recursive.
1778
1779 // https://www.w3.org/TR/css-grid-2/#algo-track-sizing
1780 // 12.3. Track Sizing Algorithm
1781
1782 // The remainder of this section is the track sizing algorithm, which calculates from the min and
1783 // max track sizing functions the used track size. Each track has a base size, a <length> which
1784 // grows throughout the algorithm and which will eventually be the track’s final size, and a growth
1785 // limit, a <length> which provides a desired maximum size for the base size. There are 5 steps:
1786
1787 // 1. Initialize Track Sizes
1788 // 2. Resolve Intrinsic Track Sizes
1789 // 3. Maximize Tracks
1790 // 4. Expand Flexible Tracks
1791 // 5. Expand Stretched auto Tracks
1792
1793 calculate_sizes_of_columns(box, available_space);
1794
1795 // https://www.w3.org/TR/css-grid-2/#algo-overview
1796 // 12.1. Grid Sizing Algorithm
1797 // 2. Next, the track sizing algorithm resolves the sizes of the grid rows.
1798 // In this process, any grid item which is subgridded in the grid container’s block axis is treated
1799 // as empty and its grid items (the grandchildren) are treated as direct children of the grid
1800 // container (their grandparent). This introspection is recursive.
1801
1802 // As with sizing columns, items which are subgridded only in the inline axis, and whose grid
1803 // container size in the block axis depends on the size of its contents are also introspected. (As
1804 // with sizing columns, the size contribution to this grid’s row sizing is taken under the provision
1805 // of having determined its track sizing only up to this corresponding point in the algorithm; and
1806 // again, this introspection is recursive.)
1807
1808 // To find the inline-axis available space for any items whose block-axis size contributions require
1809 // it, use the grid column sizes calculated in the previous step. If the grid container’s inline
1810 // size is definite, also apply justify-content to account for the effective column gap sizes.
1811
1812 // https://www.w3.org/TR/css-grid-2/#algo-track-sizing
1813 // 12.3. Track Sizing Algorithm
1814
1815 // The remainder of this section is the track sizing algorithm, which calculates from the min and
1816 // max track sizing functions the used track size. Each track has a base size, a <length> which
1817 // grows throughout the algorithm and which will eventually be the track’s final size, and a growth
1818 // limit, a <length> which provides a desired maximum size for the base size. There are 5 steps:
1819
1820 // 1. Initialize Track Sizes
1821 // 2. Resolve Intrinsic Track Sizes
1822 // 3. Maximize Tracks
1823 // 4. Expand Flexible Tracks
1824 // 5. Expand Stretched auto Tracks
1825
1826 calculate_sizes_of_rows(box);
1827
1828 // https://www.w3.org/TR/css-grid-2/#algo-overview
1829 // 12.1. Grid Sizing Algorithm
1830 // 3. Then, if the min-content contribution of any grid item has changed based on the row sizes and
1831 // alignment calculated in step 2, re-resolve the sizes of the grid columns with the new min-content
1832 // and max-content contributions (once only).
1833
1834 // To find the block-axis available space for any items whose inline-axis size contributions require
1835 // it, use the grid row sizes calculated in the previous step. If the grid container’s block size is
1836 // definite, also apply align-content to account for the effective row gap sizes
1837
1838 // 4. Next, if the min-content contribution of any grid item has changed based on the column sizes and
1839 // alignment calculated in step 3, re-resolve the sizes of the grid rows with the new min-content
1840 // and max-content contributions (once only).
1841
1842 // To find the inline-axis available space for any items whose block-axis size contributions require
1843 // it, use the grid column sizes calculated in the previous step. If the grid container’s inline
1844 // size is definite, also apply justify-content to account for the effective column gap sizes.
1845
1846 // 5. Finally, the grid container is sized using the resulting size of the grid as its content size,
1847 // and the tracks are aligned within the grid container according to the align-content and
1848 // justify-content properties.
1849
1850 // Once the size of each grid area is thus established, the grid items are laid out into their
1851 // respective containing blocks. The grid area’s width and height are considered definite for this
1852 // purpose.
1853
1854 auto layout_box = [&](int row_start, int row_end, int column_start, int column_end, Box const& child_box) -> void {
1855 auto& child_box_state = m_state.get_mutable(child_box);
1856 CSSPixels x_start = 0;
1857 CSSPixels x_end = 0;
1858 CSSPixels y_start = 0;
1859 CSSPixels y_end = 0;
1860 for (int i = 0; i < column_start; i++)
1861 x_start += m_grid_columns[i].base_size;
1862 for (int i = 0; i < column_end; i++)
1863 x_end += m_grid_columns[i].base_size;
1864 for (int i = 0; i < row_start; i++)
1865 y_start += m_grid_rows[i].base_size;
1866 for (int i = 0; i < row_end; i++)
1867 y_end += m_grid_rows[i].base_size;
1868 child_box_state.set_content_width((x_end - x_start));
1869 child_box_state.set_content_height((y_end - y_start));
1870 child_box_state.offset = { x_start, y_start };
1871
1872 auto available_space_for_children = AvailableSpace(AvailableSize::make_definite(child_box_state.content_width()), AvailableSize::make_definite(child_box_state.content_height()));
1873 if (auto independent_formatting_context = layout_inside(child_box, LayoutMode::Normal, available_space_for_children))
1874 independent_formatting_context->parent_context_did_dimension_child_root_box();
1875 };
1876
1877 for (auto& positioned_box : m_positioned_boxes) {
1878 auto resolved_row_span = box.computed_values().row_gap().is_auto() ? positioned_box.raw_row_span() : positioned_box.raw_row_span() * 2;
1879 if (!box.computed_values().row_gap().is_auto() && positioned_box.gap_adjusted_row(box) == 0)
1880 resolved_row_span -= 1;
1881 if (positioned_box.gap_adjusted_row(box) + resolved_row_span > static_cast<int>(m_grid_rows.size()))
1882 resolved_row_span = m_grid_rows.size() - positioned_box.gap_adjusted_row(box);
1883
1884 auto resolved_column_span = box.computed_values().column_gap().is_auto() ? positioned_box.raw_column_span() : positioned_box.raw_column_span() * 2;
1885 if (!box.computed_values().column_gap().is_auto() && positioned_box.gap_adjusted_column(box) == 0)
1886 resolved_column_span -= 1;
1887 if (positioned_box.gap_adjusted_column(box) + resolved_column_span > static_cast<int>(m_grid_columns.size()))
1888 resolved_column_span = m_grid_columns.size() - positioned_box.gap_adjusted_column(box);
1889
1890 layout_box(
1891 positioned_box.gap_adjusted_row(box),
1892 positioned_box.gap_adjusted_row(box) + resolved_row_span,
1893 positioned_box.gap_adjusted_column(box),
1894 positioned_box.gap_adjusted_column(box) + resolved_column_span,
1895 positioned_box.box());
1896 }
1897
1898 CSSPixels total_y = 0;
1899 for (auto& grid_row : m_grid_rows)
1900 total_y += grid_row.base_size;
1901 m_automatic_content_height = total_y;
1902}
1903
1904CSSPixels GridFormattingContext::automatic_content_height() const
1905{
1906 return m_automatic_content_height;
1907}
1908
1909bool GridFormattingContext::is_auto_positioned_row(CSS::GridTrackPlacement const& grid_row_start, CSS::GridTrackPlacement const& grid_row_end) const
1910{
1911 return is_auto_positioned_track(grid_row_start, grid_row_end);
1912}
1913
1914bool GridFormattingContext::is_auto_positioned_column(CSS::GridTrackPlacement const& grid_column_start, CSS::GridTrackPlacement const& grid_column_end) const
1915{
1916 return is_auto_positioned_track(grid_column_start, grid_column_end);
1917}
1918
1919bool GridFormattingContext::is_auto_positioned_track(CSS::GridTrackPlacement const& grid_track_start, CSS::GridTrackPlacement const& grid_track_end) const
1920{
1921 return grid_track_start.is_auto_positioned() && grid_track_end.is_auto_positioned();
1922}
1923
1924CSSPixels GridFormattingContext::get_free_space_x(AvailableSpace const& available_space)
1925{
1926 // https://www.w3.org/TR/css-grid-2/#algo-terms
1927 // free space: Equal to the available grid space minus the sum of the base sizes of all the grid
1928 // tracks (including gutters), floored at zero. If available grid space is indefinite, the free
1929 // space is indefinite as well.
1930 // FIXME: do indefinite space
1931 if (!available_space.width.is_definite())
1932 return 0;
1933 CSSPixels sum_base_sizes = 0;
1934 for (auto& grid_column : m_grid_columns)
1935 sum_base_sizes += grid_column.base_size;
1936 return max(CSSPixels(0), available_space.width.to_px() - sum_base_sizes);
1937}
1938
1939CSSPixels GridFormattingContext::get_free_space_y(Box const& box)
1940{
1941 // https://www.w3.org/TR/css-grid-2/#algo-terms
1942 // free space: Equal to the available grid space minus the sum of the base sizes of all the grid
1943 // tracks (including gutters), floored at zero. If available grid space is indefinite, the free
1944 // space is indefinite as well.
1945 CSSPixels sum_base_sizes = 0;
1946 for (auto& grid_row : m_grid_rows)
1947 sum_base_sizes += grid_row.base_size;
1948 auto& box_state = m_state.get_mutable(box);
1949 if (box_state.has_definite_height())
1950 return max(CSSPixels(0), CSSPixels(absolute_content_rect(box, m_state).height()) - sum_base_sizes);
1951 return -1;
1952}
1953
1954int GridFormattingContext::get_line_index_by_line_name(String const& needle, CSS::GridTrackSizeList grid_track_size_list)
1955{
1956 if (grid_track_size_list.track_list().size() == 0)
1957 return -1;
1958
1959 auto repeated_tracks_count = 0;
1960 for (size_t x = 0; x < grid_track_size_list.track_list().size(); x++) {
1961 if (grid_track_size_list.track_list()[x].is_repeat()) {
1962 // FIXME: Calculate amount of columns/rows if auto-fill/fit
1963 if (!grid_track_size_list.track_list()[x].repeat().is_default())
1964 return -1;
1965 auto repeat = grid_track_size_list.track_list()[x].repeat().grid_track_size_list();
1966 for (size_t y = 0; y < repeat.track_list().size(); y++) {
1967 for (size_t z = 0; z < repeat.line_names()[y].size(); z++) {
1968 if (repeat.line_names()[y][z] == needle)
1969 return x + repeated_tracks_count;
1970 repeated_tracks_count++;
1971 }
1972 }
1973 } else {
1974 for (size_t y = 0; y < grid_track_size_list.line_names()[x].size(); y++) {
1975 if (grid_track_size_list.line_names()[x][y] == needle)
1976 return x + repeated_tracks_count;
1977 }
1978 }
1979 }
1980 for (size_t y = 0; y < grid_track_size_list.line_names()[grid_track_size_list.track_list().size()].size(); y++) {
1981 if (grid_track_size_list.line_names()[grid_track_size_list.track_list().size()][y] == needle)
1982 return grid_track_size_list.track_list().size() + repeated_tracks_count;
1983 }
1984 return -1;
1985}
1986
1987OccupationGrid::OccupationGrid(int column_count, int row_count)
1988{
1989 Vector<bool> occupation_grid_row;
1990 for (int column_index = 0; column_index < max(column_count, 1); column_index++)
1991 occupation_grid_row.append(false);
1992 for (int row_index = 0; row_index < max(row_count, 1); row_index++)
1993 m_occupation_grid.append(occupation_grid_row);
1994}
1995
1996OccupationGrid::OccupationGrid()
1997{
1998}
1999
2000void OccupationGrid::maybe_add_column(int needed_number_of_columns)
2001{
2002 if (needed_number_of_columns <= column_count())
2003 return;
2004 auto column_count_before_modification = column_count();
2005 for (auto& occupation_grid_row : m_occupation_grid)
2006 for (int idx = 0; idx < needed_number_of_columns - column_count_before_modification; idx++)
2007 occupation_grid_row.append(false);
2008}
2009
2010void OccupationGrid::maybe_add_row(int needed_number_of_rows)
2011{
2012 if (needed_number_of_rows <= row_count())
2013 return;
2014
2015 Vector<bool> new_occupation_grid_row;
2016 for (int idx = 0; idx < column_count(); idx++)
2017 new_occupation_grid_row.append(false);
2018
2019 for (int idx = 0; idx < needed_number_of_rows - row_count(); idx++)
2020 m_occupation_grid.append(new_occupation_grid_row);
2021}
2022
2023void OccupationGrid::set_occupied(int column_start, int column_end, int row_start, int row_end)
2024{
2025 for (int row_index = 0; row_index < row_count(); row_index++) {
2026 if (row_index >= row_start && row_index < row_end) {
2027 for (int column_index = 0; column_index < column_count(); column_index++) {
2028 if (column_index >= column_start && column_index < column_end)
2029 set_occupied(column_index, row_index);
2030 }
2031 }
2032 }
2033}
2034
2035void OccupationGrid::set_occupied(int column_index, int row_index)
2036{
2037 m_occupation_grid[row_index][column_index] = true;
2038}
2039
2040bool OccupationGrid::is_occupied(int column_index, int row_index)
2041{
2042 return m_occupation_grid[row_index][column_index];
2043}
2044
2045int PositionedBox::gap_adjusted_row(Box const& parent_box)
2046{
2047 return parent_box.computed_values().row_gap().is_auto() ? m_row : m_row * 2;
2048}
2049
2050int PositionedBox::gap_adjusted_column(Box const& parent_box)
2051{
2052 return parent_box.computed_values().column_gap().is_auto() ? m_column : m_column * 2;
2053}
2054
2055}