/
RenderGrid.cpp
1697 lines (1440 loc) · 81.5 KB
/
RenderGrid.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright (C) 2011 Apple Inc. All rights reserved.
* Copyright (C) 2013-2017 Igalia S.L.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "RenderGrid.h"
#include "GridArea.h"
#include "GridPositionsResolver.h"
#include "GridTrackSizingAlgorithm.h"
#include "LayoutRepainter.h"
#include "RenderChildIterator.h"
#include "RenderLayer.h"
#include "RenderView.h"
#include <cstdlib>
namespace WebCore {
enum TrackSizeRestriction {
AllowInfinity,
ForbidInfinity,
};
struct ContentAlignmentData {
WTF_MAKE_FAST_ALLOCATED;
public:
bool isValid() { return positionOffset >= 0 && distributionOffset >= 0; }
static ContentAlignmentData defaultOffsets() { return {-1, -1}; }
LayoutUnit positionOffset;
LayoutUnit distributionOffset;
};
RenderGrid::RenderGrid(Element& element, RenderStyle&& style)
: RenderBlock(element, WTFMove(style), 0)
, m_grid(*this)
, m_trackSizingAlgorithm(this, m_grid)
{
// All of our children must be block level.
setChildrenInline(false);
}
RenderGrid::~RenderGrid()
{
}
void RenderGrid::addChild(RenderObject* newChild, RenderObject* beforeChild)
{
RenderBlock::addChild(newChild, beforeChild);
// Positioned grid items do not take up space or otherwise participate in the layout of the grid,
// for that reason we don't need to mark the grid as dirty when they are added.
if (newChild->isOutOfFlowPositioned())
return;
// The grid needs to be recomputed as it might contain auto-placed items that
// will change their position.
dirtyGrid();
}
void RenderGrid::removeChild(RenderObject& child)
{
RenderBlock::removeChild(child);
// Positioned grid items do not take up space or otherwise participate in the layout of the grid,
// for that reason we don't need to mark the grid as dirty when they are removed.
if (child.isOutOfFlowPositioned())
return;
// The grid needs to be recomputed as it might contain auto-placed items that
// will change their position.
dirtyGrid();
}
StyleSelfAlignmentData RenderGrid::selfAlignmentForChild(GridAxis axis, const RenderBox& child, const RenderStyle* gridStyle) const
{
return axis == GridRowAxis ? justifySelfForChild(child, gridStyle) : alignSelfForChild(child, gridStyle);
}
bool RenderGrid::selfAlignmentChangedToStretch(GridAxis axis, const RenderStyle& oldStyle, const RenderStyle& newStyle, const RenderBox& child) const
{
return selfAlignmentForChild(axis, child, &oldStyle).position() != ItemPositionStretch
&& selfAlignmentForChild(axis, child, &newStyle).position() == ItemPositionStretch;
}
bool RenderGrid::selfAlignmentChangedFromStretch(GridAxis axis, const RenderStyle& oldStyle, const RenderStyle& newStyle, const RenderBox& child) const
{
return selfAlignmentForChild(axis, child, &oldStyle).position() == ItemPositionStretch
&& selfAlignmentForChild(axis, child, &newStyle).position() != ItemPositionStretch;
}
void RenderGrid::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderBlock::styleDidChange(diff, oldStyle);
if (!oldStyle || diff != StyleDifferenceLayout)
return;
const RenderStyle& newStyle = this->style();
if (oldStyle->resolvedAlignItems(selfAlignmentNormalBehavior(this)).position() == ItemPositionStretch) {
// Style changes on the grid container implying stretching (to-stretch) or
// shrinking (from-stretch) require the affected items to be laid out again.
// These logic only applies to 'stretch' since the rest of the alignment
// values don't change the size of the box.
// In any case, the items' overrideSize will be cleared and recomputed (if
// necessary) as part of the Grid layout logic, triggered by this style
// change.
for (auto& child : childrenOfType<RenderBox>(*this)) {
if (child.isOutOfFlowPositioned())
continue;
if (selfAlignmentChangedToStretch(GridRowAxis, *oldStyle, newStyle, child)
|| selfAlignmentChangedFromStretch(GridRowAxis, *oldStyle, newStyle, child)
|| selfAlignmentChangedToStretch(GridColumnAxis, *oldStyle, newStyle, child)
|| selfAlignmentChangedFromStretch(GridColumnAxis, *oldStyle, newStyle, child)) {
child.setNeedsLayout();
}
}
}
if (explicitGridDidResize(*oldStyle) || namedGridLinesDefinitionDidChange(*oldStyle) || oldStyle->gridAutoFlow() != style().gridAutoFlow()
|| (style().gridAutoRepeatColumns().size() || style().gridAutoRepeatRows().size()))
dirtyGrid();
}
bool RenderGrid::explicitGridDidResize(const RenderStyle& oldStyle) const
{
return oldStyle.gridColumns().size() != style().gridColumns().size()
|| oldStyle.gridRows().size() != style().gridRows().size()
|| oldStyle.namedGridAreaColumnCount() != style().namedGridAreaColumnCount()
|| oldStyle.namedGridAreaRowCount() != style().namedGridAreaRowCount()
|| oldStyle.gridAutoRepeatColumns().size() != style().gridAutoRepeatColumns().size()
|| oldStyle.gridAutoRepeatRows().size() != style().gridAutoRepeatRows().size();
}
bool RenderGrid::namedGridLinesDefinitionDidChange(const RenderStyle& oldStyle) const
{
return oldStyle.namedGridRowLines() != style().namedGridRowLines()
|| oldStyle.namedGridColumnLines() != style().namedGridColumnLines();
}
// This method optimizes the gutters computation by skiping the available size
// call if gaps are fixed size (it's only needed for percentages).
std::optional<LayoutUnit> RenderGrid::availableSpaceForGutters(GridTrackSizingDirection direction) const
{
bool isRowAxis = direction == ForColumns;
const Length& gap = isRowAxis ? style().gridColumnGap() : style().gridRowGap();
if (!gap.isPercent())
return std::nullopt;
return isRowAxis ? availableLogicalWidth() : availableLogicalHeightForPercentageComputation();
}
LayoutUnit RenderGrid::computeTrackBasedLogicalHeight() const
{
LayoutUnit logicalHeight;
auto& allRows = m_trackSizingAlgorithm.tracks(ForRows);
for (const auto& row : allRows)
logicalHeight += row.baseSize();
logicalHeight += guttersSize(m_grid, ForRows, 0, allRows.size(), availableSpaceForGutters(ForRows));
return logicalHeight;
}
void RenderGrid::computeTrackSizesForDefiniteSize(GridTrackSizingDirection direction, LayoutUnit availableSpace)
{
LayoutUnit totalGuttersSize = guttersSize(m_grid, direction, 0, m_grid.numTracks(direction), availableSpace);
LayoutUnit freeSpace = availableSpace - totalGuttersSize;
m_trackSizingAlgorithm.setup(direction, numTracks(direction, m_grid), TrackSizing, availableSpace, freeSpace);
m_trackSizingAlgorithm.run();
ASSERT(m_trackSizingAlgorithm.tracksAreWiderThanMinTrackBreadth());
}
void RenderGrid::repeatTracksSizingIfNeeded(LayoutUnit availableSpaceForColumns, LayoutUnit availableSpaceForRows)
{
// In orthogonal flow cases column track's size is determined by using the computed
// row track's size, which it was estimated during the first cycle of the sizing
// algorithm. Hence we need to repeat computeUsedBreadthOfGridTracks for both,
// columns and rows, to determine the final values.
// TODO (lajava): orthogonal flows is just one of the cases which may require
// a new cycle of the sizing algorithm; there may be more. In addition, not all the
// cases with orthogonal flows require this extra cycle; we need a more specific
// condition to detect whether child's min-content contribution has changed or not.
if (m_grid.hasAnyOrthogonalGridItem()) {
computeTrackSizesForDefiniteSize(ForColumns, availableSpaceForColumns);
computeTrackSizesForDefiniteSize(ForRows, availableSpaceForRows);
}
}
bool RenderGrid::canPerformSimplifiedLayout() const
{
// We cannot perform a simplified layout if we need to position the items and we have some
// positioned items to be laid out.
if (m_grid.needsItemsPlacement() && posChildNeedsLayout())
return false;
return RenderBlock::canPerformSimplifiedLayout();
}
void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit)
{
ASSERT(needsLayout());
if (!relayoutChildren && simplifiedLayout())
return;
LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
preparePaginationBeforeBlockLayout(relayoutChildren);
LayoutSize previousSize = size();
// FIXME: We should use RenderBlock::hasDefiniteLogicalHeight() but it does not work for positioned stuff.
// FIXME: Consider caching the hasDefiniteLogicalHeight value throughout the layout.
bool hasDefiniteLogicalHeight = hasOverrideLogicalContentHeight() || computeContentLogicalHeight(MainOrPreferredSize, style().logicalHeight(), std::nullopt);
// We need to clear both own and containingBlock override sizes of orthogonal items to ensure we get the
// same result when grid's intrinsic size is computed again in the updateLogicalWidth call bellow.
if (sizesLogicalWidthToFitContent(MaxSize) || style().logicalWidth().isIntrinsicOrAuto()) {
for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) {
if (child->isOutOfFlowPositioned() || !isOrthogonalChild(*child))
continue;
child->clearOverrideSize();
child->clearContainingBlockOverrideSize();
child->setNeedsLayout();
child->layoutIfNeeded();
}
}
setLogicalHeight(0);
updateLogicalWidth();
// Fieldsets need to find their legend and position it inside the border of the object.
// The legend then gets skipped during normal layout. The same is true for ruby text.
// It doesn't get included in the normal layout process but is instead skipped.
layoutExcludedChildren(relayoutChildren);
LayoutUnit availableSpaceForColumns = availableLogicalWidth();
placeItemsOnGrid(m_grid, availableSpaceForColumns);
// At this point the logical width is always definite as the above call to updateLogicalWidth()
// properly resolves intrinsic sizes. We cannot do the same for heights though because many code
// paths inside updateLogicalHeight() require a previous call to setLogicalHeight() to resolve
// heights properly (like for positioned items for example).
computeTrackSizesForDefiniteSize(ForColumns, availableSpaceForColumns);
if (!hasDefiniteLogicalHeight) {
m_minContentHeight = LayoutUnit();
m_maxContentHeight = LayoutUnit();
computeTrackSizesForIndefiniteSize(m_trackSizingAlgorithm, ForRows, m_grid, *m_minContentHeight, *m_maxContentHeight);
// FIXME: This should be really added to the intrinsic height in RenderBox::computeContentAndScrollbarLogicalHeightUsing().
// Remove this when that is fixed.
ASSERT(m_minContentHeight);
ASSERT(m_maxContentHeight);
LayoutUnit scrollbarHeight = scrollbarLogicalHeight();
*m_minContentHeight += scrollbarHeight;
*m_maxContentHeight += scrollbarHeight;
} else
computeTrackSizesForDefiniteSize(ForRows, availableLogicalHeight(ExcludeMarginBorderPadding));
LayoutUnit trackBasedLogicalHeight = computeTrackBasedLogicalHeight() + borderAndPaddingLogicalHeight() + scrollbarLogicalHeight();
setLogicalHeight(trackBasedLogicalHeight);
LayoutUnit oldClientAfterEdge = clientLogicalBottom();
updateLogicalHeight();
// Once grid's indefinite height is resolved, we can compute the
// available free space for Content Alignment.
if (!hasDefiniteLogicalHeight)
m_trackSizingAlgorithm.setFreeSpace(ForRows, logicalHeight() - trackBasedLogicalHeight);
// 3- If the min-content contribution of any grid items have changed based on the row
// sizes calculated in step 2, steps 1 and 2 are repeated with the new min-content
// contribution (once only).
repeatTracksSizingIfNeeded(availableSpaceForColumns, contentLogicalHeight());
// Grid container should have the minimum height of a line if it's editable. That does not affect track sizing though.
if (hasLineIfEmpty()) {
LayoutUnit minHeightForEmptyLine = borderAndPaddingLogicalHeight()
+ lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)
+ scrollbarLogicalHeight();
setLogicalHeight(std::max(logicalHeight(), minHeightForEmptyLine));
}
layoutGridItems();
m_trackSizingAlgorithm.reset();
if (size() != previousSize)
relayoutChildren = true;
layoutPositionedObjects(relayoutChildren || isDocumentElementRenderer());
computeOverflow(oldClientAfterEdge);
statePusher.pop();
updateLayerTransform();
// Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
// we overflow or not.
updateScrollInfoAfterLayout();
repainter.repaintAfterLayout();
clearNeedsLayout();
}
LayoutUnit RenderGrid::gridGap(GridTrackSizingDirection direction, std::optional<LayoutUnit> availableSize) const
{
const Length& gap = direction == ForColumns ? style().gridColumnGap() : style().gridRowGap();
return valueForLength(gap, availableSize.value_or(0));
}
LayoutUnit RenderGrid::gridGap(GridTrackSizingDirection direction) const
{
return gridGap(direction, availableSpaceForGutters(direction));
}
LayoutUnit RenderGrid::guttersSize(const Grid& grid, GridTrackSizingDirection direction, unsigned startLine, unsigned span, std::optional<LayoutUnit> availableSize) const
{
if (span <= 1)
return { };
LayoutUnit gap = gridGap(direction, availableSize);
// Fast path, no collapsing tracks.
if (!grid.hasAutoRepeatEmptyTracks(direction))
return gap * (span - 1);
// If there are collapsing tracks we need to be sure that gutters are properly collapsed. Apart
// from that, if we have a collapsed track in the edges of the span we're considering, we need
// to move forward (or backwards) in order to know whether the collapsed tracks reach the end of
// the grid (so the gap becomes 0) or there is a non empty track before that.
LayoutUnit gapAccumulator;
unsigned endLine = startLine + span;
for (unsigned line = startLine; line < endLine - 1; ++line) {
if (!grid.isEmptyAutoRepeatTrack(direction, line))
gapAccumulator += gap;
}
// The above loop adds one extra gap for trailing collapsed tracks.
if (gapAccumulator && grid.isEmptyAutoRepeatTrack(direction, endLine - 1)) {
ASSERT(gapAccumulator >= gap);
gapAccumulator -= gap;
}
// If the startLine is the start line of a collapsed track we need to go backwards till we reach
// a non collapsed track. If we find a non collapsed track we need to add that gap.
if (startLine && grid.isEmptyAutoRepeatTrack(direction, startLine)) {
unsigned nonEmptyTracksBeforeStartLine = startLine;
auto begin = grid.autoRepeatEmptyTracks(direction)->begin();
for (auto it = begin; *it != startLine; ++it) {
ASSERT(nonEmptyTracksBeforeStartLine);
--nonEmptyTracksBeforeStartLine;
}
if (nonEmptyTracksBeforeStartLine)
gapAccumulator += gap;
}
// If the endLine is the end line of a collapsed track we need to go forward till we reach a non
// collapsed track. If we find a non collapsed track we need to add that gap.
if (grid.isEmptyAutoRepeatTrack(direction, endLine - 1)) {
unsigned nonEmptyTracksAfterEndLine = grid.numTracks(direction) - endLine;
auto currentEmptyTrack = grid.autoRepeatEmptyTracks(direction)->find(endLine - 1);
auto endEmptyTrack = grid.autoRepeatEmptyTracks(direction)->end();
// HashSet iterators do not implement operator- so we have to manually iterate to know the number of remaining empty tracks.
for (auto it = ++currentEmptyTrack; it != endEmptyTrack; ++it) {
ASSERT(nonEmptyTracksAfterEndLine >= 1);
--nonEmptyTracksAfterEndLine;
}
if (nonEmptyTracksAfterEndLine)
gapAccumulator += gap;
}
return gapAccumulator;
}
void RenderGrid::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
{
LayoutUnit childMinWidth;
LayoutUnit childMaxWidth;
bool hadExcludedChildren = computePreferredWidthsForExcludedChildren(childMinWidth, childMaxWidth);
Grid grid(const_cast<RenderGrid&>(*this));
placeItemsOnGrid(grid, std::nullopt);
GridTrackSizingAlgorithm algorithm(this, grid);
computeTrackSizesForIndefiniteSize(algorithm, ForColumns, grid, minLogicalWidth, maxLogicalWidth);
if (hadExcludedChildren) {
minLogicalWidth = std::max(minLogicalWidth, childMinWidth);
maxLogicalWidth = std::max(maxLogicalWidth, childMaxWidth);
}
LayoutUnit scrollbarWidth = intrinsicScrollbarLogicalWidth();
minLogicalWidth += scrollbarWidth;
maxLogicalWidth += scrollbarWidth;
}
void RenderGrid::computeTrackSizesForIndefiniteSize(GridTrackSizingAlgorithm& algorithm, GridTrackSizingDirection direction, Grid& grid, LayoutUnit& minIntrinsicSize, LayoutUnit& maxIntrinsicSize) const
{
algorithm.setup(direction, numTracks(direction, grid), IntrinsicSizeComputation, std::nullopt, std::nullopt);
algorithm.run();
size_t numberOfTracks = algorithm.tracks(direction).size();
LayoutUnit totalGuttersSize = guttersSize(grid, direction, 0, numberOfTracks, std::nullopt);
minIntrinsicSize = algorithm.minContentSize() + totalGuttersSize;
maxIntrinsicSize = algorithm.maxContentSize() + totalGuttersSize;
ASSERT(algorithm.tracksAreWiderThanMinTrackBreadth());
}
std::optional<LayoutUnit> RenderGrid::computeIntrinsicLogicalContentHeightUsing(Length logicalHeightLength, std::optional<LayoutUnit> intrinsicLogicalHeight, LayoutUnit borderAndPadding) const
{
if (!intrinsicLogicalHeight)
return std::nullopt;
if (logicalHeightLength.isMinContent())
return m_minContentHeight;
if (logicalHeightLength.isMaxContent())
return m_maxContentHeight;
if (logicalHeightLength.isFitContent()) {
LayoutUnit fillAvailableExtent = containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding);
return std::min(m_maxContentHeight.value_or(0), std::max(m_minContentHeight.value_or(0), fillAvailableExtent));
}
if (logicalHeightLength.isFillAvailable())
return containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding) - borderAndPadding;
ASSERT_NOT_REACHED();
return std::nullopt;
}
static std::optional<LayoutUnit> overrideContainingBlockContentSizeForChild(const RenderBox& child, GridTrackSizingDirection direction)
{
return direction == ForColumns ? child.overrideContainingBlockContentLogicalWidth() : child.overrideContainingBlockContentLogicalHeight();
}
bool RenderGrid::isOrthogonalChild(const RenderBox& child) const
{
return child.isHorizontalWritingMode() != isHorizontalWritingMode();
}
GridTrackSizingDirection RenderGrid::flowAwareDirectionForChild(const RenderBox& child, GridTrackSizingDirection direction) const
{
return !isOrthogonalChild(child) ? direction : (direction == ForColumns ? ForRows : ForColumns);
}
unsigned RenderGrid::computeAutoRepeatTracksCount(GridTrackSizingDirection direction, std::optional<LayoutUnit> availableSize) const
{
ASSERT(!availableSize || availableSize.value() != -1);
bool isRowAxis = direction == ForColumns;
const auto& autoRepeatTracks = isRowAxis ? style().gridAutoRepeatColumns() : style().gridAutoRepeatRows();
unsigned autoRepeatTrackListLength = autoRepeatTracks.size();
if (!autoRepeatTrackListLength)
return 0;
if (!isRowAxis && !availableSize) {
const Length& maxLength = style().logicalMaxHeight();
if (!maxLength.isUndefined()) {
availableSize = computeContentLogicalHeight(MaxSize, maxLength, std::nullopt);
if (availableSize)
availableSize = constrainContentBoxLogicalHeightByMinMax(availableSize.value(), std::nullopt);
}
}
bool needsToFulfillMinimumSize = false;
if (!availableSize) {
const Length& minSize = isRowAxis ? style().logicalMinWidth() : style().logicalMinHeight();
if (!minSize.isSpecified())
return autoRepeatTrackListLength;
LayoutUnit containingBlockAvailableSize = isRowAxis ? containingBlockLogicalWidthForContent() : containingBlockLogicalHeightForContent(ExcludeMarginBorderPadding);
availableSize = valueForLength(minSize, containingBlockAvailableSize);
needsToFulfillMinimumSize = true;
}
LayoutUnit autoRepeatTracksSize;
for (auto& autoTrackSize : autoRepeatTracks) {
ASSERT(autoTrackSize.minTrackBreadth().isLength());
ASSERT(!autoTrackSize.minTrackBreadth().isFlex());
bool hasDefiniteMaxTrackSizingFunction = autoTrackSize.maxTrackBreadth().isLength() && !autoTrackSize.maxTrackBreadth().isContentSized();
auto trackLength = hasDefiniteMaxTrackSizingFunction ? autoTrackSize.maxTrackBreadth().length() : autoTrackSize.minTrackBreadth().length();
autoRepeatTracksSize += valueForLength(trackLength, availableSize.value());
}
// For the purpose of finding the number of auto-repeated tracks, the UA must floor the track size to a UA-specified
// value to avoid division by zero. It is suggested that this floor be 1px.
autoRepeatTracksSize = std::max<LayoutUnit>(LayoutUnit(1), autoRepeatTracksSize);
// There will be always at least 1 auto-repeat track, so take it already into account when computing the total track size.
LayoutUnit tracksSize = autoRepeatTracksSize;
auto& trackSizes = isRowAxis ? style().gridColumns() : style().gridRows();
for (const auto& track : trackSizes) {
bool hasDefiniteMaxTrackBreadth = track.maxTrackBreadth().isLength() && !track.maxTrackBreadth().isContentSized();
ASSERT(hasDefiniteMaxTrackBreadth || (track.minTrackBreadth().isLength() && !track.minTrackBreadth().isContentSized()));
tracksSize += valueForLength(hasDefiniteMaxTrackBreadth ? track.maxTrackBreadth().length() : track.minTrackBreadth().length(), availableSize.value());
}
// Add gutters as if there where only 1 auto repeat track. Gaps between auto repeat tracks will be added later when
// computing the repetitions.
LayoutUnit gapSize = gridGap(direction, availableSize);
tracksSize += gapSize * trackSizes.size();
LayoutUnit freeSpace = availableSize.value() - tracksSize;
if (freeSpace <= 0)
return autoRepeatTrackListLength;
unsigned repetitions = 1 + (freeSpace / (autoRepeatTracksSize + gapSize)).toInt();
// Provided the grid container does not have a definite size or max-size in the relevant axis,
// if the min size is definite then the number of repetitions is the largest possible positive
// integer that fulfills that minimum requirement.
if (needsToFulfillMinimumSize)
++repetitions;
return repetitions * autoRepeatTrackListLength;
}
std::unique_ptr<OrderedTrackIndexSet> RenderGrid::computeEmptyTracksForAutoRepeat(Grid& grid, GridTrackSizingDirection direction) const
{
bool isRowAxis = direction == ForColumns;
if ((isRowAxis && style().gridAutoRepeatColumnsType() != AutoFit)
|| (!isRowAxis && style().gridAutoRepeatRowsType() != AutoFit))
return nullptr;
std::unique_ptr<OrderedTrackIndexSet> emptyTrackIndexes;
unsigned insertionPoint = isRowAxis ? style().gridAutoRepeatColumnsInsertionPoint() : style().gridAutoRepeatRowsInsertionPoint();
unsigned firstAutoRepeatTrack = insertionPoint + std::abs(grid.smallestTrackStart(direction));
unsigned lastAutoRepeatTrack = firstAutoRepeatTrack + grid.autoRepeatTracks(direction);
if (!grid.hasGridItems()) {
emptyTrackIndexes = std::make_unique<OrderedTrackIndexSet>();
for (unsigned trackIndex = firstAutoRepeatTrack; trackIndex < lastAutoRepeatTrack; ++trackIndex)
emptyTrackIndexes->add(trackIndex);
} else {
for (unsigned trackIndex = firstAutoRepeatTrack; trackIndex < lastAutoRepeatTrack; ++trackIndex) {
GridIterator iterator(grid, direction, trackIndex);
if (!iterator.nextGridItem()) {
if (!emptyTrackIndexes)
emptyTrackIndexes = std::make_unique<OrderedTrackIndexSet>();
emptyTrackIndexes->add(trackIndex);
}
}
}
return emptyTrackIndexes;
}
unsigned RenderGrid::clampAutoRepeatTracks(GridTrackSizingDirection direction, unsigned autoRepeatTracks) const
{
if (!autoRepeatTracks)
return 0;
unsigned insertionPoint = direction == ForColumns ? style().gridAutoRepeatColumnsInsertionPoint() : style().gridAutoRepeatRowsInsertionPoint();
unsigned maxTracks = static_cast<unsigned>(GridPosition::max());
if (!insertionPoint)
return std::min(autoRepeatTracks, maxTracks);
if (insertionPoint >= maxTracks)
return 0;
return std::min(autoRepeatTracks, maxTracks - insertionPoint);
}
// FIXME): We shouldn't have to pass the available logical width as argument. The problem is that
// availableLogicalWidth() does always return a value even if we cannot resolve it like when
// computing the intrinsic size (preferred widths). That's why we pass the responsibility to the
// caller who does know whether the available logical width is indefinite or not.
void RenderGrid::placeItemsOnGrid(Grid& grid, std::optional<LayoutUnit> availableSpace) const
{
unsigned autoRepeatColumns = computeAutoRepeatTracksCount(ForColumns, availableSpace);
unsigned autoRepeatRows = computeAutoRepeatTracksCount(ForRows, availableLogicalHeightForPercentageComputation());
autoRepeatRows = clampAutoRepeatTracks(ForRows, autoRepeatRows);
autoRepeatColumns = clampAutoRepeatTracks(ForColumns, autoRepeatColumns);
if (autoRepeatColumns != grid.autoRepeatTracks(ForColumns) || autoRepeatRows != grid.autoRepeatTracks(ForRows)) {
grid.setNeedsItemsPlacement(true);
grid.setAutoRepeatTracks(autoRepeatRows, autoRepeatColumns);
}
if (!grid.needsItemsPlacement())
return;
ASSERT(!grid.hasGridItems());
populateExplicitGridAndOrderIterator(grid);
Vector<RenderBox*> autoMajorAxisAutoGridItems;
Vector<RenderBox*> specifiedMajorAxisAutoGridItems;
bool hasAnyOrthogonalGridItem = false;
for (auto* child = grid.orderIterator().first(); child; child = grid.orderIterator().next()) {
if (grid.orderIterator().shouldSkipChild(*child))
continue;
hasAnyOrthogonalGridItem = hasAnyOrthogonalGridItem || isOrthogonalChild(*child);
GridArea area = grid.gridItemArea(*child);
if (!area.rows.isIndefinite())
area.rows.translate(std::abs(grid.smallestTrackStart(ForRows)));
if (!area.columns.isIndefinite())
area.columns.translate(std::abs(grid.smallestTrackStart(ForColumns)));
if (area.rows.isIndefinite() || area.columns.isIndefinite()) {
grid.setGridItemArea(*child, area);
bool majorAxisDirectionIsForColumns = autoPlacementMajorAxisDirection() == ForColumns;
if ((majorAxisDirectionIsForColumns && area.columns.isIndefinite())
|| (!majorAxisDirectionIsForColumns && area.rows.isIndefinite()))
autoMajorAxisAutoGridItems.append(child);
else
specifiedMajorAxisAutoGridItems.append(child);
continue;
}
grid.insert(*child, { area.rows, area.columns });
}
grid.setHasAnyOrthogonalGridItem(hasAnyOrthogonalGridItem);
#if ENABLE(ASSERT)
if (grid.hasGridItems()) {
ASSERT(grid.numTracks(ForRows) >= GridPositionsResolver::explicitGridRowCount(style(), grid.autoRepeatTracks(ForRows)));
ASSERT(grid.numTracks(ForColumns) >= GridPositionsResolver::explicitGridColumnCount(style(), grid.autoRepeatTracks(ForColumns)));
}
#endif
placeSpecifiedMajorAxisItemsOnGrid(grid, specifiedMajorAxisAutoGridItems);
placeAutoMajorAxisItemsOnGrid(grid, autoMajorAxisAutoGridItems);
// Compute collapsible tracks for auto-fit.
grid.setAutoRepeatEmptyColumns(computeEmptyTracksForAutoRepeat(grid, ForColumns));
grid.setAutoRepeatEmptyRows(computeEmptyTracksForAutoRepeat(grid, ForRows));
grid.setNeedsItemsPlacement(false);
#if ENABLE(ASSERT)
for (auto* child = grid.orderIterator().first(); child; child = grid.orderIterator().next()) {
if (grid.orderIterator().shouldSkipChild(*child))
continue;
GridArea area = grid.gridItemArea(*child);
ASSERT(area.rows.isTranslatedDefinite() && area.columns.isTranslatedDefinite());
}
#endif
}
void RenderGrid::populateExplicitGridAndOrderIterator(Grid& grid) const
{
OrderIteratorPopulator populator(grid.orderIterator());
int smallestRowStart = 0;
int smallestColumnStart = 0;
unsigned autoRepeatRows = grid.autoRepeatTracks(ForRows);
unsigned autoRepeatColumns = grid.autoRepeatTracks(ForColumns);
unsigned maximumRowIndex = GridPositionsResolver::explicitGridRowCount(style(), autoRepeatRows);
unsigned maximumColumnIndex = GridPositionsResolver::explicitGridColumnCount(style(), autoRepeatColumns);
for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
if (!populator.collectChild(*child))
continue;
GridSpan rowPositions = GridPositionsResolver::resolveGridPositionsFromStyle(style(), *child, ForRows, autoRepeatRows);
if (!rowPositions.isIndefinite()) {
smallestRowStart = std::min(smallestRowStart, rowPositions.untranslatedStartLine());
maximumRowIndex = std::max<int>(maximumRowIndex, rowPositions.untranslatedEndLine());
} else {
// Grow the grid for items with a definite row span, getting the largest such span.
unsigned spanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(style(), *child, ForRows);
maximumRowIndex = std::max(maximumRowIndex, spanSize);
}
GridSpan columnPositions = GridPositionsResolver::resolveGridPositionsFromStyle(style(), *child, ForColumns, autoRepeatColumns);
if (!columnPositions.isIndefinite()) {
smallestColumnStart = std::min(smallestColumnStart, columnPositions.untranslatedStartLine());
maximumColumnIndex = std::max<int>(maximumColumnIndex, columnPositions.untranslatedEndLine());
} else {
// Grow the grid for items with a definite column span, getting the largest such span.
unsigned spanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(style(), *child, ForColumns);
maximumColumnIndex = std::max(maximumColumnIndex, spanSize);
}
grid.setGridItemArea(*child, { rowPositions, columnPositions });
}
grid.setSmallestTracksStart(smallestRowStart, smallestColumnStart);
grid.ensureGridSize(maximumRowIndex + std::abs(smallestRowStart), maximumColumnIndex + std::abs(smallestColumnStart));
}
std::unique_ptr<GridArea> RenderGrid::createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(Grid& grid, const RenderBox& gridItem, GridTrackSizingDirection specifiedDirection, const GridSpan& specifiedPositions) const
{
GridTrackSizingDirection crossDirection = specifiedDirection == ForColumns ? ForRows : ForColumns;
const unsigned endOfCrossDirection = grid.numTracks(crossDirection);
unsigned crossDirectionSpanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(style(), gridItem, crossDirection);
GridSpan crossDirectionPositions = GridSpan::translatedDefiniteGridSpan(endOfCrossDirection, endOfCrossDirection + crossDirectionSpanSize);
return std::make_unique<GridArea>(specifiedDirection == ForColumns ? crossDirectionPositions : specifiedPositions, specifiedDirection == ForColumns ? specifiedPositions : crossDirectionPositions);
}
void RenderGrid::placeSpecifiedMajorAxisItemsOnGrid(Grid& grid, const Vector<RenderBox*>& autoGridItems) const
{
bool isForColumns = autoPlacementMajorAxisDirection() == ForColumns;
bool isGridAutoFlowDense = style().isGridAutoFlowAlgorithmDense();
// Mapping between the major axis tracks (rows or columns) and the last auto-placed item's position inserted on
// that track. This is needed to implement "sparse" packing for items locked to a given track.
// See http://dev.w3.org/csswg/css-grid/#auto-placement-algorithm
HashMap<unsigned, unsigned, DefaultHash<unsigned>::Hash, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> minorAxisCursors;
for (auto& autoGridItem : autoGridItems) {
GridSpan majorAxisPositions = grid.gridItemSpan(*autoGridItem, autoPlacementMajorAxisDirection());
ASSERT(majorAxisPositions.isTranslatedDefinite());
ASSERT(grid.gridItemSpan(*autoGridItem, autoPlacementMinorAxisDirection()).isIndefinite());
unsigned minorAxisSpanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(style(), *autoGridItem, autoPlacementMinorAxisDirection());
unsigned majorAxisInitialPosition = majorAxisPositions.startLine();
GridIterator iterator(grid, autoPlacementMajorAxisDirection(), majorAxisPositions.startLine(), isGridAutoFlowDense ? 0 : minorAxisCursors.get(majorAxisInitialPosition));
std::unique_ptr<GridArea> emptyGridArea = iterator.nextEmptyGridArea(majorAxisPositions.integerSpan(), minorAxisSpanSize);
if (!emptyGridArea)
emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(grid, *autoGridItem, autoPlacementMajorAxisDirection(), majorAxisPositions);
grid.insert(*autoGridItem, *emptyGridArea);
if (!isGridAutoFlowDense)
minorAxisCursors.set(majorAxisInitialPosition, isForColumns ? emptyGridArea->rows.startLine() : emptyGridArea->columns.startLine());
}
}
void RenderGrid::placeAutoMajorAxisItemsOnGrid(Grid& grid, const Vector<RenderBox*>& autoGridItems) const
{
AutoPlacementCursor autoPlacementCursor = {0, 0};
bool isGridAutoFlowDense = style().isGridAutoFlowAlgorithmDense();
for (auto& autoGridItem : autoGridItems) {
placeAutoMajorAxisItemOnGrid(grid, *autoGridItem, autoPlacementCursor);
if (isGridAutoFlowDense) {
autoPlacementCursor.first = 0;
autoPlacementCursor.second = 0;
}
}
}
void RenderGrid::placeAutoMajorAxisItemOnGrid(Grid& grid, RenderBox& gridItem, AutoPlacementCursor& autoPlacementCursor) const
{
ASSERT(grid.gridItemSpan(gridItem, autoPlacementMajorAxisDirection()).isIndefinite());
unsigned majorAxisSpanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(style(), gridItem, autoPlacementMajorAxisDirection());
const unsigned endOfMajorAxis = grid.numTracks(autoPlacementMajorAxisDirection());
unsigned majorAxisAutoPlacementCursor = autoPlacementMajorAxisDirection() == ForColumns ? autoPlacementCursor.second : autoPlacementCursor.first;
unsigned minorAxisAutoPlacementCursor = autoPlacementMajorAxisDirection() == ForColumns ? autoPlacementCursor.first : autoPlacementCursor.second;
std::unique_ptr<GridArea> emptyGridArea;
GridSpan minorAxisPositions = grid.gridItemSpan(gridItem, autoPlacementMinorAxisDirection());
if (minorAxisPositions.isTranslatedDefinite()) {
// Move to the next track in major axis if initial position in minor axis is before auto-placement cursor.
if (minorAxisPositions.startLine() < minorAxisAutoPlacementCursor)
majorAxisAutoPlacementCursor++;
if (majorAxisAutoPlacementCursor < endOfMajorAxis) {
GridIterator iterator(grid, autoPlacementMinorAxisDirection(), minorAxisPositions.startLine(), majorAxisAutoPlacementCursor);
emptyGridArea = iterator.nextEmptyGridArea(minorAxisPositions.integerSpan(), majorAxisSpanSize);
}
if (!emptyGridArea)
emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(grid, gridItem, autoPlacementMinorAxisDirection(), minorAxisPositions);
} else {
unsigned minorAxisSpanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(style(), gridItem, autoPlacementMinorAxisDirection());
for (unsigned majorAxisIndex = majorAxisAutoPlacementCursor; majorAxisIndex < endOfMajorAxis; ++majorAxisIndex) {
GridIterator iterator(grid, autoPlacementMajorAxisDirection(), majorAxisIndex, minorAxisAutoPlacementCursor);
emptyGridArea = iterator.nextEmptyGridArea(majorAxisSpanSize, minorAxisSpanSize);
if (emptyGridArea) {
// Check that it fits in the minor axis direction, as we shouldn't grow in that direction here (it was already managed in populateExplicitGridAndOrderIterator()).
unsigned minorAxisFinalPositionIndex = autoPlacementMinorAxisDirection() == ForColumns ? emptyGridArea->columns.endLine() : emptyGridArea->rows.endLine();
const unsigned endOfMinorAxis = grid.numTracks(autoPlacementMinorAxisDirection());
if (minorAxisFinalPositionIndex <= endOfMinorAxis)
break;
// Discard empty grid area as it does not fit in the minor axis direction.
// We don't need to create a new empty grid area yet as we might find a valid one in the next iteration.
emptyGridArea = nullptr;
}
// As we're moving to the next track in the major axis we should reset the auto-placement cursor in the minor axis.
minorAxisAutoPlacementCursor = 0;
}
if (!emptyGridArea)
emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(grid, gridItem, autoPlacementMinorAxisDirection(), GridSpan::translatedDefiniteGridSpan(0, minorAxisSpanSize));
}
grid.insert(gridItem, *emptyGridArea);
autoPlacementCursor.first = emptyGridArea->rows.startLine();
autoPlacementCursor.second = emptyGridArea->columns.startLine();
}
GridTrackSizingDirection RenderGrid::autoPlacementMajorAxisDirection() const
{
return style().isGridAutoFlowDirectionColumn() ? ForColumns : ForRows;
}
GridTrackSizingDirection RenderGrid::autoPlacementMinorAxisDirection() const
{
return style().isGridAutoFlowDirectionColumn() ? ForRows : ForColumns;
}
void RenderGrid::dirtyGrid()
{
if (m_grid.needsItemsPlacement())
return;
m_grid.setNeedsItemsPlacement(true);
}
Vector<LayoutUnit> RenderGrid::trackSizesForComputedStyle(GridTrackSizingDirection direction) const
{
bool isRowAxis = direction == ForColumns;
auto& positions = isRowAxis ? m_columnPositions : m_rowPositions;
size_t numPositions = positions.size();
LayoutUnit offsetBetweenTracks = isRowAxis ? m_offsetBetweenColumns : m_offsetBetweenRows;
Vector<LayoutUnit> tracks;
if (numPositions < 2)
return tracks;
ASSERT(!m_grid.needsItemsPlacement());
bool hasCollapsedTracks = m_grid.hasAutoRepeatEmptyTracks(direction);
LayoutUnit gap = !hasCollapsedTracks ? gridGap(direction) : LayoutUnit();
tracks.reserveCapacity(numPositions - 1);
for (size_t i = 0; i < numPositions - 2; ++i)
tracks.append(positions[i + 1] - positions[i] - offsetBetweenTracks - gap);
tracks.append(positions[numPositions - 1] - positions[numPositions - 2]);
if (!hasCollapsedTracks)
return tracks;
size_t remainingEmptyTracks = m_grid.autoRepeatEmptyTracks(direction)->size();
size_t lastLine = tracks.size();
gap = gridGap(direction);
for (size_t i = 1; i < lastLine; ++i) {
if (m_grid.isEmptyAutoRepeatTrack(direction, i - 1))
--remainingEmptyTracks;
else {
// Remove the gap between consecutive non empty tracks. Remove it also just once for an
// arbitrary number of empty tracks between two non empty ones.
bool allRemainingTracksAreEmpty = remainingEmptyTracks == (lastLine - i);
if (!allRemainingTracksAreEmpty || !m_grid.isEmptyAutoRepeatTrack(direction, i))
tracks[i - 1] -= gap;
}
}
return tracks;
}
const StyleContentAlignmentData& RenderGrid::contentAlignmentNormalBehaviorGrid()
{
static const StyleContentAlignmentData normalBehavior = {ContentPositionNormal, ContentDistributionStretch};
return normalBehavior;
}
void RenderGrid::layoutGridItems()
{
populateGridPositionsForDirection(ForColumns);
populateGridPositionsForDirection(ForRows);
for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
if (m_grid.orderIterator().shouldSkipChild(*child)) {
if (child->isOutOfFlowPositioned())
prepareChildForPositionedLayout(*child);
continue;
}
// Because the grid area cannot be styled, we don't need to adjust
// the grid breadth to account for 'box-sizing'.
std::optional<LayoutUnit> oldOverrideContainingBlockContentLogicalWidth = child->hasOverrideContainingBlockLogicalWidth() ? child->overrideContainingBlockContentLogicalWidth() : LayoutUnit();
std::optional<LayoutUnit> oldOverrideContainingBlockContentLogicalHeight = child->hasOverrideContainingBlockLogicalHeight() ? child->overrideContainingBlockContentLogicalHeight() : LayoutUnit();
LayoutUnit overrideContainingBlockContentLogicalWidth = gridAreaBreadthForChildIncludingAlignmentOffsets(*child, ForColumns);
LayoutUnit overrideContainingBlockContentLogicalHeight = gridAreaBreadthForChildIncludingAlignmentOffsets(*child, ForRows);
if (!oldOverrideContainingBlockContentLogicalWidth || oldOverrideContainingBlockContentLogicalWidth.value() != overrideContainingBlockContentLogicalWidth
|| ((!oldOverrideContainingBlockContentLogicalHeight || oldOverrideContainingBlockContentLogicalHeight.value() != overrideContainingBlockContentLogicalHeight)
&& child->hasRelativeLogicalHeight()))
child->setNeedsLayout(MarkOnlyThis);
child->setOverrideContainingBlockContentLogicalWidth(overrideContainingBlockContentLogicalWidth);
child->setOverrideContainingBlockContentLogicalHeight(overrideContainingBlockContentLogicalHeight);
LayoutRect oldChildRect = child->frameRect();
// Stretching logic might force a child layout, so we need to run it before the layoutIfNeeded
// call to avoid unnecessary relayouts. This might imply that child margins, needed to correctly
// determine the available space before stretching, are not set yet.
applyStretchAlignmentToChildIfNeeded(*child);
child->layoutIfNeeded();
// We need pending layouts to be done in order to compute auto-margins properly.
updateAutoMarginsInColumnAxisIfNeeded(*child);
updateAutoMarginsInRowAxisIfNeeded(*child);
child->setLogicalLocation(findChildLogicalPosition(*child));
// If the child moved, we have to repaint it as well as any floating/positioned
// descendants. An exception is if we need a layout. In this case, we know we're going to
// repaint ourselves (and the child) anyway.
if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
child->repaintDuringLayoutIfMoved(oldChildRect);
}
}
void RenderGrid::prepareChildForPositionedLayout(RenderBox& child)
{
ASSERT(child.isOutOfFlowPositioned());
child.containingBlock()->insertPositionedObject(child);
RenderLayer* childLayer = child.layer();
childLayer->setStaticInlinePosition(borderStart());
childLayer->setStaticBlockPosition(borderBefore());
}
void RenderGrid::layoutPositionedObject(RenderBox& child, bool relayoutChildren, bool fixedPositionObjectsOnly)
{
LayoutUnit columnOffset;
LayoutUnit columnBreadth;
offsetAndBreadthForPositionedChild(child, ForColumns, columnOffset, columnBreadth);
LayoutUnit rowOffset;
LayoutUnit rowBreadth;
offsetAndBreadthForPositionedChild(child, ForRows, rowOffset, rowBreadth);
child.setOverrideContainingBlockContentLogicalWidth(columnBreadth);
child.setOverrideContainingBlockContentLogicalHeight(rowBreadth);
// Mark for layout as we're resetting the position before and we relay in generic layout logic
// for positioned items in order to get the offsets properly resolved.
child.setChildNeedsLayout(MarkOnlyThis);
// FIXME: If possible it'd be nice to avoid this layout here when it's not needed.
RenderBlock::layoutPositionedObject(child, relayoutChildren, fixedPositionObjectsOnly);
bool isOrthogonal = isOrthogonalChild(child);
LayoutUnit logicalLeft = child.logicalLeft() + (isOrthogonal ? rowOffset : columnOffset);
LayoutUnit logicalTop = child.logicalTop() + (isOrthogonal ? columnOffset : rowOffset);
child.setLogicalLocation(LayoutPoint(logicalLeft, logicalTop));
}
void RenderGrid::offsetAndBreadthForPositionedChild(const RenderBox& child, GridTrackSizingDirection direction, LayoutUnit& offset, LayoutUnit& breadth)
{
bool isRowAxis = direction == ForColumns;
unsigned autoRepeatCount = m_grid.autoRepeatTracks(direction);
GridSpan positions = GridPositionsResolver::resolveGridPositionsFromStyle(style(), child, direction, autoRepeatCount);
if (positions.isIndefinite()) {
offset = LayoutUnit();
breadth = isRowAxis ? clientLogicalWidth() : clientLogicalHeight();
return;
}
// For positioned items we cannot use GridSpan::translate() because we could end up with negative values, as the positioned items do not create implicit tracks per spec.
int smallestStart = std::abs(m_grid.smallestTrackStart(direction));
int startLine = positions.untranslatedStartLine() + smallestStart;
int endLine = positions.untranslatedEndLine() + smallestStart;
GridPosition startPosition = isRowAxis ? child.style().gridItemColumnStart() : child.style().gridItemRowStart();
GridPosition endPosition = isRowAxis ? child.style().gridItemColumnEnd() : child.style().gridItemRowEnd();
int lastLine = numTracks(direction, m_grid);
bool startIsAuto = startPosition.isAuto()
|| (startPosition.isNamedGridArea() && !NamedLineCollection::isValidNamedLineOrArea(startPosition.namedGridLine(), style(), (direction == ForColumns) ? ColumnStartSide : RowStartSide))
|| (startLine < 0)
|| (startLine > lastLine);
bool endIsAuto = endPosition.isAuto()
|| (endPosition.isNamedGridArea() && !NamedLineCollection::isValidNamedLineOrArea(endPosition.namedGridLine(), style(), (direction == ForColumns) ? ColumnEndSide : RowEndSide))
|| (endLine < 0)
|| (endLine > lastLine);