Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Side by Side Diff: third_party/WebKit/Source/core/layout/GridTrackSizingAlgorithm.cpp

Issue 2654533003: [css-grid] Move the track sizing algorithm to its own class (Closed)
Patch Set: Win build fix Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "core/layout/GridTrackSizingAlgorithm.h"
6
7 #include "core/layout/Grid.h"
8 #include "core/layout/LayoutGrid.h"
9 #include "platform/LengthFunctions.h"
10
11 namespace blink {
12
13 class GridSizingData;
14
15 LayoutUnit GridTrack::baseSize() const {
16 DCHECK(isGrowthLimitBiggerThanBaseSize());
17 return m_baseSize;
18 }
19
20 LayoutUnit GridTrack::growthLimit() const {
21 DCHECK(isGrowthLimitBiggerThanBaseSize());
22 DCHECK(!m_growthLimitCap || m_growthLimitCap.value() >= m_growthLimit ||
23 m_baseSize >= m_growthLimitCap.value());
24 return m_growthLimit;
25 }
26
27 void GridTrack::setBaseSize(LayoutUnit baseSize) {
28 m_baseSize = baseSize;
29 ensureGrowthLimitIsBiggerThanBaseSize();
30 }
31
32 void GridTrack::setGrowthLimit(LayoutUnit growthLimit) {
33 m_growthLimit =
34 growthLimit == infinity
35 ? growthLimit
36 : std::min(growthLimit, m_growthLimitCap.value_or(growthLimit));
37 ensureGrowthLimitIsBiggerThanBaseSize();
38 }
39
40 bool GridTrack::infiniteGrowthPotential() const {
41 return growthLimitIsInfinite() || m_infinitelyGrowable;
42 }
43
44 void GridTrack::setPlannedSize(LayoutUnit plannedSize) {
45 DCHECK(plannedSize >= 0 || plannedSize == infinity);
46 m_plannedSize = plannedSize;
47 }
48
49 void GridTrack::setSizeDuringDistribution(LayoutUnit sizeDuringDistribution) {
50 DCHECK_GE(sizeDuringDistribution, 0);
51 DCHECK(growthLimitIsInfinite() || growthLimit() >= sizeDuringDistribution);
52 m_sizeDuringDistribution = sizeDuringDistribution;
53 }
54
55 void GridTrack::growSizeDuringDistribution(LayoutUnit sizeDuringDistribution) {
56 DCHECK_GE(sizeDuringDistribution, 0);
57 m_sizeDuringDistribution += sizeDuringDistribution;
58 }
59
60 void GridTrack::setInfinitelyGrowable(bool infinitelyGrowable) {
61 m_infinitelyGrowable = infinitelyGrowable;
62 }
63
64 void GridTrack::setGrowthLimitCap(Optional<LayoutUnit> growthLimitCap) {
65 DCHECK(!growthLimitCap || *growthLimitCap >= 0);
66 m_growthLimitCap = growthLimitCap;
67 }
68
69 bool GridTrack::isGrowthLimitBiggerThanBaseSize() const {
70 return growthLimitIsInfinite() || m_growthLimit >= m_baseSize;
71 }
72
73 void GridTrack::ensureGrowthLimitIsBiggerThanBaseSize() {
74 if (m_growthLimit != infinity && m_growthLimit < m_baseSize)
75 m_growthLimit = m_baseSize;
76 }
77
78 class IndefiniteSizeStrategy final : public GridTrackSizingAlgorithmStrategy {
79 public:
80 IndefiniteSizeStrategy(GridTrackSizingAlgorithm& algorithm)
81 : GridTrackSizingAlgorithmStrategy(algorithm) {}
82
83 private:
84 LayoutUnit minLogicalWidthForChild(LayoutBox&,
85 Length childMinSize,
86 GridTrackSizingDirection) const override;
87 void layoutGridItemForMinSizeComputation(
88 LayoutBox&,
89 bool overrideSizeHasChanged) const override;
90 void maximizeTracks(Vector<GridTrack>&, LayoutUnit& freeSpace) override;
91 double findUsedFlexFraction(Vector<size_t>& flexibleSizedTracksIndex,
92 GridTrackSizingDirection,
93 LayoutUnit freeSpace) const override;
94 bool recomputeUsedFlexFractionIfNeeded(
95 Vector<size_t>& flexibleSizedTracksIndex,
96 double& flexFraction,
97 Vector<LayoutUnit>& increments,
98 LayoutUnit& totalGrowth) const override;
99 };
100
101 class DefiniteSizeStrategy final : public GridTrackSizingAlgorithmStrategy {
102 public:
103 DefiniteSizeStrategy(GridTrackSizingAlgorithm& algorithm)
104 : GridTrackSizingAlgorithmStrategy(algorithm) {}
105
106 private:
107 LayoutUnit minLogicalWidthForChild(LayoutBox&,
108 Length childMinSize,
109 GridTrackSizingDirection) const override;
110 void layoutGridItemForMinSizeComputation(
111 LayoutBox&,
112 bool overrideSizeHasChanged) const override;
113 void maximizeTracks(Vector<GridTrack>&, LayoutUnit& freeSpace) override;
114 double findUsedFlexFraction(Vector<size_t>& flexibleSizedTracksIndex,
115 GridTrackSizingDirection,
116 LayoutUnit freeSpace) const override;
117 bool recomputeUsedFlexFractionIfNeeded(
118 Vector<size_t>& flexibleSizedTracksIndex,
119 double& flexFraction,
120 Vector<LayoutUnit>& increments,
121 LayoutUnit& totalGrowth) const override {
122 return false;
123 }
124 };
125
126 // TODO(svillar): Repeated in LayoutGrid.
127 static LayoutUnit computeMarginLogicalSizeForChild(MarginDirection forDirection,
128 const LayoutGrid* grid,
129 const LayoutBox& child) {
130 if (!child.styleRef().hasMargin())
131 return LayoutUnit();
132
133 bool isRowAxis = forDirection == InlineDirection;
134 LayoutUnit marginStart;
135 LayoutUnit marginEnd;
136 LayoutUnit logicalSize =
137 isRowAxis ? child.logicalWidth() : child.logicalHeight();
138 Length marginStartLength = isRowAxis ? child.styleRef().marginStart()
139 : child.styleRef().marginBefore();
140 Length marginEndLength =
141 isRowAxis ? child.styleRef().marginEnd() : child.styleRef().marginAfter();
142 child.computeMarginsForDirection(
143 forDirection, grid, child.containingBlockLogicalWidthForContent(),
144 logicalSize, marginStart, marginEnd, marginStartLength, marginEndLength);
145
146 return marginStart + marginEnd;
147 }
148
149 static bool hasOverrideContainingBlockContentSizeForChild(
150 const LayoutBox& child,
151 GridTrackSizingDirection direction) {
152 return direction == ForColumns
153 ? child.hasOverrideContainingBlockLogicalWidth()
154 : child.hasOverrideContainingBlockLogicalHeight();
155 }
156
157 static LayoutUnit overrideContainingBlockContentSizeForChild(
158 const LayoutBox& child,
159 GridTrackSizingDirection direction) {
160 return direction == ForColumns
161 ? child.overrideContainingBlockContentLogicalWidth()
162 : child.overrideContainingBlockContentLogicalHeight();
163 }
164
165 static bool shouldClearOverrideContainingBlockContentSizeForChild(
166 const LayoutBox& child,
167 GridTrackSizingDirection direction) {
168 if (direction == ForColumns) {
169 return child.hasRelativeLogicalWidth() ||
170 child.styleRef().logicalWidth().isIntrinsicOrAuto();
171 }
172 return child.hasRelativeLogicalHeight() ||
173 child.styleRef().logicalHeight().isIntrinsicOrAuto();
174 }
175
176 static void setOverrideContainingBlockContentSizeForChild(
177 LayoutBox& child,
178 GridTrackSizingDirection direction,
179 LayoutUnit size) {
180 if (direction == ForColumns)
181 child.setOverrideContainingBlockContentLogicalWidth(size);
182 else
183 child.setOverrideContainingBlockContentLogicalHeight(size);
184 }
185
186 static GridTrackSizingDirection flowAwareDirectionForChild(
187 const LayoutGrid* layoutGrid,
188 const LayoutBox& child,
189 GridTrackSizingDirection direction) {
190 return child.isHorizontalWritingMode() ==
191 layoutGrid->isHorizontalWritingMode()
192 ? direction
193 : (direction == ForColumns ? ForRows : ForColumns);
194 }
195
196 LayoutUnit GridTrackSizingAlgorithm::assumedRowsSizeForOrthogonalChild(
197 const LayoutBox& child) const {
198 DCHECK(m_layoutGrid->isOrthogonalChild(child));
199 const GridSpan& span = m_grid.gridItemSpan(child, ForRows);
200 LayoutUnit gridAreaSize;
201 bool gridAreaIsIndefinite = false;
202 LayoutUnit containingBlockAvailableSize =
203 m_layoutGrid->containingBlockLogicalHeightForContent(
204 ExcludeMarginBorderPadding);
205 for (auto trackPosition : span) {
206 GridLength maxTrackSize =
207 gridTrackSize(ForRows, trackPosition).maxTrackBreadth();
208 if (maxTrackSize.isContentSized() || maxTrackSize.isFlex()) {
209 gridAreaIsIndefinite = true;
210 } else {
211 gridAreaSize +=
212 valueForLength(maxTrackSize.length(), containingBlockAvailableSize);
213 }
214 }
215
216 gridAreaSize += m_layoutGrid->guttersSize(
217 m_grid, ForRows, span.startLine(), span.integerSpan(), m_sizingOperation);
218
219 return gridAreaIsIndefinite
220 ? std::max(child.maxPreferredLogicalWidth(), gridAreaSize)
221 : gridAreaSize;
222 }
223
224 LayoutUnit GridTrackSizingAlgorithm::gridAreaBreadthForChild(
225 const LayoutBox& child,
226 GridTrackSizingDirection direction) {
227 if (direction == ForRows && m_sizingState == ColumnSizingFirstIteration)
228 return assumedRowsSizeForOrthogonalChild(child);
229
230 Vector<GridTrack>& allTracks = tracks(direction);
231 const GridSpan& span = m_grid.gridItemSpan(child, direction);
232 LayoutUnit gridAreaBreadth;
233 for (const auto& trackPosition : span)
234 gridAreaBreadth += allTracks[trackPosition].baseSize();
235
236 gridAreaBreadth +=
237 m_layoutGrid->guttersSize(m_grid, direction, span.startLine(),
238 span.integerSpan(), m_sizingOperation);
239
240 return gridAreaBreadth;
241 }
242
243 bool GridTrackSizingAlgorithmStrategy::
244 updateOverrideContainingBlockContentSizeForChild(
245 LayoutBox& child,
246 GridTrackSizingDirection direction) const {
247 LayoutUnit overrideSize =
248 m_algorithm.gridAreaBreadthForChild(child, direction);
249 if (hasOverrideContainingBlockContentSizeForChild(child, direction) &&
250 overrideContainingBlockContentSizeForChild(child, direction) ==
251 overrideSize)
252 return false;
253
254 setOverrideContainingBlockContentSizeForChild(child, direction, overrideSize);
255 return true;
256 }
257
258 LayoutUnit GridTrackSizingAlgorithmStrategy::logicalHeightForChild(
259 LayoutBox& child) const {
260 GridTrackSizingDirection childBlockDirection =
261 flowAwareDirectionForChild(layoutGrid(), child, ForRows);
262
263 // If |child| has a relative logical height, we shouldn't let it override its
264 // intrinsic height, which is what we are interested in here. Thus we need to
265 // set the block-axis override size to -1 (no possible resolution).
266 if (shouldClearOverrideContainingBlockContentSizeForChild(child, ForRows)) {
267 setOverrideContainingBlockContentSizeForChild(child, childBlockDirection,
268 LayoutUnit(-1));
269 child.setNeedsLayout(LayoutInvalidationReason::GridChanged);
270 }
271
272 // We need to clear the stretched height to properly compute logical height
273 // during layout.
274 if (child.needsLayout())
275 child.clearOverrideLogicalContentHeight();
276
277 child.layoutIfNeeded();
278 return child.logicalHeight() + child.marginLogicalHeight();
279 }
280
281 DISABLE_CFI_PERF
282 LayoutUnit GridTrackSizingAlgorithmStrategy::minContentForChild(
283 LayoutBox& child) const {
284 GridTrackSizingDirection childInlineDirection =
285 flowAwareDirectionForChild(layoutGrid(), child, ForColumns);
286 if (direction() == childInlineDirection) {
287 // If |child| has a relative logical width, we shouldn't let it override its
288 // intrinsic width, which is what we are interested in here. Thus we need to
289 // set the inline-axis override size to -1 (no possible resolution).
290 if (shouldClearOverrideContainingBlockContentSizeForChild(child,
291 ForColumns)) {
292 setOverrideContainingBlockContentSizeForChild(child, childInlineDirection,
293 LayoutUnit(-1));
294 }
295
296 // FIXME: It's unclear if we should return the intrinsic width or the
297 // preferred width.
298 // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html
299 LayoutUnit marginLogicalWidth =
300 child.needsLayout() ? computeMarginLogicalSizeForChild(
301 InlineDirection, layoutGrid(), child)
302 : child.marginLogicalWidth();
303 return child.minPreferredLogicalWidth() + marginLogicalWidth;
304 }
305
306 // All orthogonal flow boxes were already laid out during an early layout
307 // phase performed in FrameView::performLayout.
308 // It's true that grid track sizing was not completed at that time and it may
309 // afffect the final height of a grid item, but since it's forbidden to
310 // perform a layout during intrinsic width computation, we have to use that
311 // computed height for now.
312 if (direction() == ForColumns &&
313 m_algorithm.m_sizingOperation == IntrinsicSizeComputation) {
314 DCHECK(layoutGrid()->isOrthogonalChild(child));
315 return child.logicalHeight() + child.marginLogicalHeight();
316 }
317
318 if (updateOverrideContainingBlockContentSizeForChild(child,
319 childInlineDirection))
320 child.setNeedsLayout(LayoutInvalidationReason::GridChanged);
321 return logicalHeightForChild(child);
322 }
323
324 DISABLE_CFI_PERF
325 LayoutUnit GridTrackSizingAlgorithmStrategy::maxContentForChild(
326 LayoutBox& child) const {
327 GridTrackSizingDirection childInlineDirection =
328 flowAwareDirectionForChild(layoutGrid(), child, ForColumns);
329 if (direction() == childInlineDirection) {
330 // If |child| has a relative logical width, we shouldn't let it override its
331 // intrinsic width, which is what we are interested in here. Thus we need to
332 // set the inline-axis override size to -1 (no possible resolution).
333 if (shouldClearOverrideContainingBlockContentSizeForChild(child,
334 ForColumns)) {
335 setOverrideContainingBlockContentSizeForChild(child, childInlineDirection,
336 LayoutUnit(-1));
337 }
338
339 // FIXME: It's unclear if we should return the intrinsic width or the
340 // preferred width.
341 // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html
342 LayoutUnit marginLogicalWidth =
343 child.needsLayout() ? computeMarginLogicalSizeForChild(
344 InlineDirection, layoutGrid(), child)
345 : child.marginLogicalWidth();
346 return child.maxPreferredLogicalWidth() + marginLogicalWidth;
347 }
348
349 if (direction() == ForColumns &&
350 m_algorithm.m_sizingOperation == IntrinsicSizeComputation) {
351 // All orthogonal flow boxes were already laid out during an early layout
352 // phase performed in FrameView::performLayout. It's true that grid track
353 // sizing was not completed at that time and it may afffect the final height
354 // of a grid item, but since it's forbidden to perform a layout during
355 // intrinsic width computation, we have to use that computed height for now.
356 DCHECK(layoutGrid()->isOrthogonalChild(child));
357 return child.logicalHeight() + child.marginLogicalHeight();
358 }
359
360 if (updateOverrideContainingBlockContentSizeForChild(child,
361 childInlineDirection))
362 child.setNeedsLayout(LayoutInvalidationReason::GridChanged);
363 return logicalHeightForChild(child);
364 }
365
366 LayoutUnit GridTrackSizingAlgorithmStrategy::minSizeForChild(
367 LayoutBox& child) const {
368 GridTrackSizingDirection childInlineDirection =
369 flowAwareDirectionForChild(layoutGrid(), child, ForColumns);
370 bool isRowAxis = direction() == childInlineDirection;
371 const Length& childSize = isRowAxis ? child.styleRef().logicalWidth()
372 : child.styleRef().logicalHeight();
373 const Length& childMinSize = isRowAxis ? child.styleRef().logicalMinWidth()
374 : child.styleRef().logicalMinHeight();
375 bool overflowIsVisible =
376 isRowAxis
377 ? child.styleRef().overflowInlineDirection() == EOverflow::Visible
378 : child.styleRef().overflowBlockDirection() == EOverflow::Visible;
379 if (!childSize.isAuto() || (childMinSize.isAuto() && overflowIsVisible))
380 return minContentForChild(child);
381
382 bool overrideSizeHasChanged =
383 updateOverrideContainingBlockContentSizeForChild(child,
384 childInlineDirection);
385 if (isRowAxis)
386 return minLogicalWidthForChild(child, childMinSize, childInlineDirection);
387
388 layoutGridItemForMinSizeComputation(child, overrideSizeHasChanged);
389
390 return child.computeLogicalHeightUsing(MinSize, childMinSize,
391 child.intrinsicLogicalHeight()) +
392 child.marginLogicalHeight() + child.scrollbarLogicalHeight();
393 }
394
395 LayoutUnit GridTrackSizingAlgorithmStrategy::computeTrackBasedSize() const {
396 return m_algorithm.computeTrackBasedSize();
397 }
398
399 double GridTrackSizingAlgorithmStrategy::findFrUnitSize(
400 const GridSpan& tracksSpan,
401 LayoutUnit leftOverSpace) const {
402 return m_algorithm.findFrUnitSize(tracksSpan, leftOverSpace);
403 }
404
405 void GridTrackSizingAlgorithmStrategy::distributeSpaceToTracks(
406 Vector<GridTrack*>& tracks,
407 LayoutUnit& availableLogicalSpace) const {
408 m_algorithm.distributeSpaceToTracks<MaximizeTracks>(tracks, nullptr,
409 availableLogicalSpace);
410 }
411
412 LayoutUnit DefiniteSizeStrategy::minLogicalWidthForChild(
413 LayoutBox& child,
414 Length childMinSize,
415 GridTrackSizingDirection childInlineDirection) const {
416 LayoutUnit marginLogicalWidth =
417 computeMarginLogicalSizeForChild(InlineDirection, layoutGrid(), child);
418 return child.computeLogicalWidthUsing(
419 MinSize, childMinSize, overrideContainingBlockContentSizeForChild(
420 child, childInlineDirection),
421 layoutGrid()) +
422 marginLogicalWidth;
423 }
424
425 void DefiniteSizeStrategy::layoutGridItemForMinSizeComputation(
426 LayoutBox& child,
427 bool overrideSizeHasChanged) const {
428 if (overrideSizeHasChanged)
429 child.setNeedsLayout(LayoutInvalidationReason::GridChanged);
430 child.layoutIfNeeded();
431 }
432
433 void DefiniteSizeStrategy::maximizeTracks(Vector<GridTrack>& tracks,
434 LayoutUnit& freeSpace) {
435 size_t tracksSize = tracks.size();
436 Vector<GridTrack*> tracksForDistribution(tracksSize);
437 for (size_t i = 0; i < tracksSize; ++i) {
438 tracksForDistribution[i] = tracks.data() + i;
439 tracksForDistribution[i]->setPlannedSize(
440 tracksForDistribution[i]->baseSize());
441 }
442
443 distributeSpaceToTracks(tracksForDistribution, freeSpace);
444
445 for (auto* track : tracksForDistribution)
446 track->setBaseSize(track->plannedSize());
447 }
448
449 double DefiniteSizeStrategy::findUsedFlexFraction(
450 Vector<size_t>& flexibleSizedTracksIndex,
451 GridTrackSizingDirection direction,
452 LayoutUnit freeSpace) const {
453 GridSpan allTracksSpan = GridSpan::translatedDefiniteGridSpan(
454 0, m_algorithm.tracks(direction).size());
455 return findFrUnitSize(allTracksSpan, freeSpace);
456 }
457
458 LayoutUnit IndefiniteSizeStrategy::minLogicalWidthForChild(
459 LayoutBox& child,
460 Length childMinSize,
461 GridTrackSizingDirection childInlineDirection) const {
462 // TODO(svillar): we should use marginIntrinsicLogicalWidthForChild() instead
463 // but it is protected for LayoutObjects. Apparently none of the current tests
464 // fail, so we need a test case for this too.
465 LayoutUnit marginLogicalWidth = LayoutUnit();
466 return child.computeLogicalWidthUsing(
467 MinSize, childMinSize, overrideContainingBlockContentSizeForChild(
468 child, childInlineDirection),
469 layoutGrid()) +
470 marginLogicalWidth;
471 }
472
473 void IndefiniteSizeStrategy::layoutGridItemForMinSizeComputation(
474 LayoutBox& child,
475 bool overrideSizeHasChanged) const {
476 if (overrideSizeHasChanged && direction() != ForColumns)
477 child.setNeedsLayout(LayoutInvalidationReason::GridChanged);
478 child.layoutIfNeeded();
479 }
480
481 void IndefiniteSizeStrategy::maximizeTracks(Vector<GridTrack>& tracks,
482 LayoutUnit&) {
483 for (auto& track : tracks)
484 track.setBaseSize(track.growthLimit());
485 }
486
487 static inline double normalizedFlexFraction(const GridTrack& track,
488 double flexFactor) {
489 return track.baseSize() / std::max<double>(1, flexFactor);
490 }
491
492 double IndefiniteSizeStrategy::findUsedFlexFraction(
493 Vector<size_t>& flexibleSizedTracksIndex,
494 GridTrackSizingDirection direction,
495 LayoutUnit freeSpace) const {
496 auto allTracks = m_algorithm.tracks(direction);
497
498 double flexFraction = 0;
499 for (const auto& trackIndex : flexibleSizedTracksIndex) {
500 // TODO(svillar): we pass TrackSizing to gridTrackSize() because it does not
501 // really matter as we know the track is a flex sized track. It'd be nice
502 // not to have to do that.
503 flexFraction = std::max(
504 flexFraction,
505 normalizedFlexFraction(
506 allTracks[trackIndex],
507 m_algorithm.gridTrackSize(direction, trackIndex, TrackSizing)
508 .maxTrackBreadth()
509 .flex()));
510 }
511
512 const Grid& grid = m_algorithm.grid();
513 if (!grid.hasGridItems())
514 return flexFraction;
515
516 for (size_t i = 0; i < flexibleSizedTracksIndex.size(); ++i) {
517 GridIterator iterator(grid, direction, flexibleSizedTracksIndex[i]);
518 while (LayoutBox* gridItem = iterator.nextGridItem()) {
519 const GridSpan& span = grid.gridItemSpan(*gridItem, direction);
520
521 // Do not include already processed items.
522 if (i > 0 && span.startLine() <= flexibleSizedTracksIndex[i - 1])
523 continue;
524
525 flexFraction = std::max(
526 flexFraction, findFrUnitSize(span, maxContentForChild(*gridItem)));
527 }
528 }
529
530 return flexFraction;
531 }
532
533 bool IndefiniteSizeStrategy::recomputeUsedFlexFractionIfNeeded(
534 Vector<size_t>& flexibleSizedTracksIndex,
535 double& flexFraction,
536 Vector<LayoutUnit>& increments,
537 LayoutUnit& totalGrowth) const {
538 if (direction() == ForColumns)
539 return false;
540
541 const LayoutGrid* layoutGrid = this->layoutGrid();
542 LayoutUnit minSize = layoutGrid->computeContentLogicalHeight(
543 MinSize, layoutGrid->styleRef().logicalMinHeight(), LayoutUnit(-1));
544 LayoutUnit maxSize = layoutGrid->computeContentLogicalHeight(
545 MaxSize, layoutGrid->styleRef().logicalMaxHeight(), LayoutUnit(-1));
546
547 // Redo the flex fraction computation using min|max-height as definite
548 // available space in case the total height is smaller than min-height or
549 // larger than max-height.
550 LayoutUnit rowsSize = totalGrowth + computeTrackBasedSize();
551 bool checkMinSize = minSize && rowsSize < minSize;
552 bool checkMaxSize = maxSize != -1 && rowsSize > maxSize;
553 if (!checkMinSize && !checkMaxSize)
554 return false;
555
556 LayoutUnit freeSpace = checkMaxSize ? maxSize : LayoutUnit(-1);
557 const Grid& grid = m_algorithm.grid();
558 freeSpace = std::max(freeSpace, minSize) -
559 layoutGrid->guttersSize(grid, ForRows, 0, grid.numTracks(ForRows),
560 IntrinsicSizeComputation);
561
562 size_t numberOfTracks = m_algorithm.tracks(direction()).size();
563 flexFraction = findFrUnitSize(
564 GridSpan::translatedDefiniteGridSpan(0, numberOfTracks), freeSpace);
565 return true;
566 }
567
568 LayoutUnit& GridTrackSizingAlgorithm::freeSpace(
569 GridTrackSizingDirection direction) {
570 return direction == ForRows ? m_freeSpaceRows : m_freeSpaceColumns;
571 }
572
573 Vector<GridTrack>& GridTrackSizingAlgorithm::tracks(
574 GridTrackSizingDirection direction) {
575 return direction == ForColumns ? m_columns : m_rows;
576 }
577
578 const Vector<GridTrack>& GridTrackSizingAlgorithm::tracks(
579 GridTrackSizingDirection direction) const {
580 return direction == ForColumns ? m_columns : m_rows;
581 }
582
583 GridTrackSize GridTrackSizingAlgorithm::rawGridTrackSize(
584 GridTrackSizingDirection direction,
585 size_t translatedIndex) const {
586 bool isRowAxis = direction == ForColumns;
587 const Vector<GridTrackSize>& trackStyles =
588 isRowAxis ? m_layoutGrid->styleRef().gridTemplateColumns()
589 : m_layoutGrid->styleRef().gridTemplateRows();
590 const Vector<GridTrackSize>& autoRepeatTrackStyles =
591 isRowAxis ? m_layoutGrid->styleRef().gridAutoRepeatColumns()
592 : m_layoutGrid->styleRef().gridAutoRepeatRows();
593 const Vector<GridTrackSize>& autoTrackStyles =
594 isRowAxis ? m_layoutGrid->styleRef().gridAutoColumns()
595 : m_layoutGrid->styleRef().gridAutoRows();
596 size_t insertionPoint =
597 isRowAxis ? m_layoutGrid->styleRef().gridAutoRepeatColumnsInsertionPoint()
598 : m_layoutGrid->styleRef().gridAutoRepeatRowsInsertionPoint();
599 size_t autoRepeatTracksCount = m_grid.autoRepeatTracks(direction);
600
601 // We should not use GridPositionsResolver::explicitGridXXXCount() for this
602 // because the explicit grid might be larger than the number of tracks in
603 // grid-template-rows|columns (if grid-template-areas is specified for
604 // example).
605 size_t explicitTracksCount = trackStyles.size() + autoRepeatTracksCount;
606
607 int untranslatedIndexAsInt =
608 translatedIndex + m_grid.smallestTrackStart(direction);
609 size_t autoTrackStylesSize = autoTrackStyles.size();
610 if (untranslatedIndexAsInt < 0) {
611 int index = untranslatedIndexAsInt % static_cast<int>(autoTrackStylesSize);
612 // We need to traspose the index because the first negative implicit line
613 // will get the last defined auto track and so on.
614 index += index ? autoTrackStylesSize : 0;
615 return autoTrackStyles[index];
616 }
617
618 size_t untranslatedIndex = static_cast<size_t>(untranslatedIndexAsInt);
619 if (untranslatedIndex >= explicitTracksCount) {
620 return autoTrackStyles[(untranslatedIndex - explicitTracksCount) %
621 autoTrackStylesSize];
622 }
623
624 if (LIKELY(!autoRepeatTracksCount) || untranslatedIndex < insertionPoint)
625 return trackStyles[untranslatedIndex];
626
627 if (untranslatedIndex < (insertionPoint + autoRepeatTracksCount)) {
628 size_t autoRepeatLocalIndex = untranslatedIndexAsInt - insertionPoint;
629 return autoRepeatTrackStyles[autoRepeatLocalIndex %
630 autoRepeatTrackStyles.size()];
631 }
632
633 return trackStyles[untranslatedIndex - autoRepeatTracksCount];
634 }
635
636 GridTrackSize GridTrackSizingAlgorithm::gridTrackSize(
637 GridTrackSizingDirection direction,
638 size_t translatedIndex) const {
639 return gridTrackSize(direction, translatedIndex, m_sizingOperation);
640 }
641
642 GridTrackSize GridTrackSizingAlgorithm::gridTrackSize(
643 GridTrackSizingDirection direction,
644 size_t translatedIndex,
645 SizingOperation sizingOperation) const {
646 // Collapse empty auto repeat tracks if auto-fit.
647 if (m_grid.hasAutoRepeatEmptyTracks(direction) &&
648 m_grid.isEmptyAutoRepeatTrack(direction, translatedIndex))
649 return {Length(Fixed), LengthTrackSizing};
650
651 const GridTrackSize& trackSize = rawGridTrackSize(direction, translatedIndex);
652 if (trackSize.isFitContent())
653 return trackSize;
654
655 GridLength minTrackBreadth = trackSize.minTrackBreadth();
656 GridLength maxTrackBreadth = trackSize.maxTrackBreadth();
657 // If the logical width/height of the grid container is indefinite, percentage
658 // values are treated as <auto>.
659 if (minTrackBreadth.hasPercentage() || maxTrackBreadth.hasPercentage()) {
660 // For the inline axis this only happens when we're computing the intrinsic
661 // sizes.
662 if ((sizingOperation == IntrinsicSizeComputation) ||
663 (direction == ForRows &&
664 !m_layoutGrid->cachedHasDefiniteLogicalHeight())) {
665 if (minTrackBreadth.hasPercentage())
666 minTrackBreadth = Length(Auto);
667 if (maxTrackBreadth.hasPercentage())
668 maxTrackBreadth = Length(Auto);
669 }
670 }
671
672 // Flex sizes are invalid as a min sizing function. However we still can have
673 // a flexible |minTrackBreadth| if the track had a flex size directly (e.g.
674 // "1fr"), the spec says that in this case it implies an automatic minimum.
675 if (minTrackBreadth.isFlex())
676 minTrackBreadth = Length(Auto);
677
678 return GridTrackSize(minTrackBreadth, maxTrackBreadth);
679 }
680
681 LayoutUnit GridTrackSizingAlgorithm::initialBaseSize(
682 const GridTrackSize& trackSize) const {
683 const GridLength& gridLength = trackSize.minTrackBreadth();
684 if (gridLength.isFlex())
685 return LayoutUnit();
686
687 const Length& trackLength = gridLength.length();
688 if (trackLength.isSpecified())
689 return valueForLength(trackLength, m_availableSpace.clampNegativeToZero());
690
691 DCHECK(trackLength.isMinContent() || trackLength.isAuto() ||
692 trackLength.isMaxContent());
693 return LayoutUnit();
694 }
695
696 LayoutUnit GridTrackSizingAlgorithm::initialGrowthLimit(
697 const GridTrackSize& trackSize,
698 LayoutUnit baseSize) const {
699 const GridLength& gridLength = trackSize.maxTrackBreadth();
700 if (gridLength.isFlex())
701 return baseSize;
702
703 const Length& trackLength = gridLength.length();
704 if (trackLength.isSpecified())
705 return valueForLength(trackLength, m_availableSpace.clampNegativeToZero());
706
707 DCHECK(trackLength.isMinContent() || trackLength.isAuto() ||
708 trackLength.isMaxContent());
709 return LayoutUnit(infinity);
710 }
711
712 void GridTrackSizingAlgorithm::initializeTrackSizes() {
713 DCHECK(m_contentSizedTracksIndex.isEmpty());
714 DCHECK(m_flexibleSizedTracksIndex.isEmpty());
715 Vector<GridTrack>& trackList = tracks(m_direction);
716 bool hasDefiniteFreeSpace = m_sizingOperation == TrackSizing;
717 size_t numTracks = trackList.size();
718 for (size_t i = 0; i < numTracks; ++i) {
719 GridTrackSize trackSize = gridTrackSize(m_direction, i);
720 GridTrack& track = trackList[i];
721 track.setBaseSize(initialBaseSize(trackSize));
722 track.setGrowthLimit(initialGrowthLimit(trackSize, track.baseSize()));
723 track.setInfinitelyGrowable(false);
724
725 if (trackSize.isFitContent()) {
726 GridLength gridLength = trackSize.fitContentTrackBreadth();
727 if (!gridLength.hasPercentage() || hasDefiniteFreeSpace) {
728 track.setGrowthLimitCap(valueForLength(
729 gridLength.length(), m_availableSpace.clampNegativeToZero()));
730 }
731 }
732
733 if (trackSize.isContentSized())
734 m_contentSizedTracksIndex.push_back(i);
735 if (trackSize.maxTrackBreadth().isFlex())
736 m_flexibleSizedTracksIndex.push_back(i);
737 }
738 }
739
740 void GridTrackSizingAlgorithm::sizeTrackToFitNonSpanningItem(
741 const GridSpan& span,
742 LayoutBox& gridItem,
743 GridTrack& track) {
744 const size_t trackPosition = span.startLine();
745 GridTrackSize trackSize = gridTrackSize(m_direction, trackPosition);
746
747 if (trackSize.hasMinContentMinTrackBreadth()) {
748 track.setBaseSize(
749 std::max(track.baseSize(), m_strategy->minContentForChild(gridItem)));
750 } else if (trackSize.hasMaxContentMinTrackBreadth()) {
751 track.setBaseSize(
752 std::max(track.baseSize(), m_strategy->maxContentForChild(gridItem)));
753 } else if (trackSize.hasAutoMinTrackBreadth()) {
754 track.setBaseSize(
755 std::max(track.baseSize(), m_strategy->minSizeForChild(gridItem)));
756 }
757
758 if (trackSize.hasMinContentMaxTrackBreadth()) {
759 track.setGrowthLimit(std::max(track.growthLimit(),
760 m_strategy->minContentForChild(gridItem)));
761 } else if (trackSize.hasMaxContentOrAutoMaxTrackBreadth()) {
762 LayoutUnit growthLimit = m_strategy->maxContentForChild(gridItem);
763 if (trackSize.isFitContent()) {
764 growthLimit =
765 std::min(growthLimit,
766 valueForLength(trackSize.fitContentTrackBreadth().length(),
767 m_availableSpace));
768 }
769 track.setGrowthLimit(std::max(track.growthLimit(), growthLimit));
770 }
771 }
772
773 bool GridTrackSizingAlgorithm::spanningItemCrossesFlexibleSizedTracks(
774 const GridSpan& span) const {
775 for (const auto& trackPosition : span) {
776 const GridTrackSize& trackSize = gridTrackSize(m_direction, trackPosition);
777 if (trackSize.minTrackBreadth().isFlex() ||
778 trackSize.maxTrackBreadth().isFlex())
779 return true;
780 }
781
782 return false;
783 }
784
785 // We're basically using a class instead of a std::pair because of accessing
786 // gridItem() or getGridSpan() is much more self-explanatory that using .first
787 // or .second members in the pair. Having a std::pair<LayoutBox*, size_t>
788 // does not work either because we still need the GridSpan so we'd have to add
789 // an extra hash lookup for each item.
790 class GridItemWithSpan {
791 public:
792 GridItemWithSpan(LayoutBox& gridItem, const GridSpan& gridSpan)
793 : m_gridItem(&gridItem), m_gridSpan(gridSpan) {}
794
795 LayoutBox& gridItem() const { return *m_gridItem; }
796 GridSpan getGridSpan() const { return m_gridSpan; }
797
798 bool operator<(const GridItemWithSpan other) const {
799 return m_gridSpan.integerSpan() < other.m_gridSpan.integerSpan();
800 }
801
802 private:
803 LayoutBox* m_gridItem;
804 GridSpan m_gridSpan;
805 };
806
807 struct GridItemsSpanGroupRange {
808 Vector<GridItemWithSpan>::iterator rangeStart;
809 Vector<GridItemWithSpan>::iterator rangeEnd;
810 };
811
812 enum TrackSizeRestriction {
813 AllowInfinity,
814 ForbidInfinity,
815 };
816
817 static LayoutUnit trackSizeForTrackSizeComputationPhase(
818 TrackSizeComputationPhase phase,
819 const GridTrack& track,
820 TrackSizeRestriction restriction) {
821 switch (phase) {
822 case ResolveIntrinsicMinimums:
823 case ResolveContentBasedMinimums:
824 case ResolveMaxContentMinimums:
825 case MaximizeTracks:
826 return track.baseSize();
827 case ResolveIntrinsicMaximums:
828 case ResolveMaxContentMaximums:
829 const LayoutUnit& growthLimit = track.growthLimit();
830 if (restriction == AllowInfinity)
831 return growthLimit;
832 return growthLimit == infinity ? track.baseSize() : growthLimit;
833 }
834
835 NOTREACHED();
836 return track.baseSize();
837 }
838
839 static bool shouldProcessTrackForTrackSizeComputationPhase(
840 TrackSizeComputationPhase phase,
841 const GridTrackSize& trackSize) {
842 switch (phase) {
843 case ResolveIntrinsicMinimums:
844 return trackSize.hasIntrinsicMinTrackBreadth();
845 case ResolveContentBasedMinimums:
846 return trackSize.hasMinOrMaxContentMinTrackBreadth();
847 case ResolveMaxContentMinimums:
848 return trackSize.hasMaxContentMinTrackBreadth();
849 case ResolveIntrinsicMaximums:
850 return trackSize.hasIntrinsicMaxTrackBreadth();
851 case ResolveMaxContentMaximums:
852 return trackSize.hasMaxContentOrAutoMaxTrackBreadth();
853 case MaximizeTracks:
854 NOTREACHED();
855 return false;
856 }
857
858 NOTREACHED();
859 return false;
860 }
861
862 static bool trackShouldGrowBeyondGrowthLimitsForTrackSizeComputationPhase(
863 TrackSizeComputationPhase phase,
864 const GridTrackSize& trackSize) {
865 switch (phase) {
866 case ResolveIntrinsicMinimums:
867 case ResolveContentBasedMinimums:
868 return trackSize
869 .hasAutoOrMinContentMinTrackBreadthAndIntrinsicMaxTrackBreadth();
870 case ResolveMaxContentMinimums:
871 return trackSize
872 .hasMaxContentMinTrackBreadthAndMaxContentMaxTrackBreadth();
873 case ResolveIntrinsicMaximums:
874 case ResolveMaxContentMaximums:
875 return true;
876 case MaximizeTracks:
877 NOTREACHED();
878 return false;
879 }
880
881 NOTREACHED();
882 return false;
883 }
884
885 static void markAsInfinitelyGrowableForTrackSizeComputationPhase(
886 TrackSizeComputationPhase phase,
887 GridTrack& track) {
888 switch (phase) {
889 case ResolveIntrinsicMinimums:
890 case ResolveContentBasedMinimums:
891 case ResolveMaxContentMinimums:
892 return;
893 case ResolveIntrinsicMaximums:
894 if (trackSizeForTrackSizeComputationPhase(phase, track, AllowInfinity) ==
895 infinity &&
896 track.plannedSize() != infinity)
897 track.setInfinitelyGrowable(true);
898 return;
899 case ResolveMaxContentMaximums:
900 if (track.infinitelyGrowable())
901 track.setInfinitelyGrowable(false);
902 return;
903 case MaximizeTracks:
904 NOTREACHED();
905 return;
906 }
907
908 NOTREACHED();
909 }
910
911 static void updateTrackSizeForTrackSizeComputationPhase(
912 TrackSizeComputationPhase phase,
913 GridTrack& track) {
914 switch (phase) {
915 case ResolveIntrinsicMinimums:
916 case ResolveContentBasedMinimums:
917 case ResolveMaxContentMinimums:
918 track.setBaseSize(track.plannedSize());
919 return;
920 case ResolveIntrinsicMaximums:
921 case ResolveMaxContentMaximums:
922 track.setGrowthLimit(track.plannedSize());
923 return;
924 case MaximizeTracks:
925 NOTREACHED();
926 return;
927 }
928
929 NOTREACHED();
930 }
931
932 LayoutUnit GridTrackSizingAlgorithm::itemSizeForTrackSizeComputationPhase(
933 TrackSizeComputationPhase phase,
934 LayoutBox& gridItem) const {
935 switch (phase) {
936 case ResolveIntrinsicMinimums:
937 case ResolveIntrinsicMaximums:
938 return m_strategy->minSizeForChild(gridItem);
939 case ResolveContentBasedMinimums:
940 return m_strategy->minContentForChild(gridItem);
941 case ResolveMaxContentMinimums:
942 case ResolveMaxContentMaximums:
943 return m_strategy->maxContentForChild(gridItem);
944 case MaximizeTracks:
945 NOTREACHED();
946 return LayoutUnit();
947 }
948
949 NOTREACHED();
950 return LayoutUnit();
951 }
952
953 static bool sortByGridTrackGrowthPotential(const GridTrack* track1,
954 const GridTrack* track2) {
955 // This check ensures that we respect the irreflexivity property of the strict
956 // weak ordering required by std::sort(forall x: NOT x < x).
957 bool track1HasInfiniteGrowthPotentialWithoutCap =
958 track1->infiniteGrowthPotential() && !track1->growthLimitCap();
959 bool track2HasInfiniteGrowthPotentialWithoutCap =
960 track2->infiniteGrowthPotential() && !track2->growthLimitCap();
961
962 if (track1HasInfiniteGrowthPotentialWithoutCap &&
963 track2HasInfiniteGrowthPotentialWithoutCap)
964 return false;
965
966 if (track1HasInfiniteGrowthPotentialWithoutCap ||
967 track2HasInfiniteGrowthPotentialWithoutCap)
968 return track2HasInfiniteGrowthPotentialWithoutCap;
969
970 LayoutUnit track1Limit =
971 track1->growthLimitCap().value_or(track1->growthLimit());
972 LayoutUnit track2Limit =
973 track2->growthLimitCap().value_or(track2->growthLimit());
974 return (track1Limit - track1->baseSize()) <
975 (track2Limit - track2->baseSize());
976 }
977
978 static void clampGrowthShareIfNeeded(TrackSizeComputationPhase phase,
979 const GridTrack& track,
980 LayoutUnit& growthShare) {
981 if (phase != ResolveMaxContentMaximums || !track.growthLimitCap())
982 return;
983
984 LayoutUnit distanceToCap =
985 track.growthLimitCap().value() - track.sizeDuringDistribution();
986 if (distanceToCap <= 0)
987 return;
988
989 growthShare = std::min(growthShare, distanceToCap);
990 }
991
992 template <TrackSizeComputationPhase phase>
993 void GridTrackSizingAlgorithm::distributeSpaceToTracks(
994 Vector<GridTrack*>& tracks,
995 Vector<GridTrack*>* growBeyondGrowthLimitsTracks,
996 LayoutUnit& availableLogicalSpace) const {
997 DCHECK_GE(availableLogicalSpace, 0);
998
999 for (auto* track : tracks) {
1000 track->setSizeDuringDistribution(
1001 trackSizeForTrackSizeComputationPhase(phase, *track, ForbidInfinity));
1002 }
1003
1004 if (availableLogicalSpace > 0) {
1005 std::sort(tracks.begin(), tracks.end(), sortByGridTrackGrowthPotential);
1006
1007 size_t tracksSize = tracks.size();
1008 for (size_t i = 0; i < tracksSize; ++i) {
1009 GridTrack& track = *tracks[i];
1010 LayoutUnit availableLogicalSpaceShare =
1011 availableLogicalSpace / (tracksSize - i);
1012 const LayoutUnit& trackBreadth =
1013 trackSizeForTrackSizeComputationPhase(phase, track, ForbidInfinity);
1014 LayoutUnit growthShare =
1015 track.infiniteGrowthPotential()
1016 ? availableLogicalSpaceShare
1017 : std::min(availableLogicalSpaceShare,
1018 track.growthLimit() - trackBreadth);
1019 clampGrowthShareIfNeeded(phase, track, growthShare);
1020 DCHECK_GE(growthShare, 0) << "We must never shrink any grid track or "
1021 "else we can't guarantee we abide by our "
1022 "min-sizing function.";
1023 track.growSizeDuringDistribution(growthShare);
1024 availableLogicalSpace -= growthShare;
1025 }
1026 }
1027
1028 if (availableLogicalSpace > 0 && growBeyondGrowthLimitsTracks) {
1029 // We need to sort them because there might be tracks with growth limit caps
1030 // (like the ones with fit-content()) which cannot indefinitely grow over
1031 // the limits.
1032 if (phase == ResolveMaxContentMaximums) {
1033 std::sort(growBeyondGrowthLimitsTracks->begin(),
1034 growBeyondGrowthLimitsTracks->end(),
1035 sortByGridTrackGrowthPotential);
1036 }
1037
1038 size_t tracksGrowingAboveMaxBreadthSize =
1039 growBeyondGrowthLimitsTracks->size();
1040 for (size_t i = 0; i < tracksGrowingAboveMaxBreadthSize; ++i) {
1041 GridTrack* track = growBeyondGrowthLimitsTracks->at(i);
1042 LayoutUnit growthShare =
1043 availableLogicalSpace / (tracksGrowingAboveMaxBreadthSize - i);
1044 clampGrowthShareIfNeeded(phase, *track, growthShare);
1045 DCHECK_GE(growthShare, 0) << "We must never shrink any grid track or "
1046 "else we can't guarantee we abide by our "
1047 "min-sizing function.";
1048 track->growSizeDuringDistribution(growthShare);
1049 availableLogicalSpace -= growthShare;
1050 }
1051 }
1052
1053 for (auto* track : tracks) {
1054 track->setPlannedSize(
1055 track->plannedSize() == infinity
1056 ? track->sizeDuringDistribution()
1057 : std::max(track->plannedSize(), track->sizeDuringDistribution()));
1058 }
1059 }
1060
1061 template <TrackSizeComputationPhase phase>
1062 void GridTrackSizingAlgorithm::increaseSizesToAccommodateSpanningItems(
1063 const GridItemsSpanGroupRange& gridItemsWithSpan) {
1064 Vector<GridTrack>& allTracks = tracks(m_direction);
1065 for (const auto& trackIndex : m_contentSizedTracksIndex) {
1066 GridTrack& track = allTracks[trackIndex];
1067 track.setPlannedSize(
1068 trackSizeForTrackSizeComputationPhase(phase, track, AllowInfinity));
1069 }
1070
1071 Vector<GridTrack*> growBeyondGrowthLimitsTracks;
1072 Vector<GridTrack*> filteredTracks;
1073 for (auto it = gridItemsWithSpan.rangeStart; it != gridItemsWithSpan.rangeEnd;
1074 ++it) {
1075 GridItemWithSpan& gridItemWithSpan = *it;
1076 DCHECK_GT(gridItemWithSpan.getGridSpan().integerSpan(), 1u);
1077 const GridSpan& itemSpan = gridItemWithSpan.getGridSpan();
1078
1079 growBeyondGrowthLimitsTracks.shrink(0);
1080 filteredTracks.shrink(0);
1081 LayoutUnit spanningTracksSize;
1082 for (const auto& trackPosition : itemSpan) {
1083 GridTrackSize trackSize = gridTrackSize(m_direction, trackPosition);
1084 GridTrack& track = tracks(m_direction)[trackPosition];
1085 spanningTracksSize +=
1086 trackSizeForTrackSizeComputationPhase(phase, track, ForbidInfinity);
1087 if (!shouldProcessTrackForTrackSizeComputationPhase(phase, trackSize))
1088 continue;
1089
1090 filteredTracks.push_back(&track);
1091
1092 if (trackShouldGrowBeyondGrowthLimitsForTrackSizeComputationPhase(
1093 phase, trackSize))
1094 growBeyondGrowthLimitsTracks.push_back(&track);
1095 }
1096
1097 if (filteredTracks.isEmpty())
1098 continue;
1099
1100 spanningTracksSize +=
1101 m_layoutGrid->guttersSize(m_grid, m_direction, itemSpan.startLine(),
1102 itemSpan.integerSpan(), m_sizingOperation);
1103
1104 LayoutUnit extraSpace = itemSizeForTrackSizeComputationPhase(
1105 phase, gridItemWithSpan.gridItem()) -
1106 spanningTracksSize;
1107 extraSpace = extraSpace.clampNegativeToZero();
1108 auto& tracksToGrowBeyondGrowthLimits =
1109 growBeyondGrowthLimitsTracks.isEmpty() ? filteredTracks
1110 : growBeyondGrowthLimitsTracks;
1111 distributeSpaceToTracks<phase>(filteredTracks,
1112 &tracksToGrowBeyondGrowthLimits, extraSpace);
1113 }
1114
1115 for (const auto& trackIndex : m_contentSizedTracksIndex) {
1116 GridTrack& track = allTracks[trackIndex];
1117 markAsInfinitelyGrowableForTrackSizeComputationPhase(phase, track);
1118 updateTrackSizeForTrackSizeComputationPhase(phase, track);
1119 }
1120 }
1121
1122 void GridTrackSizingAlgorithm::resolveIntrinsicTrackSizes() {
1123 Vector<GridItemWithSpan> itemsSortedByIncreasingSpan;
1124 if (m_grid.hasGridItems()) {
1125 HashSet<LayoutBox*> itemsSet;
1126 for (const auto& trackIndex : m_contentSizedTracksIndex) {
1127 GridIterator iterator(m_grid, m_direction, trackIndex);
1128 GridTrack& track = tracks(m_direction)[trackIndex];
1129 while (LayoutBox* gridItem = iterator.nextGridItem()) {
1130 if (itemsSet.add(gridItem).isNewEntry) {
1131 const GridSpan& span = m_grid.gridItemSpan(*gridItem, m_direction);
1132 if (span.integerSpan() == 1) {
1133 sizeTrackToFitNonSpanningItem(span, *gridItem, track);
1134 } else if (!spanningItemCrossesFlexibleSizedTracks(span)) {
1135 itemsSortedByIncreasingSpan.push_back(
1136 GridItemWithSpan(*gridItem, span));
1137 }
1138 }
1139 }
1140 }
1141 std::sort(itemsSortedByIncreasingSpan.begin(),
1142 itemsSortedByIncreasingSpan.end());
1143 }
1144
1145 auto it = itemsSortedByIncreasingSpan.begin();
1146 auto end = itemsSortedByIncreasingSpan.end();
1147 while (it != end) {
1148 GridItemsSpanGroupRange spanGroupRange = {it,
1149 std::upper_bound(it, end, *it)};
1150 increaseSizesToAccommodateSpanningItems<ResolveIntrinsicMinimums>(
1151 spanGroupRange);
1152 increaseSizesToAccommodateSpanningItems<ResolveContentBasedMinimums>(
1153 spanGroupRange);
1154 increaseSizesToAccommodateSpanningItems<ResolveMaxContentMinimums>(
1155 spanGroupRange);
1156 increaseSizesToAccommodateSpanningItems<ResolveIntrinsicMaximums>(
1157 spanGroupRange);
1158 increaseSizesToAccommodateSpanningItems<ResolveMaxContentMaximums>(
1159 spanGroupRange);
1160 it = spanGroupRange.rangeEnd;
1161 }
1162
1163 for (const auto& trackIndex : m_contentSizedTracksIndex) {
1164 GridTrack& track = tracks(m_direction)[trackIndex];
1165 if (track.growthLimit() == infinity)
1166 track.setGrowthLimit(track.baseSize());
1167 }
1168 }
1169
1170 void GridTrackSizingAlgorithm::computeGridContainerIntrinsicSizes() {
1171 m_minContentSize = m_maxContentSize = LayoutUnit();
1172
1173 Vector<GridTrack>& allTracks = tracks(m_direction);
1174 for (auto& track : allTracks) {
1175 DCHECK(!track.infiniteGrowthPotential());
1176 m_minContentSize += track.baseSize();
1177 m_maxContentSize += track.growthLimit();
1178 // The growth limit caps must be cleared now in order to properly sort
1179 // tracks by growth potential on an eventual "Maximize Tracks".
1180 track.setGrowthLimitCap(WTF::nullopt);
1181 }
1182 }
1183
1184 LayoutUnit GridTrackSizingAlgorithm::computeTrackBasedSize() const {
1185 LayoutUnit size;
1186
1187 const Vector<GridTrack>& allTracks = tracks(m_direction);
1188 for (auto& track : allTracks)
1189 size += track.baseSize();
1190
1191 size += m_layoutGrid->guttersSize(m_grid, m_direction, 0, allTracks.size(),
1192 m_sizingOperation);
1193
1194 return size;
1195 }
1196
1197 double GridTrackSizingAlgorithm::findFrUnitSize(
1198 const GridSpan& tracksSpan,
1199 LayoutUnit leftOverSpace) const {
1200 if (leftOverSpace <= 0)
1201 return 0;
1202
1203 const Vector<GridTrack>& allTracks = tracks(m_direction);
1204 double flexFactorSum = 0;
1205 Vector<size_t, 8> flexibleTracksIndexes;
1206 for (const auto& trackIndex : tracksSpan) {
1207 GridTrackSize trackSize = gridTrackSize(m_direction, trackIndex);
1208 if (!trackSize.maxTrackBreadth().isFlex()) {
1209 leftOverSpace -= allTracks[trackIndex].baseSize();
1210 } else {
1211 flexibleTracksIndexes.push_back(trackIndex);
1212 flexFactorSum += trackSize.maxTrackBreadth().flex();
1213 }
1214 }
1215
1216 // The function is not called if we don't have <flex> grid tracks.
1217 DCHECK(!flexibleTracksIndexes.isEmpty());
1218
1219 return computeFlexFactorUnitSize(allTracks, flexFactorSum, leftOverSpace,
1220 flexibleTracksIndexes);
1221 }
1222
1223 double GridTrackSizingAlgorithm::computeFlexFactorUnitSize(
1224 const Vector<GridTrack>& tracks,
1225 double flexFactorSum,
1226 LayoutUnit& leftOverSpace,
1227 const Vector<size_t, 8>& flexibleTracksIndexes,
1228 std::unique_ptr<TrackIndexSet> tracksToTreatAsInflexible) const {
1229 // We want to avoid the effect of flex factors sum below 1 making the factor
1230 // unit size to grow exponentially.
1231 double hypotheticalFactorUnitSize =
1232 leftOverSpace / std::max<double>(1, flexFactorSum);
1233
1234 // product of the hypothetical "flex factor unit" and any flexible track's
1235 // "flex factor" must be grater than such track's "base size".
1236 std::unique_ptr<TrackIndexSet> additionalTracksToTreatAsInflexible =
1237 std::move(tracksToTreatAsInflexible);
1238 bool validFlexFactorUnit = true;
1239 for (auto index : flexibleTracksIndexes) {
1240 if (additionalTracksToTreatAsInflexible &&
1241 additionalTracksToTreatAsInflexible->contains(index))
1242 continue;
1243 LayoutUnit baseSize = tracks[index].baseSize();
1244 double flexFactor =
1245 gridTrackSize(m_direction, index).maxTrackBreadth().flex();
1246 // treating all such tracks as inflexible.
1247 if (baseSize > hypotheticalFactorUnitSize * flexFactor) {
1248 leftOverSpace -= baseSize;
1249 flexFactorSum -= flexFactor;
1250 if (!additionalTracksToTreatAsInflexible)
1251 additionalTracksToTreatAsInflexible = WTF::makeUnique<TrackIndexSet>();
1252 additionalTracksToTreatAsInflexible->add(index);
1253 validFlexFactorUnit = false;
1254 }
1255 }
1256 if (!validFlexFactorUnit) {
1257 return computeFlexFactorUnitSize(
1258 tracks, flexFactorSum, leftOverSpace, flexibleTracksIndexes,
1259 std::move(additionalTracksToTreatAsInflexible));
1260 }
1261 return hypotheticalFactorUnitSize;
1262 }
1263
1264 void GridTrackSizingAlgorithm::computeFlexSizedTracksGrowth(
1265 double flexFraction,
1266 Vector<LayoutUnit>& increments,
1267 LayoutUnit& totalGrowth) const {
1268 size_t numFlexTracks = m_flexibleSizedTracksIndex.size();
1269 DCHECK_EQ(increments.size(), numFlexTracks);
1270 const Vector<GridTrack>& allTracks = tracks(m_direction);
1271 for (size_t i = 0; i < numFlexTracks; ++i) {
1272 size_t trackIndex = m_flexibleSizedTracksIndex[i];
1273 auto trackSize = gridTrackSize(m_direction, trackIndex);
1274 DCHECK(trackSize.maxTrackBreadth().isFlex());
1275 LayoutUnit oldBaseSize = allTracks[trackIndex].baseSize();
1276 LayoutUnit newBaseSize =
1277 std::max(oldBaseSize,
1278 LayoutUnit(flexFraction * trackSize.maxTrackBreadth().flex()));
1279 increments[i] = newBaseSize - oldBaseSize;
1280 totalGrowth += increments[i];
1281 }
1282 }
1283
1284 void GridTrackSizingAlgorithm::stretchFlexibleTracks(LayoutUnit freeSpace) {
1285 double flexFraction = m_strategy->findUsedFlexFraction(
1286 m_flexibleSizedTracksIndex, m_direction, freeSpace);
1287
1288 LayoutUnit totalGrowth;
1289 Vector<LayoutUnit> increments;
1290 increments.grow(m_flexibleSizedTracksIndex.size());
1291 computeFlexSizedTracksGrowth(flexFraction, increments, totalGrowth);
1292
1293 if (m_strategy->recomputeUsedFlexFractionIfNeeded(
1294 m_flexibleSizedTracksIndex, flexFraction, increments, totalGrowth)) {
1295 totalGrowth = LayoutUnit(0);
1296 computeFlexSizedTracksGrowth(flexFraction, increments, totalGrowth);
1297 }
1298
1299 size_t i = 0;
1300 Vector<GridTrack>& allTracks = tracks(m_direction);
1301 for (auto trackIndex : m_flexibleSizedTracksIndex) {
1302 auto& track = allTracks[trackIndex];
1303 if (LayoutUnit increment = increments[i++])
1304 track.setBaseSize(track.baseSize() + increment);
1305 }
1306 this->freeSpace(m_direction) -= totalGrowth;
1307 m_maxContentSize += totalGrowth;
1308 }
1309
1310 void GridTrackSizingAlgorithm::advanceNextState() {
1311 switch (m_sizingState) {
1312 case ColumnSizingFirstIteration:
1313 m_sizingState = RowSizingFirstIteration;
1314 return;
1315 case RowSizingFirstIteration:
1316 m_sizingState = ColumnSizingSecondIteration;
1317 return;
1318 case ColumnSizingSecondIteration:
1319 m_sizingState = RowSizingSecondIteration;
1320 return;
1321 case RowSizingSecondIteration:
1322 m_sizingState = ColumnSizingFirstIteration;
1323 return;
1324 }
1325 NOTREACHED();
1326 m_sizingState = ColumnSizingFirstIteration;
1327 }
1328
1329 bool GridTrackSizingAlgorithm::isValidTransition() const {
1330 switch (m_sizingState) {
1331 case ColumnSizingFirstIteration:
1332 case ColumnSizingSecondIteration:
1333 return m_direction == ForColumns;
1334 case RowSizingFirstIteration:
1335 case RowSizingSecondIteration:
1336 return m_direction == ForRows;
1337 }
1338 NOTREACHED();
1339 return false;
1340 }
1341
1342 void GridTrackSizingAlgorithm::setup(GridTrackSizingDirection direction,
1343 size_t numTracks,
1344 SizingOperation sizingOperation,
1345 LayoutUnit availableSpace,
1346 LayoutUnit freeSpace) {
1347 DCHECK(m_needsSetup);
1348 m_direction = direction;
1349 m_availableSpace = availableSpace;
1350
1351 m_sizingOperation = sizingOperation;
1352 switch (m_sizingOperation) {
1353 case IntrinsicSizeComputation:
1354 m_strategy = WTF::makeUnique<IndefiniteSizeStrategy>(*this);
1355 break;
1356 case TrackSizing:
1357 m_strategy = WTF::makeUnique<DefiniteSizeStrategy>(*this);
1358 break;
1359 }
1360
1361 m_contentSizedTracksIndex.shrink(0);
1362 m_flexibleSizedTracksIndex.shrink(0);
1363
1364 this->freeSpace(direction) = freeSpace;
1365 tracks(direction).resize(numTracks);
1366
1367 m_needsSetup = false;
1368 }
1369
1370 // Described in https://drafts.csswg.org/css-grid/#algo-track-sizing
1371 void GridTrackSizingAlgorithm::run() {
1372 StateMachine stateMachine(*this);
1373
1374 // Step 1.
1375 LayoutUnit initialFreeSpace = freeSpace(m_direction);
1376 initializeTrackSizes();
1377
1378 // Step 2.
1379 if (!m_contentSizedTracksIndex.isEmpty())
1380 resolveIntrinsicTrackSizes();
1381
1382 // This is not exactly a step of the track sizing algorithm, but we use the
1383 // track sizes computed
1384 // up to this moment (before maximization) to calculate the grid container
1385 // intrinsic sizes.
1386 computeGridContainerIntrinsicSizes();
1387 freeSpace(m_direction) -= m_minContentSize;
1388
1389 if (m_sizingOperation == TrackSizing && freeSpace(m_direction) <= 0)
1390 return;
1391
1392 // Step 3.
1393 m_strategy->maximizeTracks(tracks(m_direction), freeSpace(m_direction));
1394
1395 if (m_flexibleSizedTracksIndex.isEmpty())
1396 return;
1397
1398 // Step 4.
1399 stretchFlexibleTracks(initialFreeSpace);
1400 }
1401
1402 void GridTrackSizingAlgorithm::reset() {
1403 m_sizingState = ColumnSizingFirstIteration;
1404 m_columns.shrink(0);
1405 m_rows.shrink(0);
1406 m_contentSizedTracksIndex.shrink(0);
1407 m_flexibleSizedTracksIndex.shrink(0);
1408 }
1409
1410 #if DCHECK_IS_ON()
1411 bool GridTrackSizingAlgorithm::tracksAreWiderThanMinTrackBreadth() const {
1412 const Vector<GridTrack>& allTracks = tracks(m_direction);
1413 for (size_t i = 0; i < allTracks.size(); ++i) {
1414 GridTrackSize trackSize = gridTrackSize(m_direction, i);
1415 if (initialBaseSize(trackSize) > allTracks[i].baseSize())
1416 return false;
1417 }
1418 return true;
1419 }
1420 #endif
1421
1422 GridTrackSizingAlgorithm::StateMachine::StateMachine(
1423 GridTrackSizingAlgorithm& algorithm)
1424 : m_algorithm(algorithm) {
1425 DCHECK(m_algorithm.isValidTransition());
1426 DCHECK(!m_algorithm.m_needsSetup);
1427 }
1428
1429 GridTrackSizingAlgorithm::StateMachine::~StateMachine() {
1430 m_algorithm.advanceNextState();
1431 m_algorithm.m_needsSetup = true;
1432 }
1433
1434 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/layout/GridTrackSizingAlgorithm.h ('k') | third_party/WebKit/Source/core/layout/LayoutGrid.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698