Serenity Operating System
at master 2055 lines 112 kB view raw
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}