1/*
2 * Copyright (C) 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2013-2017 Igalia S.L.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "RenderGrid.h"
29
30#include "GridArea.h"
31#include "GridLayoutFunctions.h"
32#include "GridPositionsResolver.h"
33#include "GridTrackSizingAlgorithm.h"
34#include "LayoutRepainter.h"
35#include "RenderChildIterator.h"
36#include "RenderLayer.h"
37#include "RenderLayoutState.h"
38#include "RenderTreeBuilder.h"
39#include "RenderView.h"
40#include <cstdlib>
41#include <wtf/IsoMallocInlines.h>
42
43namespace WebCore {
44
45WTF_MAKE_ISO_ALLOCATED_IMPL(RenderGrid);
46
47enum TrackSizeRestriction {
48 AllowInfinity,
49 ForbidInfinity,
50};
51
52RenderGrid::RenderGrid(Element& element, RenderStyle&& style)
53 : RenderBlock(element, WTFMove(style), 0)
54 , m_grid(*this)
55 , m_trackSizingAlgorithm(this, m_grid)
56{
57 // All of our children must be block level.
58 setChildrenInline(false);
59}
60
61RenderGrid::~RenderGrid() = default;
62
63StyleSelfAlignmentData RenderGrid::selfAlignmentForChild(GridAxis axis, const RenderBox& child, const RenderStyle* gridStyle) const
64{
65 return axis == GridRowAxis ? justifySelfForChild(child, gridStyle) : alignSelfForChild(child, gridStyle);
66}
67
68bool RenderGrid::selfAlignmentChangedToStretch(GridAxis axis, const RenderStyle& oldStyle, const RenderStyle& newStyle, const RenderBox& child) const
69{
70 return selfAlignmentForChild(axis, child, &oldStyle).position() != ItemPosition::Stretch
71 && selfAlignmentForChild(axis, child, &newStyle).position() == ItemPosition::Stretch;
72}
73
74bool RenderGrid::selfAlignmentChangedFromStretch(GridAxis axis, const RenderStyle& oldStyle, const RenderStyle& newStyle, const RenderBox& child) const
75{
76 return selfAlignmentForChild(axis, child, &oldStyle).position() == ItemPosition::Stretch
77 && selfAlignmentForChild(axis, child, &newStyle).position() != ItemPosition::Stretch;
78}
79
80void RenderGrid::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
81{
82 RenderBlock::styleDidChange(diff, oldStyle);
83 if (!oldStyle || diff != StyleDifference::Layout)
84 return;
85
86 const RenderStyle& newStyle = this->style();
87 if (oldStyle->resolvedAlignItems(selfAlignmentNormalBehavior(this)).position() == ItemPosition::Stretch) {
88 // Style changes on the grid container implying stretching (to-stretch) or
89 // shrinking (from-stretch) require the affected items to be laid out again.
90 // These logic only applies to 'stretch' since the rest of the alignment
91 // values don't change the size of the box.
92 // In any case, the items' overrideSize will be cleared and recomputed (if
93 // necessary) as part of the Grid layout logic, triggered by this style
94 // change.
95 for (auto& child : childrenOfType<RenderBox>(*this)) {
96 if (child.isOutOfFlowPositioned())
97 continue;
98 if (selfAlignmentChangedToStretch(GridRowAxis, *oldStyle, newStyle, child)
99 || selfAlignmentChangedFromStretch(GridRowAxis, *oldStyle, newStyle, child)
100 || selfAlignmentChangedToStretch(GridColumnAxis, *oldStyle, newStyle, child)
101 || selfAlignmentChangedFromStretch(GridColumnAxis, *oldStyle, newStyle, child)) {
102 child.setNeedsLayout();
103 }
104 }
105 }
106
107 if (explicitGridDidResize(*oldStyle) || namedGridLinesDefinitionDidChange(*oldStyle) || oldStyle->gridAutoFlow() != style().gridAutoFlow()
108 || (style().gridAutoRepeatColumns().size() || style().gridAutoRepeatRows().size()))
109 dirtyGrid();
110}
111
112bool RenderGrid::explicitGridDidResize(const RenderStyle& oldStyle) const
113{
114 return oldStyle.gridColumns().size() != style().gridColumns().size()
115 || oldStyle.gridRows().size() != style().gridRows().size()
116 || oldStyle.namedGridAreaColumnCount() != style().namedGridAreaColumnCount()
117 || oldStyle.namedGridAreaRowCount() != style().namedGridAreaRowCount()
118 || oldStyle.gridAutoRepeatColumns().size() != style().gridAutoRepeatColumns().size()
119 || oldStyle.gridAutoRepeatRows().size() != style().gridAutoRepeatRows().size();
120}
121
122bool RenderGrid::namedGridLinesDefinitionDidChange(const RenderStyle& oldStyle) const
123{
124 return oldStyle.namedGridRowLines() != style().namedGridRowLines()
125 || oldStyle.namedGridColumnLines() != style().namedGridColumnLines();
126}
127
128// This method optimizes the gutters computation by skiping the available size
129// call if gaps are fixed size (it's only needed for percentages).
130Optional<LayoutUnit> RenderGrid::availableSpaceForGutters(GridTrackSizingDirection direction) const
131{
132 bool isRowAxis = direction == ForColumns;
133 const GapLength& gapLength = isRowAxis ? style().columnGap() : style().rowGap();
134 if (gapLength.isNormal() || !gapLength.length().isPercentOrCalculated())
135 return WTF::nullopt;
136
137 return isRowAxis ? availableLogicalWidth() : contentLogicalHeight();
138}
139
140void RenderGrid::computeTrackSizesForDefiniteSize(GridTrackSizingDirection direction, LayoutUnit availableSpace)
141{
142 LayoutUnit totalGuttersSize = guttersSize(m_grid, direction, 0, m_grid.numTracks(direction), availableSpace);
143 LayoutUnit freeSpace = availableSpace - totalGuttersSize;
144
145 m_trackSizingAlgorithm.setup(direction, numTracks(direction, m_grid), TrackSizing, availableSpace, freeSpace);
146 m_trackSizingAlgorithm.run();
147
148 ASSERT(m_trackSizingAlgorithm.tracksAreWiderThanMinTrackBreadth());
149}
150
151void RenderGrid::repeatTracksSizingIfNeeded(LayoutUnit availableSpaceForColumns, LayoutUnit availableSpaceForRows)
152{
153 // In orthogonal flow cases column track's size is determined by using the computed
154 // row track's size, which it was estimated during the first cycle of the sizing
155 // algorithm. Hence we need to repeat computeUsedBreadthOfGridTracks for both,
156 // columns and rows, to determine the final values.
157 // TODO (lajava): orthogonal flows is just one of the cases which may require
158 // a new cycle of the sizing algorithm; there may be more. In addition, not all the
159 // cases with orthogonal flows require this extra cycle; we need a more specific
160 // condition to detect whether child's min-content contribution has changed or not.
161 if (m_hasAnyOrthogonalItem || m_trackSizingAlgorithm.hasAnyPercentSizedRowsIndefiniteHeight()) {
162 computeTrackSizesForDefiniteSize(ForColumns, availableSpaceForColumns);
163 computeContentPositionAndDistributionOffset(ForColumns, m_trackSizingAlgorithm.freeSpace(ForColumns).value(), nonCollapsedTracks(ForColumns));
164 computeTrackSizesForDefiniteSize(ForRows, availableSpaceForRows);
165 computeContentPositionAndDistributionOffset(ForRows, m_trackSizingAlgorithm.freeSpace(ForRows).value(), nonCollapsedTracks(ForRows));
166 }
167}
168
169bool RenderGrid::canPerformSimplifiedLayout() const
170{
171 // We cannot perform a simplified layout if we need to position the items and we have some
172 // positioned items to be laid out.
173 if (m_grid.needsItemsPlacement() && posChildNeedsLayout())
174 return false;
175
176 return RenderBlock::canPerformSimplifiedLayout();
177}
178
179void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit)
180{
181 ASSERT(needsLayout());
182
183 if (!relayoutChildren && simplifiedLayout())
184 return;
185
186 LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
187 {
188 LayoutStateMaintainer statePusher(*this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
189
190 preparePaginationBeforeBlockLayout(relayoutChildren);
191 beginUpdateScrollInfoAfterLayoutTransaction();
192
193 LayoutSize previousSize = size();
194 // FIXME: We should use RenderBlock::hasDefiniteLogicalHeight() but it does not work for positioned stuff.
195 // FIXME: Consider caching the hasDefiniteLogicalHeight value throughout the layout.
196 bool hasDefiniteLogicalHeight = hasOverrideContentLogicalHeight() || computeContentLogicalHeight(MainOrPreferredSize, style().logicalHeight(), WTF::nullopt);
197
198 m_hasAnyOrthogonalItem = false;
199 for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) {
200 if (child->isOutOfFlowPositioned())
201 continue;
202 // Grid's layout logic controls the grid item's override height, hence we need to
203 // clear any override height set previously, so it doesn't interfere in current layout
204 // execution. Grid never uses the override width, that's why we don't need to clear it.
205 child->clearOverrideContentLogicalHeight();
206
207 // We may need to repeat the track sizing in case of any grid item was orthogonal.
208 if (GridLayoutFunctions::isOrthogonalChild(*this, *child))
209 m_hasAnyOrthogonalItem = true;
210
211 // We keep a cache of items with baseline as alignment values so
212 // that we only compute the baseline shims for such items. This
213 // cache is needed for performance related reasons due to the
214 // cost of evaluating the item's participation in a baseline
215 // context during the track sizing algorithm.
216 if (isBaselineAlignmentForChild(*child, GridColumnAxis))
217 m_trackSizingAlgorithm.cacheBaselineAlignedItem(*child, GridColumnAxis);
218 if (isBaselineAlignmentForChild(*child, GridRowAxis))
219 m_trackSizingAlgorithm.cacheBaselineAlignedItem(*child, GridRowAxis);
220 }
221 m_baselineItemsCached = true;
222 setLogicalHeight(0);
223 updateLogicalWidth();
224
225 // Fieldsets need to find their legend and position it inside the border of the object.
226 // The legend then gets skipped during normal layout. The same is true for ruby text.
227 // It doesn't get included in the normal layout process but is instead skipped.
228 layoutExcludedChildren(relayoutChildren);
229
230 LayoutUnit availableSpaceForColumns = availableLogicalWidth();
231 placeItemsOnGrid(m_trackSizingAlgorithm, availableSpaceForColumns);
232
233 performGridItemsPreLayout(m_trackSizingAlgorithm);
234
235 // 1- First, the track sizing algorithm is used to resolve the sizes of the
236 // grid columns.
237 // At this point the logical width is always definite as the above call to
238 // updateLogicalWidth() properly resolves intrinsic sizes. We cannot do the
239 // same for heights though because many code paths inside
240 // updateLogicalHeight() require a previous call to setLogicalHeight() to
241 // resolve heights properly (like for positioned items for example).
242 computeTrackSizesForDefiniteSize(ForColumns, availableSpaceForColumns);
243
244 // 1.5- Compute Content Distribution offsets for column tracks
245 computeContentPositionAndDistributionOffset(ForColumns, m_trackSizingAlgorithm.freeSpace(ForColumns).value(), nonCollapsedTracks(ForColumns));
246
247 // 2- Next, the track sizing algorithm resolves the sizes of the grid rows,
248 // using the grid column sizes calculated in the previous step.
249 if (!hasDefiniteLogicalHeight)
250 computeTrackSizesForIndefiniteSize(m_trackSizingAlgorithm, ForRows);
251 else
252 computeTrackSizesForDefiniteSize(ForRows, availableLogicalHeight(ExcludeMarginBorderPadding));
253 LayoutUnit trackBasedLogicalHeight = m_trackSizingAlgorithm.computeTrackBasedSize() + borderAndPaddingLogicalHeight() + scrollbarLogicalHeight();
254 setLogicalHeight(trackBasedLogicalHeight);
255
256 LayoutUnit oldClientAfterEdge = clientLogicalBottom();
257 updateLogicalHeight();
258
259 // Once grid's indefinite height is resolved, we can compute the
260 // available free space for Content Alignment.
261 if (!hasDefiniteLogicalHeight)
262 m_trackSizingAlgorithm.setFreeSpace(ForRows, logicalHeight() - trackBasedLogicalHeight);
263
264 // 2.5- Compute Content Distribution offsets for rows tracks
265 computeContentPositionAndDistributionOffset(ForRows, m_trackSizingAlgorithm.freeSpace(ForRows).value(), nonCollapsedTracks(ForRows));
266
267 // 3- If the min-content contribution of any grid items have changed based on the row
268 // sizes calculated in step 2, steps 1 and 2 are repeated with the new min-content
269 // contribution (once only).
270 repeatTracksSizingIfNeeded(availableSpaceForColumns, contentLogicalHeight());
271
272 // Grid container should have the minimum height of a line if it's editable. That does not affect track sizing though.
273 if (hasLineIfEmpty()) {
274 LayoutUnit minHeightForEmptyLine = borderAndPaddingLogicalHeight()
275 + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)
276 + scrollbarLogicalHeight();
277 setLogicalHeight(std::max(logicalHeight(), minHeightForEmptyLine));
278 }
279
280 layoutGridItems();
281 m_trackSizingAlgorithm.reset();
282
283 endAndCommitUpdateScrollInfoAfterLayoutTransaction();
284
285 if (size() != previousSize)
286 relayoutChildren = true;
287
288 m_outOfFlowItemColumn.clear();
289 m_outOfFlowItemRow.clear();
290
291 layoutPositionedObjects(relayoutChildren || isDocumentElementRenderer());
292
293 computeOverflow(oldClientAfterEdge);
294 }
295
296 updateLayerTransform();
297
298 // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
299 // we overflow or not.
300 updateScrollInfoAfterLayout();
301
302 repainter.repaintAfterLayout();
303
304 clearNeedsLayout();
305
306 m_trackSizingAlgorithm.clearBaselineItemsCache();
307 m_baselineItemsCached = false;
308}
309
310LayoutUnit RenderGrid::gridGap(GridTrackSizingDirection direction, Optional<LayoutUnit> availableSize) const
311{
312 const GapLength& gapLength = direction == ForColumns? style().columnGap() : style().rowGap();
313 if (gapLength.isNormal())
314 return 0_lu;
315
316 return valueForLength(gapLength.length(), availableSize.valueOr(0));
317}
318
319LayoutUnit RenderGrid::gridGap(GridTrackSizingDirection direction) const
320{
321 return gridGap(direction, availableSpaceForGutters(direction));
322}
323
324LayoutUnit RenderGrid::gridItemOffset(GridTrackSizingDirection direction) const
325{
326 return direction == ForRows ? m_offsetBetweenRows.distributionOffset : m_offsetBetweenColumns.distributionOffset;
327}
328
329LayoutUnit RenderGrid::guttersSize(const Grid& grid, GridTrackSizingDirection direction, unsigned startLine, unsigned span, Optional<LayoutUnit> availableSize) const
330{
331 if (span <= 1)
332 return { };
333
334 LayoutUnit gap = gridGap(direction, availableSize);
335
336 // Fast path, no collapsing tracks.
337 if (!grid.hasAutoRepeatEmptyTracks(direction))
338 return gap * (span - 1);
339
340 // If there are collapsing tracks we need to be sure that gutters are properly collapsed. Apart
341 // from that, if we have a collapsed track in the edges of the span we're considering, we need
342 // to move forward (or backwards) in order to know whether the collapsed tracks reach the end of
343 // the grid (so the gap becomes 0) or there is a non empty track before that.
344
345 LayoutUnit gapAccumulator;
346 unsigned endLine = startLine + span;
347
348 for (unsigned line = startLine; line < endLine - 1; ++line) {
349 if (!grid.isEmptyAutoRepeatTrack(direction, line))
350 gapAccumulator += gap;
351 }
352
353 // The above loop adds one extra gap for trailing collapsed tracks.
354 if (gapAccumulator && grid.isEmptyAutoRepeatTrack(direction, endLine - 1)) {
355 ASSERT(gapAccumulator >= gap);
356 gapAccumulator -= gap;
357 }
358
359 // If the startLine is the start line of a collapsed track we need to go backwards till we reach
360 // a non collapsed track. If we find a non collapsed track we need to add that gap.
361 size_t nonEmptyTracksBeforeStartLine = 0;
362 if (startLine && grid.isEmptyAutoRepeatTrack(direction, startLine)) {
363 nonEmptyTracksBeforeStartLine = startLine;
364 auto begin = grid.autoRepeatEmptyTracks(direction)->begin();
365 for (auto it = begin; *it != startLine; ++it) {
366 ASSERT(nonEmptyTracksBeforeStartLine);
367 --nonEmptyTracksBeforeStartLine;
368 }
369 if (nonEmptyTracksBeforeStartLine)
370 gapAccumulator += gap;
371 }
372
373 // If the endLine is the end line of a collapsed track we need to go forward till we reach a non
374 // collapsed track. If we find a non collapsed track we need to add that gap.
375 if (grid.isEmptyAutoRepeatTrack(direction, endLine - 1)) {
376 unsigned nonEmptyTracksAfterEndLine = grid.numTracks(direction) - endLine;
377 auto currentEmptyTrack = grid.autoRepeatEmptyTracks(direction)->find(endLine - 1);
378 auto endEmptyTrack = grid.autoRepeatEmptyTracks(direction)->end();
379 // HashSet iterators do not implement operator- so we have to manually iterate to know the number of remaining empty tracks.
380 for (auto it = ++currentEmptyTrack; it != endEmptyTrack; ++it) {
381 ASSERT(nonEmptyTracksAfterEndLine >= 1);
382 --nonEmptyTracksAfterEndLine;
383 }
384 if (nonEmptyTracksAfterEndLine) {
385 // We shouldn't count the gap twice if the span starts and ends in a collapsed track bewtween two non-empty tracks.
386 if (!nonEmptyTracksBeforeStartLine)
387 gapAccumulator += gap;
388 } else if (nonEmptyTracksBeforeStartLine) {
389 // We shouldn't count the gap if the the span starts and ends in a collapsed but there isn't non-empty tracks afterwards (it's at the end of the grid).
390 gapAccumulator -= gap;
391 }
392 }
393
394 return gapAccumulator;
395}
396
397void RenderGrid::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
398{
399 LayoutUnit childMinWidth;
400 LayoutUnit childMaxWidth;
401 bool hadExcludedChildren = computePreferredWidthsForExcludedChildren(childMinWidth, childMaxWidth);
402
403 Grid grid(const_cast<RenderGrid&>(*this));
404 GridTrackSizingAlgorithm algorithm(this, grid);
405 placeItemsOnGrid(algorithm, WTF::nullopt);
406
407 performGridItemsPreLayout(algorithm);
408
409 if (m_baselineItemsCached)
410 algorithm.copyBaselineItemsCache(m_trackSizingAlgorithm, GridRowAxis);
411 else {
412 for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) {
413 if (child->isOutOfFlowPositioned())
414 continue;
415 if (isBaselineAlignmentForChild(*child, GridRowAxis))
416 algorithm.cacheBaselineAlignedItem(*child, GridRowAxis);
417 }
418 }
419
420 computeTrackSizesForIndefiniteSize(algorithm, ForColumns, &minLogicalWidth, &maxLogicalWidth);
421
422 if (hadExcludedChildren) {
423 minLogicalWidth = std::max(minLogicalWidth, childMinWidth);
424 maxLogicalWidth = std::max(maxLogicalWidth, childMaxWidth);
425 }
426
427 LayoutUnit scrollbarWidth = intrinsicScrollbarLogicalWidth();
428 minLogicalWidth += scrollbarWidth;
429 maxLogicalWidth += scrollbarWidth;
430}
431
432void RenderGrid::computeTrackSizesForIndefiniteSize(GridTrackSizingAlgorithm& algorithm, GridTrackSizingDirection direction, LayoutUnit* minIntrinsicSize, LayoutUnit* maxIntrinsicSize) const
433{
434 const Grid& grid = algorithm.grid();
435 algorithm.setup(direction, numTracks(direction, grid), IntrinsicSizeComputation, WTF::nullopt, WTF::nullopt);
436 algorithm.run();
437
438 size_t numberOfTracks = algorithm.tracks(direction).size();
439 LayoutUnit totalGuttersSize = guttersSize(grid, direction, 0, numberOfTracks, WTF::nullopt);
440
441 if (minIntrinsicSize)
442 *minIntrinsicSize = algorithm.minContentSize() + totalGuttersSize;
443 if (maxIntrinsicSize)
444 *maxIntrinsicSize = algorithm.maxContentSize() + totalGuttersSize;
445
446 ASSERT(algorithm.tracksAreWiderThanMinTrackBreadth());
447}
448
449unsigned RenderGrid::computeAutoRepeatTracksCount(GridTrackSizingDirection direction, Optional<LayoutUnit> availableSize) const
450{
451 ASSERT(!availableSize || availableSize.value() != -1);
452 bool isRowAxis = direction == ForColumns;
453 const auto& autoRepeatTracks = isRowAxis ? style().gridAutoRepeatColumns() : style().gridAutoRepeatRows();
454 unsigned autoRepeatTrackListLength = autoRepeatTracks.size();
455
456 if (!autoRepeatTrackListLength)
457 return 0;
458
459 bool needsToFulfillMinimumSize = false;
460 if (!availableSize) {
461 const Length& maxSize = isRowAxis ? style().logicalMaxWidth() : style().logicalMaxHeight();
462 Optional<LayoutUnit> containingBlockAvailableSize;
463 Optional<LayoutUnit> availableMaxSize;
464 if (maxSize.isSpecified()) {
465 if (maxSize.isPercentOrCalculated())
466 containingBlockAvailableSize = isRowAxis ? containingBlockLogicalWidthForContent() : containingBlockLogicalHeightForContent(ExcludeMarginBorderPadding);
467 LayoutUnit maxSizeValue = valueForLength(maxSize, containingBlockAvailableSize.valueOr(LayoutUnit()));
468 availableMaxSize = isRowAxis ? adjustContentBoxLogicalWidthForBoxSizing(maxSizeValue) : adjustContentBoxLogicalHeightForBoxSizing(maxSizeValue);
469 }
470
471 const Length& minSize = isRowAxis ? style().logicalMinWidth() : style().logicalMinHeight();
472 if (!availableMaxSize && !minSize.isSpecified())
473 return autoRepeatTrackListLength;
474
475 Optional<LayoutUnit> availableMinSize;
476 if (minSize.isSpecified()) {
477 if (!containingBlockAvailableSize && minSize.isPercentOrCalculated())
478 containingBlockAvailableSize = isRowAxis ? containingBlockLogicalWidthForContent() : containingBlockLogicalHeightForContent(ExcludeMarginBorderPadding);
479 LayoutUnit minSizeValue = valueForLength(minSize, containingBlockAvailableSize.valueOr(LayoutUnit()));
480 availableMinSize = isRowAxis ? adjustContentBoxLogicalWidthForBoxSizing(minSizeValue) : adjustContentBoxLogicalHeightForBoxSizing(minSizeValue);
481 if (!maxSize.isSpecified())
482 needsToFulfillMinimumSize = true;
483 }
484
485 availableSize = std::max(availableMinSize.valueOr(LayoutUnit()), availableMaxSize.valueOr(LayoutUnit()));
486 }
487
488 LayoutUnit autoRepeatTracksSize;
489 for (auto& autoTrackSize : autoRepeatTracks) {
490 ASSERT(autoTrackSize.minTrackBreadth().isLength());
491 ASSERT(!autoTrackSize.minTrackBreadth().isFlex());
492 bool hasDefiniteMaxTrackSizingFunction = autoTrackSize.maxTrackBreadth().isLength() && !autoTrackSize.maxTrackBreadth().isContentSized();
493 auto trackLength = hasDefiniteMaxTrackSizingFunction ? autoTrackSize.maxTrackBreadth().length() : autoTrackSize.minTrackBreadth().length();
494 autoRepeatTracksSize += valueForLength(trackLength, availableSize.value());
495 }
496 // For the purpose of finding the number of auto-repeated tracks, the UA must floor the track size to a UA-specified
497 // value to avoid division by zero. It is suggested that this floor be 1px.
498 autoRepeatTracksSize = std::max<LayoutUnit>(1_lu, autoRepeatTracksSize);
499
500 // There will be always at least 1 auto-repeat track, so take it already into account when computing the total track size.
501 LayoutUnit tracksSize = autoRepeatTracksSize;
502 auto& trackSizes = isRowAxis ? style().gridColumns() : style().gridRows();
503
504 for (const auto& track : trackSizes) {
505 bool hasDefiniteMaxTrackBreadth = track.maxTrackBreadth().isLength() && !track.maxTrackBreadth().isContentSized();
506 ASSERT(hasDefiniteMaxTrackBreadth || (track.minTrackBreadth().isLength() && !track.minTrackBreadth().isContentSized()));
507 tracksSize += valueForLength(hasDefiniteMaxTrackBreadth ? track.maxTrackBreadth().length() : track.minTrackBreadth().length(), availableSize.value());
508 }
509
510 // Add gutters as if there where only 1 auto repeat track. Gaps between auto repeat tracks will be added later when
511 // computing the repetitions.
512 LayoutUnit gapSize = gridGap(direction, availableSize);
513 tracksSize += gapSize * trackSizes.size();
514
515 LayoutUnit freeSpace = availableSize.value() - tracksSize;
516 if (freeSpace <= 0)
517 return autoRepeatTrackListLength;
518
519 LayoutUnit autoRepeatSizeWithGap = autoRepeatTracksSize + gapSize;
520 unsigned repetitions = 1 + (freeSpace / autoRepeatSizeWithGap).toUnsigned();
521 freeSpace -= autoRepeatSizeWithGap * (repetitions - 1);
522 ASSERT(freeSpace >= 0);
523
524 // Provided the grid container does not have a definite size or max-size in the relevant axis,
525 // if the min size is definite then the number of repetitions is the largest possible positive
526 // integer that fulfills that minimum requirement.
527 if (needsToFulfillMinimumSize && freeSpace)
528 ++repetitions;
529
530 return repetitions * autoRepeatTrackListLength;
531}
532
533
534std::unique_ptr<OrderedTrackIndexSet> RenderGrid::computeEmptyTracksForAutoRepeat(Grid& grid, GridTrackSizingDirection direction) const
535{
536 bool isRowAxis = direction == ForColumns;
537 if ((isRowAxis && style().gridAutoRepeatColumnsType() != AutoRepeatType::Fit)
538 || (!isRowAxis && style().gridAutoRepeatRowsType() != AutoRepeatType::Fit))
539 return nullptr;
540
541 std::unique_ptr<OrderedTrackIndexSet> emptyTrackIndexes;
542 unsigned insertionPoint = isRowAxis ? style().gridAutoRepeatColumnsInsertionPoint() : style().gridAutoRepeatRowsInsertionPoint();
543 unsigned firstAutoRepeatTrack = insertionPoint + std::abs(grid.smallestTrackStart(direction));
544 unsigned lastAutoRepeatTrack = firstAutoRepeatTrack + grid.autoRepeatTracks(direction);
545
546 if (!grid.hasGridItems()) {
547 emptyTrackIndexes = std::make_unique<OrderedTrackIndexSet>();
548 for (unsigned trackIndex = firstAutoRepeatTrack; trackIndex < lastAutoRepeatTrack; ++trackIndex)
549 emptyTrackIndexes->add(trackIndex);
550 } else {
551 for (unsigned trackIndex = firstAutoRepeatTrack; trackIndex < lastAutoRepeatTrack; ++trackIndex) {
552 GridIterator iterator(grid, direction, trackIndex);
553 if (!iterator.nextGridItem()) {
554 if (!emptyTrackIndexes)
555 emptyTrackIndexes = std::make_unique<OrderedTrackIndexSet>();
556 emptyTrackIndexes->add(trackIndex);
557 }
558 }
559 }
560 return emptyTrackIndexes;
561}
562
563unsigned RenderGrid::clampAutoRepeatTracks(GridTrackSizingDirection direction, unsigned autoRepeatTracks) const
564{
565 if (!autoRepeatTracks)
566 return 0;
567
568 unsigned insertionPoint = direction == ForColumns ? style().gridAutoRepeatColumnsInsertionPoint() : style().gridAutoRepeatRowsInsertionPoint();
569 unsigned maxTracks = static_cast<unsigned>(GridPosition::max());
570
571 if (!insertionPoint)
572 return std::min(autoRepeatTracks, maxTracks);
573
574 if (insertionPoint >= maxTracks)
575 return 0;
576
577 return std::min(autoRepeatTracks, maxTracks - insertionPoint);
578}
579
580// FIXME: We shouldn't have to pass the available logical width as argument. The problem is that
581// availableLogicalWidth() does always return a value even if we cannot resolve it like when
582// computing the intrinsic size (preferred widths). That's why we pass the responsibility to the
583// caller who does know whether the available logical width is indefinite or not.
584void RenderGrid::placeItemsOnGrid(GridTrackSizingAlgorithm& algorithm, Optional<LayoutUnit> availableLogicalWidth) const
585{
586 Grid& grid = algorithm.mutableGrid();
587 unsigned autoRepeatColumns = computeAutoRepeatTracksCount(ForColumns, availableLogicalWidth);
588 unsigned autoRepeatRows = computeAutoRepeatTracksCount(ForRows, availableLogicalHeightForPercentageComputation());
589
590 autoRepeatRows = clampAutoRepeatTracks(ForRows, autoRepeatRows);
591 autoRepeatColumns = clampAutoRepeatTracks(ForColumns, autoRepeatColumns);
592
593 if (autoRepeatColumns != grid.autoRepeatTracks(ForColumns) || autoRepeatRows != grid.autoRepeatTracks(ForRows)) {
594 grid.setNeedsItemsPlacement(true);
595 grid.setAutoRepeatTracks(autoRepeatRows, autoRepeatColumns);
596 }
597
598 if (!grid.needsItemsPlacement())
599 return;
600
601 ASSERT(!grid.hasGridItems());
602 populateExplicitGridAndOrderIterator(grid);
603
604 Vector<RenderBox*> autoMajorAxisAutoGridItems;
605 Vector<RenderBox*> specifiedMajorAxisAutoGridItems;
606 for (auto* child = grid.orderIterator().first(); child; child = grid.orderIterator().next()) {
607 if (grid.orderIterator().shouldSkipChild(*child))
608 continue;
609
610 // Grid items should use the grid area sizes instead of the containing block (grid container)
611 // sizes, we initialize the overrides here if needed to ensure it.
612 if (!child->hasOverrideContainingBlockContentLogicalWidth())
613 child->setOverrideContainingBlockContentLogicalWidth(LayoutUnit());
614 if (!child->hasOverrideContainingBlockContentLogicalHeight())
615 child->setOverrideContainingBlockContentLogicalHeight(LayoutUnit(-1));
616
617 GridArea area = grid.gridItemArea(*child);
618 if (!area.rows.isIndefinite())
619 area.rows.translate(std::abs(grid.smallestTrackStart(ForRows)));
620 if (!area.columns.isIndefinite())
621 area.columns.translate(std::abs(grid.smallestTrackStart(ForColumns)));
622
623 if (area.rows.isIndefinite() || area.columns.isIndefinite()) {
624 grid.setGridItemArea(*child, area);
625 bool majorAxisDirectionIsForColumns = autoPlacementMajorAxisDirection() == ForColumns;
626 if ((majorAxisDirectionIsForColumns && area.columns.isIndefinite())
627 || (!majorAxisDirectionIsForColumns && area.rows.isIndefinite()))
628 autoMajorAxisAutoGridItems.append(child);
629 else
630 specifiedMajorAxisAutoGridItems.append(child);
631 continue;
632 }
633 grid.insert(*child, { area.rows, area.columns });
634 }
635
636#if !ASSERT_DISABLED
637 if (grid.hasGridItems()) {
638 ASSERT(grid.numTracks(ForRows) >= GridPositionsResolver::explicitGridRowCount(style(), grid.autoRepeatTracks(ForRows)));
639 ASSERT(grid.numTracks(ForColumns) >= GridPositionsResolver::explicitGridColumnCount(style(), grid.autoRepeatTracks(ForColumns)));
640 }
641#endif
642
643 placeSpecifiedMajorAxisItemsOnGrid(grid, specifiedMajorAxisAutoGridItems);
644 placeAutoMajorAxisItemsOnGrid(grid, autoMajorAxisAutoGridItems);
645
646 // Compute collapsible tracks for auto-fit.
647 grid.setAutoRepeatEmptyColumns(computeEmptyTracksForAutoRepeat(grid, ForColumns));
648 grid.setAutoRepeatEmptyRows(computeEmptyTracksForAutoRepeat(grid, ForRows));
649
650 grid.setNeedsItemsPlacement(false);
651
652#if !ASSERT_DISABLED
653 for (auto* child = grid.orderIterator().first(); child; child = grid.orderIterator().next()) {
654 if (grid.orderIterator().shouldSkipChild(*child))
655 continue;
656
657 GridArea area = grid.gridItemArea(*child);
658 ASSERT(area.rows.isTranslatedDefinite() && area.columns.isTranslatedDefinite());
659 }
660#endif
661}
662
663void RenderGrid::performGridItemsPreLayout(const GridTrackSizingAlgorithm& algorithm) const
664{
665 ASSERT(!algorithm.grid().needsItemsPlacement());
666 // FIXME: We need a way when we are calling this during intrinsic size compuation before performing
667 // the layout. Maybe using the PreLayout phase ?
668 for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) {
669 if (child->isOutOfFlowPositioned())
670 continue;
671 // Orthogonal items should be laid out in order to properly compute content-sized tracks that may depend on item's intrinsic size.
672 // We also need to properly estimate its grid area size, since it may affect to the baseline shims if such item particiaptes in baseline alignment.
673 if (GridLayoutFunctions::isOrthogonalChild(*this, *child)) {
674 updateGridAreaLogicalSize(*child, algorithm.estimatedGridAreaBreadthForChild(*child));
675 child->layoutIfNeeded();
676 continue;
677 }
678 // We need to layout the item to know whether it must synthesize its
679 // baseline or not, which may imply a cyclic sizing dependency.
680 // FIXME: Can we avoid it ?
681 if (isBaselineAlignmentForChild(*child)) {
682 updateGridAreaLogicalSize(*child, algorithm.estimatedGridAreaBreadthForChild(*child));
683 child->layoutIfNeeded();
684 }
685 }
686}
687
688void RenderGrid::populateExplicitGridAndOrderIterator(Grid& grid) const
689{
690 OrderIteratorPopulator populator(grid.orderIterator());
691 int smallestRowStart = 0;
692 int smallestColumnStart = 0;
693 unsigned autoRepeatRows = grid.autoRepeatTracks(ForRows);
694 unsigned autoRepeatColumns = grid.autoRepeatTracks(ForColumns);
695 unsigned maximumRowIndex = GridPositionsResolver::explicitGridRowCount(style(), autoRepeatRows);
696 unsigned maximumColumnIndex = GridPositionsResolver::explicitGridColumnCount(style(), autoRepeatColumns);
697
698 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
699 if (!populator.collectChild(*child))
700 continue;
701
702 GridSpan rowPositions = GridPositionsResolver::resolveGridPositionsFromStyle(style(), *child, ForRows, autoRepeatRows);
703 if (!rowPositions.isIndefinite()) {
704 smallestRowStart = std::min(smallestRowStart, rowPositions.untranslatedStartLine());
705 maximumRowIndex = std::max<int>(maximumRowIndex, rowPositions.untranslatedEndLine());
706 } else {
707 // Grow the grid for items with a definite row span, getting the largest such span.
708 unsigned spanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(*child, ForRows);
709 maximumRowIndex = std::max(maximumRowIndex, spanSize);
710 }
711
712 GridSpan columnPositions = GridPositionsResolver::resolveGridPositionsFromStyle(style(), *child, ForColumns, autoRepeatColumns);
713 if (!columnPositions.isIndefinite()) {
714 smallestColumnStart = std::min(smallestColumnStart, columnPositions.untranslatedStartLine());
715 maximumColumnIndex = std::max<int>(maximumColumnIndex, columnPositions.untranslatedEndLine());
716 } else {
717 // Grow the grid for items with a definite column span, getting the largest such span.
718 unsigned spanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(*child, ForColumns);
719 maximumColumnIndex = std::max(maximumColumnIndex, spanSize);
720 }
721
722 grid.setGridItemArea(*child, { rowPositions, columnPositions });
723 }
724
725 grid.setSmallestTracksStart(smallestRowStart, smallestColumnStart);
726 grid.ensureGridSize(maximumRowIndex + std::abs(smallestRowStart), maximumColumnIndex + std::abs(smallestColumnStart));
727}
728
729std::unique_ptr<GridArea> RenderGrid::createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(Grid& grid, const RenderBox& gridItem, GridTrackSizingDirection specifiedDirection, const GridSpan& specifiedPositions) const
730{
731 GridTrackSizingDirection crossDirection = specifiedDirection == ForColumns ? ForRows : ForColumns;
732 const unsigned endOfCrossDirection = grid.numTracks(crossDirection);
733 unsigned crossDirectionSpanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(gridItem, crossDirection);
734 GridSpan crossDirectionPositions = GridSpan::translatedDefiniteGridSpan(endOfCrossDirection, endOfCrossDirection + crossDirectionSpanSize);
735 return std::make_unique<GridArea>(specifiedDirection == ForColumns ? crossDirectionPositions : specifiedPositions, specifiedDirection == ForColumns ? specifiedPositions : crossDirectionPositions);
736}
737
738void RenderGrid::placeSpecifiedMajorAxisItemsOnGrid(Grid& grid, const Vector<RenderBox*>& autoGridItems) const
739{
740 bool isForColumns = autoPlacementMajorAxisDirection() == ForColumns;
741 bool isGridAutoFlowDense = style().isGridAutoFlowAlgorithmDense();
742
743 // Mapping between the major axis tracks (rows or columns) and the last auto-placed item's position inserted on
744 // that track. This is needed to implement "sparse" packing for items locked to a given track.
745 // See http://dev.w3.org/csswg/css-grid/#auto-placement-algorithm
746 HashMap<unsigned, unsigned, DefaultHash<unsigned>::Hash, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> minorAxisCursors;
747
748 for (auto& autoGridItem : autoGridItems) {
749 GridSpan majorAxisPositions = grid.gridItemSpan(*autoGridItem, autoPlacementMajorAxisDirection());
750 ASSERT(majorAxisPositions.isTranslatedDefinite());
751 ASSERT(grid.gridItemSpan(*autoGridItem, autoPlacementMinorAxisDirection()).isIndefinite());
752 unsigned minorAxisSpanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(*autoGridItem, autoPlacementMinorAxisDirection());
753 unsigned majorAxisInitialPosition = majorAxisPositions.startLine();
754
755 GridIterator iterator(grid, autoPlacementMajorAxisDirection(), majorAxisPositions.startLine(), isGridAutoFlowDense ? 0 : minorAxisCursors.get(majorAxisInitialPosition));
756 std::unique_ptr<GridArea> emptyGridArea = iterator.nextEmptyGridArea(majorAxisPositions.integerSpan(), minorAxisSpanSize);
757 if (!emptyGridArea)
758 emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(grid, *autoGridItem, autoPlacementMajorAxisDirection(), majorAxisPositions);
759
760 grid.insert(*autoGridItem, *emptyGridArea);
761
762 if (!isGridAutoFlowDense)
763 minorAxisCursors.set(majorAxisInitialPosition, isForColumns ? emptyGridArea->rows.startLine() : emptyGridArea->columns.startLine());
764 }
765}
766
767void RenderGrid::placeAutoMajorAxisItemsOnGrid(Grid& grid, const Vector<RenderBox*>& autoGridItems) const
768{
769 AutoPlacementCursor autoPlacementCursor = {0, 0};
770 bool isGridAutoFlowDense = style().isGridAutoFlowAlgorithmDense();
771
772 for (auto& autoGridItem : autoGridItems) {
773 placeAutoMajorAxisItemOnGrid(grid, *autoGridItem, autoPlacementCursor);
774
775 if (isGridAutoFlowDense) {
776 autoPlacementCursor.first = 0;
777 autoPlacementCursor.second = 0;
778 }
779 }
780}
781
782void RenderGrid::placeAutoMajorAxisItemOnGrid(Grid& grid, RenderBox& gridItem, AutoPlacementCursor& autoPlacementCursor) const
783{
784 ASSERT(grid.gridItemSpan(gridItem, autoPlacementMajorAxisDirection()).isIndefinite());
785 unsigned majorAxisSpanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(gridItem, autoPlacementMajorAxisDirection());
786
787 const unsigned endOfMajorAxis = grid.numTracks(autoPlacementMajorAxisDirection());
788 unsigned majorAxisAutoPlacementCursor = autoPlacementMajorAxisDirection() == ForColumns ? autoPlacementCursor.second : autoPlacementCursor.first;
789 unsigned minorAxisAutoPlacementCursor = autoPlacementMajorAxisDirection() == ForColumns ? autoPlacementCursor.first : autoPlacementCursor.second;
790
791 std::unique_ptr<GridArea> emptyGridArea;
792 GridSpan minorAxisPositions = grid.gridItemSpan(gridItem, autoPlacementMinorAxisDirection());
793 if (minorAxisPositions.isTranslatedDefinite()) {
794 // Move to the next track in major axis if initial position in minor axis is before auto-placement cursor.
795 if (minorAxisPositions.startLine() < minorAxisAutoPlacementCursor)
796 majorAxisAutoPlacementCursor++;
797
798 if (majorAxisAutoPlacementCursor < endOfMajorAxis) {
799 GridIterator iterator(grid, autoPlacementMinorAxisDirection(), minorAxisPositions.startLine(), majorAxisAutoPlacementCursor);
800 emptyGridArea = iterator.nextEmptyGridArea(minorAxisPositions.integerSpan(), majorAxisSpanSize);
801 }
802
803 if (!emptyGridArea)
804 emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(grid, gridItem, autoPlacementMinorAxisDirection(), minorAxisPositions);
805 } else {
806 unsigned minorAxisSpanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(gridItem, autoPlacementMinorAxisDirection());
807
808 for (unsigned majorAxisIndex = majorAxisAutoPlacementCursor; majorAxisIndex < endOfMajorAxis; ++majorAxisIndex) {
809 GridIterator iterator(grid, autoPlacementMajorAxisDirection(), majorAxisIndex, minorAxisAutoPlacementCursor);
810 emptyGridArea = iterator.nextEmptyGridArea(majorAxisSpanSize, minorAxisSpanSize);
811
812 if (emptyGridArea) {
813 // Check that it fits in the minor axis direction, as we shouldn't grow in that direction here (it was already managed in populateExplicitGridAndOrderIterator()).
814 unsigned minorAxisFinalPositionIndex = autoPlacementMinorAxisDirection() == ForColumns ? emptyGridArea->columns.endLine() : emptyGridArea->rows.endLine();
815 const unsigned endOfMinorAxis = grid.numTracks(autoPlacementMinorAxisDirection());
816 if (minorAxisFinalPositionIndex <= endOfMinorAxis)
817 break;
818
819 // Discard empty grid area as it does not fit in the minor axis direction.
820 // We don't need to create a new empty grid area yet as we might find a valid one in the next iteration.
821 emptyGridArea = nullptr;
822 }
823
824 // As we're moving to the next track in the major axis we should reset the auto-placement cursor in the minor axis.
825 minorAxisAutoPlacementCursor = 0;
826 }
827
828 if (!emptyGridArea)
829 emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(grid, gridItem, autoPlacementMinorAxisDirection(), GridSpan::translatedDefiniteGridSpan(0, minorAxisSpanSize));
830 }
831
832 grid.insert(gridItem, *emptyGridArea);
833 autoPlacementCursor.first = emptyGridArea->rows.startLine();
834 autoPlacementCursor.second = emptyGridArea->columns.startLine();
835}
836
837GridTrackSizingDirection RenderGrid::autoPlacementMajorAxisDirection() const
838{
839 return style().isGridAutoFlowDirectionColumn() ? ForColumns : ForRows;
840}
841
842GridTrackSizingDirection RenderGrid::autoPlacementMinorAxisDirection() const
843{
844 return style().isGridAutoFlowDirectionColumn() ? ForRows : ForColumns;
845}
846
847void RenderGrid::dirtyGrid()
848{
849 if (m_grid.needsItemsPlacement())
850 return;
851
852 m_grid.setNeedsItemsPlacement(true);
853}
854
855Vector<LayoutUnit> RenderGrid::trackSizesForComputedStyle(GridTrackSizingDirection direction) const
856{
857 bool isRowAxis = direction == ForColumns;
858 auto& positions = isRowAxis ? m_columnPositions : m_rowPositions;
859 size_t numPositions = positions.size();
860 LayoutUnit offsetBetweenTracks = isRowAxis ? m_offsetBetweenColumns.distributionOffset : m_offsetBetweenRows.distributionOffset;
861
862 Vector<LayoutUnit> tracks;
863 if (numPositions < 2)
864 return tracks;
865
866 ASSERT(!m_grid.needsItemsPlacement());
867 bool hasCollapsedTracks = m_grid.hasAutoRepeatEmptyTracks(direction);
868 LayoutUnit gap = !hasCollapsedTracks ? gridGap(direction) : 0_lu;
869 tracks.reserveCapacity(numPositions - 1);
870 for (size_t i = 0; i < numPositions - 2; ++i)
871 tracks.append(positions[i + 1] - positions[i] - offsetBetweenTracks - gap);
872 tracks.append(positions[numPositions - 1] - positions[numPositions - 2]);
873
874 if (!hasCollapsedTracks)
875 return tracks;
876
877 size_t remainingEmptyTracks = m_grid.autoRepeatEmptyTracks(direction)->size();
878 size_t lastLine = tracks.size();
879 gap = gridGap(direction);
880 for (size_t i = 1; i < lastLine; ++i) {
881 if (m_grid.isEmptyAutoRepeatTrack(direction, i - 1))
882 --remainingEmptyTracks;
883 else {
884 // Remove the gap between consecutive non empty tracks. Remove it also just once for an
885 // arbitrary number of empty tracks between two non empty ones.
886 bool allRemainingTracksAreEmpty = remainingEmptyTracks == (lastLine - i);
887 if (!allRemainingTracksAreEmpty || !m_grid.isEmptyAutoRepeatTrack(direction, i))
888 tracks[i - 1] -= gap;
889 }
890 }
891
892 return tracks;
893}
894
895static const StyleContentAlignmentData& contentAlignmentNormalBehaviorGrid()
896{
897 static const StyleContentAlignmentData normalBehavior = {ContentPosition::Normal, ContentDistribution::Stretch};
898 return normalBehavior;
899}
900
901static bool overrideSizeChanged(const RenderBox& child, GridTrackSizingDirection direction, LayoutSize size)
902{
903 if (direction == ForColumns)
904 return !child.hasOverrideContainingBlockContentLogicalWidth() || child.overrideContainingBlockContentLogicalWidth() != size.width();
905 return !child.hasOverrideContainingBlockContentLogicalHeight() || child.overrideContainingBlockContentLogicalHeight() != size.height();
906}
907
908static bool hasRelativeBlockAxisSize(const RenderGrid& grid, const RenderBox& child)
909{
910 return GridLayoutFunctions::isOrthogonalChild(grid, child) ? child.hasRelativeLogicalWidth() || child.style().logicalWidth().isAuto() : child.hasRelativeLogicalHeight();
911}
912
913void RenderGrid::updateGridAreaLogicalSize(RenderBox& child, LayoutSize gridAreaLogicalSize) const
914{
915 // Because the grid area cannot be styled, we don't need to adjust
916 // the grid breadth to account for 'box-sizing'.
917 bool gridAreaWidthChanged = overrideSizeChanged(child, ForColumns, gridAreaLogicalSize);
918 bool gridAreaHeightChanged = overrideSizeChanged(child, ForRows, gridAreaLogicalSize);
919 if (gridAreaWidthChanged || (gridAreaHeightChanged && hasRelativeBlockAxisSize(*this, child)))
920 child.setNeedsLayout(MarkOnlyThis);
921
922 child.setOverrideContainingBlockContentLogicalWidth(gridAreaLogicalSize.width());
923 child.setOverrideContainingBlockContentLogicalHeight(gridAreaLogicalSize.height());
924}
925
926void RenderGrid::layoutGridItems()
927{
928 populateGridPositionsForDirection(ForColumns);
929 populateGridPositionsForDirection(ForRows);
930
931 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
932
933 if (m_grid.orderIterator().shouldSkipChild(*child)) {
934 if (child->isOutOfFlowPositioned())
935 prepareChildForPositionedLayout(*child);
936 continue;
937 }
938
939 // Setting the definite grid area's sizes. It may imply that the
940 // item must perform a layout if its area differs from the one
941 // used during the track sizing algorithm.
942 updateGridAreaLogicalSize(*child, LayoutSize(gridAreaBreadthForChildIncludingAlignmentOffsets(*child, ForColumns), gridAreaBreadthForChildIncludingAlignmentOffsets(*child, ForRows)));
943
944 LayoutRect oldChildRect = child->frameRect();
945
946 // Stretching logic might force a child layout, so we need to run it before the layoutIfNeeded
947 // call to avoid unnecessary relayouts. This might imply that child margins, needed to correctly
948 // determine the available space before stretching, are not set yet.
949 applyStretchAlignmentToChildIfNeeded(*child);
950
951 child->layoutIfNeeded();
952
953 // We need pending layouts to be done in order to compute auto-margins properly.
954 updateAutoMarginsInColumnAxisIfNeeded(*child);
955 updateAutoMarginsInRowAxisIfNeeded(*child);
956
957 setLogicalPositionForChild(*child);
958
959 // If the child moved, we have to repaint it as well as any floating/positioned
960 // descendants. An exception is if we need a layout. In this case, we know we're going to
961 // repaint ourselves (and the child) anyway.
962 if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
963 child->repaintDuringLayoutIfMoved(oldChildRect);
964 }
965}
966
967void RenderGrid::prepareChildForPositionedLayout(RenderBox& child)
968{
969 ASSERT(child.isOutOfFlowPositioned());
970 child.containingBlock()->insertPositionedObject(child);
971
972 RenderLayer* childLayer = child.layer();
973 // Static position of a positioned child should use the content-box (https://drafts.csswg.org/css-grid/#static-position).
974 childLayer->setStaticInlinePosition(borderAndPaddingStart());
975 childLayer->setStaticBlockPosition(borderAndPaddingBefore());
976}
977
978bool RenderGrid::hasStaticPositionForChild(const RenderBox& child, GridTrackSizingDirection direction) const
979{
980 return direction == ForColumns ? child.style().hasStaticInlinePosition(isHorizontalWritingMode()) : child.style().hasStaticBlockPosition(isHorizontalWritingMode());
981}
982
983void RenderGrid::layoutPositionedObject(RenderBox& child, bool relayoutChildren, bool fixedPositionObjectsOnly)
984{
985 LayoutUnit columnBreadth = gridAreaBreadthForOutOfFlowChild(child, ForColumns);
986 LayoutUnit rowBreadth = gridAreaBreadthForOutOfFlowChild(child, ForRows);
987
988 child.setOverrideContainingBlockContentLogicalWidth(columnBreadth);
989 child.setOverrideContainingBlockContentLogicalHeight(rowBreadth);
990
991 // Mark for layout as we're resetting the position before and we relay in generic layout logic
992 // for positioned items in order to get the offsets properly resolved.
993 child.setChildNeedsLayout(MarkOnlyThis);
994
995 RenderBlock::layoutPositionedObject(child, relayoutChildren, fixedPositionObjectsOnly);
996
997 setLogicalOffsetForChild(child, ForColumns);
998 setLogicalOffsetForChild(child, ForRows);
999}
1000
1001LayoutUnit RenderGrid::gridAreaBreadthForChildIncludingAlignmentOffsets(const RenderBox& child, GridTrackSizingDirection direction) const
1002{
1003 // We need the cached value when available because Content Distribution alignment properties
1004 // may have some influence in the final grid area breadth.
1005 const auto& tracks = m_trackSizingAlgorithm.tracks(direction);
1006 const auto& span = m_grid.gridItemSpan(child, direction);
1007 const auto& linePositions = (direction == ForColumns) ? m_columnPositions : m_rowPositions;
1008
1009 LayoutUnit initialTrackPosition = linePositions[span.startLine()];
1010 LayoutUnit finalTrackPosition = linePositions[span.endLine() - 1];
1011
1012 // Track Positions vector stores the 'start' grid line of each track, so we have to add last track's baseSize.
1013 return finalTrackPosition - initialTrackPosition + tracks[span.endLine() - 1].baseSize();
1014}
1015
1016void RenderGrid::populateGridPositionsForDirection(GridTrackSizingDirection direction)
1017{
1018 // Since we add alignment offsets and track gutters, grid lines are not always adjacent. Hence we will have to
1019 // assume from now on that we just store positions of the initial grid lines of each track,
1020 // except the last one, which is the only one considered as a final grid line of a track.
1021
1022 // The grid container's frame elements (border, padding and <content-position> offset) are sensible to the
1023 // inline-axis flow direction. However, column lines positions are 'direction' unaware. This simplification
1024 // allows us to use the same indexes to identify the columns independently on the inline-axis direction.
1025 bool isRowAxis = direction == ForColumns;
1026 auto& tracks = m_trackSizingAlgorithm.tracks(direction);
1027 unsigned numberOfTracks = tracks.size();
1028 unsigned numberOfLines = numberOfTracks + 1;
1029 unsigned lastLine = numberOfLines - 1;
1030 bool hasCollapsedTracks = m_grid.hasAutoRepeatEmptyTracks(direction);
1031 size_t numberOfCollapsedTracks = hasCollapsedTracks ? m_grid.autoRepeatEmptyTracks(direction)->size() : 0;
1032 const auto& offset = direction == ForColumns ? m_offsetBetweenColumns : m_offsetBetweenRows;
1033 auto& positions = isRowAxis ? m_columnPositions : m_rowPositions;
1034 positions.resize(numberOfLines);
1035
1036 auto borderAndPadding = isRowAxis ? borderAndPaddingLogicalLeft() : borderAndPaddingBefore();
1037#if !PLATFORM(IOS_FAMILY)
1038 // FIXME: Ideally scrollbarLogicalWidth() should return zero in iOS so we don't need this
1039 // (see bug https://webkit.org/b/191857).
1040 // If we are in horizontal writing mode and RTL direction the scrollbar is painted on the left,
1041 // so we need to take into account when computing the position of the columns.
1042 if (isRowAxis && style().isHorizontalWritingMode() && !style().isLeftToRightDirection())
1043 borderAndPadding += scrollbarLogicalWidth();
1044#endif
1045
1046 positions[0] = borderAndPadding + offset.positionOffset;
1047 if (numberOfLines > 1) {
1048 // If we have collapsed tracks we just ignore gaps here and add them later as we might not
1049 // compute the gap between two consecutive tracks without examining the surrounding ones.
1050 LayoutUnit gap = !hasCollapsedTracks ? gridGap(direction) : 0_lu;
1051 unsigned nextToLastLine = numberOfLines - 2;
1052 for (unsigned i = 0; i < nextToLastLine; ++i)
1053 positions[i + 1] = positions[i] + offset.distributionOffset + tracks[i].baseSize() + gap;
1054 positions[lastLine] = positions[nextToLastLine] + tracks[nextToLastLine].baseSize();
1055
1056 // Adjust collapsed gaps. Collapsed tracks cause the surrounding gutters to collapse (they
1057 // coincide exactly) except on the edges of the grid where they become 0.
1058 if (hasCollapsedTracks) {
1059 gap = gridGap(direction);
1060 unsigned remainingEmptyTracks = numberOfCollapsedTracks;
1061 LayoutUnit offsetAccumulator;
1062 LayoutUnit gapAccumulator;
1063 for (unsigned i = 1; i < lastLine; ++i) {
1064 if (m_grid.isEmptyAutoRepeatTrack(direction, i - 1)) {
1065 --remainingEmptyTracks;
1066 offsetAccumulator += offset.distributionOffset;
1067 } else {
1068 // Add gap between consecutive non empty tracks. Add it also just once for an
1069 // arbitrary number of empty tracks between two non empty ones.
1070 bool allRemainingTracksAreEmpty = remainingEmptyTracks == (lastLine - i);
1071 if (!allRemainingTracksAreEmpty || !m_grid.isEmptyAutoRepeatTrack(direction, i))
1072 gapAccumulator += gap;
1073 }
1074 positions[i] += gapAccumulator - offsetAccumulator;
1075 }
1076 positions[lastLine] += gapAccumulator - offsetAccumulator;
1077 }
1078 }
1079}
1080
1081static LayoutUnit computeOverflowAlignmentOffset(OverflowAlignment overflow, LayoutUnit trackSize, LayoutUnit childSize)
1082{
1083 LayoutUnit offset = trackSize - childSize;
1084 switch (overflow) {
1085 case OverflowAlignment::Safe:
1086 // If overflow is 'safe', we have to make sure we don't overflow the 'start'
1087 // edge (potentially cause some data loss as the overflow is unreachable).
1088 return std::max<LayoutUnit>(0, offset);
1089 case OverflowAlignment::Unsafe:
1090 case OverflowAlignment::Default:
1091 // If we overflow our alignment container and overflow is 'true' (default), we
1092 // ignore the overflow and just return the value regardless (which may cause data
1093 // loss as we overflow the 'start' edge).
1094 return offset;
1095 }
1096
1097 ASSERT_NOT_REACHED();
1098 return 0;
1099}
1100
1101LayoutUnit RenderGrid::availableAlignmentSpaceForChildBeforeStretching(LayoutUnit gridAreaBreadthForChild, const RenderBox& child) const
1102{
1103 // Because we want to avoid multiple layouts, stretching logic might be performed before
1104 // children are laid out, so we can't use the child cached values. Hence, we need to
1105 // compute margins in order to determine the available height before stretching.
1106 GridTrackSizingDirection childBlockFlowDirection = GridLayoutFunctions::flowAwareDirectionForChild(*this, child, ForRows);
1107 return gridAreaBreadthForChild - GridLayoutFunctions::marginLogicalSizeForChild(*this, childBlockFlowDirection, child);
1108}
1109
1110StyleSelfAlignmentData RenderGrid::alignSelfForChild(const RenderBox& child, const RenderStyle* gridStyle) const
1111{
1112 if (!gridStyle)
1113 gridStyle = &style();
1114 return child.style().resolvedAlignSelf(gridStyle, selfAlignmentNormalBehavior(&child));
1115}
1116
1117StyleSelfAlignmentData RenderGrid::justifySelfForChild(const RenderBox& child, const RenderStyle* gridStyle) const
1118{
1119 if (!gridStyle)
1120 gridStyle = &style();
1121 return child.style().resolvedJustifySelf(gridStyle, selfAlignmentNormalBehavior(&child));
1122}
1123
1124// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
1125void RenderGrid::applyStretchAlignmentToChildIfNeeded(RenderBox& child)
1126{
1127 ASSERT(child.overrideContainingBlockContentLogicalHeight());
1128
1129 // We clear height override values because we will decide now whether it's allowed or
1130 // not, evaluating the conditions which might have changed since the old values were set.
1131 child.clearOverrideContentLogicalHeight();
1132
1133 GridTrackSizingDirection childBlockDirection = GridLayoutFunctions::flowAwareDirectionForChild(*this, child, ForRows);
1134 bool blockFlowIsColumnAxis = childBlockDirection == ForRows;
1135 bool allowedToStretchChildBlockSize = blockFlowIsColumnAxis ? allowedToStretchChildAlongColumnAxis(child) : allowedToStretchChildAlongRowAxis(child);
1136 if (allowedToStretchChildBlockSize) {
1137 LayoutUnit stretchedLogicalHeight = availableAlignmentSpaceForChildBeforeStretching(GridLayoutFunctions::overrideContainingBlockContentSizeForChild(child, childBlockDirection).value(), child);
1138 LayoutUnit desiredLogicalHeight = child.constrainLogicalHeightByMinMax(stretchedLogicalHeight, -1_lu);
1139 child.setOverrideContentLogicalHeight(desiredLogicalHeight - child.borderAndPaddingLogicalHeight());
1140 if (desiredLogicalHeight != child.logicalHeight()) {
1141 // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905.
1142 child.setLogicalHeight(0_lu);
1143 child.setNeedsLayout();
1144 }
1145 }
1146}
1147
1148// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
1149bool RenderGrid::hasAutoMarginsInColumnAxis(const RenderBox& child) const
1150{
1151 if (isHorizontalWritingMode())
1152 return child.style().marginTop().isAuto() || child.style().marginBottom().isAuto();
1153 return child.style().marginLeft().isAuto() || child.style().marginRight().isAuto();
1154}
1155
1156// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
1157bool RenderGrid::hasAutoMarginsInRowAxis(const RenderBox& child) const
1158{
1159 if (isHorizontalWritingMode())
1160 return child.style().marginLeft().isAuto() || child.style().marginRight().isAuto();
1161 return child.style().marginTop().isAuto() || child.style().marginBottom().isAuto();
1162}
1163
1164// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
1165void RenderGrid::updateAutoMarginsInRowAxisIfNeeded(RenderBox& child)
1166{
1167 ASSERT(!child.isOutOfFlowPositioned());
1168
1169 LayoutUnit availableAlignmentSpace = child.overrideContainingBlockContentLogicalWidth().value() - child.logicalWidth() - child.marginLogicalWidth();
1170 if (availableAlignmentSpace <= 0)
1171 return;
1172
1173 const RenderStyle& parentStyle = style();
1174 Length marginStart = child.style().marginStartUsing(&parentStyle);
1175 Length marginEnd = child.style().marginEndUsing(&parentStyle);
1176 if (marginStart.isAuto() && marginEnd.isAuto()) {
1177 child.setMarginStart(availableAlignmentSpace / 2, &parentStyle);
1178 child.setMarginEnd(availableAlignmentSpace / 2, &parentStyle);
1179 } else if (marginStart.isAuto()) {
1180 child.setMarginStart(availableAlignmentSpace, &parentStyle);
1181 } else if (marginEnd.isAuto()) {
1182 child.setMarginEnd(availableAlignmentSpace, &parentStyle);
1183 }
1184}
1185
1186// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
1187void RenderGrid::updateAutoMarginsInColumnAxisIfNeeded(RenderBox& child)
1188{
1189 ASSERT(!child.isOutOfFlowPositioned());
1190
1191 LayoutUnit availableAlignmentSpace = child.overrideContainingBlockContentLogicalHeight().value() - child.logicalHeight() - child.marginLogicalHeight();
1192 if (availableAlignmentSpace <= 0)
1193 return;
1194
1195 const RenderStyle& parentStyle = style();
1196 Length marginBefore = child.style().marginBeforeUsing(&parentStyle);
1197 Length marginAfter = child.style().marginAfterUsing(&parentStyle);
1198 if (marginBefore.isAuto() && marginAfter.isAuto()) {
1199 child.setMarginBefore(availableAlignmentSpace / 2, &parentStyle);
1200 child.setMarginAfter(availableAlignmentSpace / 2, &parentStyle);
1201 } else if (marginBefore.isAuto()) {
1202 child.setMarginBefore(availableAlignmentSpace, &parentStyle);
1203 } else if (marginAfter.isAuto()) {
1204 child.setMarginAfter(availableAlignmentSpace, &parentStyle);
1205 }
1206}
1207
1208// FIXME: This logic could be refactored somehow and defined in RenderBox.
1209static int synthesizedBaselineFromBorderBox(const RenderBox& box, LineDirectionMode direction)
1210{
1211 return (direction == HorizontalLine ? box.size().height() : box.size().width()).toInt();
1212}
1213
1214bool RenderGrid::isBaselineAlignmentForChild(const RenderBox& child) const
1215{
1216 return isBaselineAlignmentForChild(child, GridRowAxis) || isBaselineAlignmentForChild(child, GridColumnAxis);
1217}
1218
1219bool RenderGrid::isBaselineAlignmentForChild(const RenderBox& child, GridAxis baselineAxis) const
1220{
1221 if (child.isOutOfFlowPositioned())
1222 return false;
1223 ItemPosition align = selfAlignmentForChild(baselineAxis, child).position();
1224 bool hasAutoMargins = baselineAxis == GridColumnAxis ? hasAutoMarginsInColumnAxis(child) : hasAutoMarginsInRowAxis(child);
1225 return isBaselinePosition(align) && !hasAutoMargins;
1226}
1227
1228// FIXME: This logic is shared by RenderFlexibleBox, so it might be refactored somehow.
1229int RenderGrid::baselinePosition(FontBaseline, bool, LineDirectionMode direction, LinePositionMode mode) const
1230{
1231#if !ASSERT_DISABLED
1232 ASSERT(mode == PositionOnContainingLine);
1233#else
1234 UNUSED_PARAM(mode);
1235#endif
1236 auto baseline = firstLineBaseline();
1237 if (!baseline)
1238 return synthesizedBaselineFromBorderBox(*this, direction) + marginLogicalHeight();
1239
1240 return baseline.value() + (direction == HorizontalLine ? marginTop() : marginRight()).toInt();
1241}
1242
1243Optional<int> RenderGrid::firstLineBaseline() const
1244{
1245 if (isWritingModeRoot() || !m_grid.hasGridItems())
1246 return WTF::nullopt;
1247
1248 const RenderBox* baselineChild = nullptr;
1249 // Finding the first grid item in grid order.
1250 unsigned numColumns = m_grid.numTracks(ForColumns);
1251 for (size_t column = 0; column < numColumns; column++) {
1252 for (auto& child : m_grid.cell(0, column)) {
1253 ASSERT(child.get());
1254 // If an item participates in baseline alignment, we select such item.
1255 if (isBaselineAlignmentForChild(*child)) {
1256 // FIXME: self-baseline and content-baseline alignment not implemented yet.
1257 baselineChild = child.get();
1258 break;
1259 }
1260 if (!baselineChild)
1261 baselineChild = child.get();
1262 }
1263 }
1264
1265 if (!baselineChild)
1266 return WTF::nullopt;
1267
1268 auto baseline = GridLayoutFunctions::isOrthogonalChild(*this, *baselineChild) ? WTF::nullopt : baselineChild->firstLineBaseline();
1269 // We take border-box's bottom if no valid baseline.
1270 if (!baseline) {
1271 // FIXME: We should pass |direction| into firstLineBaseline and stop bailing out if we're a writing
1272 // mode root. This would also fix some cases where the grid is orthogonal to its container.
1273 LineDirectionMode direction = isHorizontalWritingMode() ? HorizontalLine : VerticalLine;
1274 return synthesizedBaselineFromBorderBox(*baselineChild, direction) + logicalTopForChild(*baselineChild).toInt();
1275 }
1276 return baseline.value() + baselineChild->logicalTop().toInt();
1277}
1278
1279Optional<int> RenderGrid::inlineBlockBaseline(LineDirectionMode) const
1280{
1281 return firstLineBaseline();
1282}
1283
1284LayoutUnit RenderGrid::columnAxisBaselineOffsetForChild(const RenderBox& child) const
1285{
1286 return m_trackSizingAlgorithm.baselineOffsetForChild(child, GridColumnAxis);
1287}
1288
1289LayoutUnit RenderGrid::rowAxisBaselineOffsetForChild(const RenderBox& child) const
1290{
1291 return m_trackSizingAlgorithm.baselineOffsetForChild(child, GridRowAxis);
1292}
1293
1294GridAxisPosition RenderGrid::columnAxisPositionForChild(const RenderBox& child) const
1295{
1296 bool hasSameWritingMode = child.style().writingMode() == style().writingMode();
1297 bool childIsLTR = child.style().isLeftToRightDirection();
1298 if (child.isOutOfFlowPositioned() && !hasStaticPositionForChild(child, ForRows))
1299 return GridAxisStart;
1300
1301 switch (alignSelfForChild(child).position()) {
1302 case ItemPosition::SelfStart:
1303 // FIXME: Should we implement this logic in a generic utility function ?
1304 // Aligns the alignment subject to be flush with the edge of the alignment container
1305 // corresponding to the alignment subject's 'start' side in the column axis.
1306 if (GridLayoutFunctions::isOrthogonalChild(*this, child)) {
1307 // If orthogonal writing-modes, self-start will be based on the child's inline-axis
1308 // direction (inline-start), because it's the one parallel to the column axis.
1309 if (style().isFlippedBlocksWritingMode())
1310 return childIsLTR ? GridAxisEnd : GridAxisStart;
1311 return childIsLTR ? GridAxisStart : GridAxisEnd;
1312 }
1313 // self-start is based on the child's block-flow direction. That's why we need to check against the grid container's block-flow direction.
1314 return hasSameWritingMode ? GridAxisStart : GridAxisEnd;
1315 case ItemPosition::SelfEnd:
1316 // FIXME: Should we implement this logic in a generic utility function ?
1317 // Aligns the alignment subject to be flush with the edge of the alignment container
1318 // corresponding to the alignment subject's 'end' side in the column axis.
1319 if (GridLayoutFunctions::isOrthogonalChild(*this, child)) {
1320 // If orthogonal writing-modes, self-end will be based on the child's inline-axis
1321 // direction, (inline-end) because it's the one parallel to the column axis.
1322 if (style().isFlippedBlocksWritingMode())
1323 return childIsLTR ? GridAxisStart : GridAxisEnd;
1324 return childIsLTR ? GridAxisEnd : GridAxisStart;
1325 }
1326 // self-end is based on the child's block-flow direction. That's why we need to check against the grid container's block-flow direction.
1327 return hasSameWritingMode ? GridAxisEnd : GridAxisStart;
1328 case ItemPosition::Left:
1329 // Aligns the alignment subject to be flush with the alignment container's 'line-left' edge.
1330 // The alignment axis (column axis) is always orthogonal to the inline axis, hence this value behaves as 'start'.
1331 return GridAxisStart;
1332 case ItemPosition::Right:
1333 // Aligns the alignment subject to be flush with the alignment container's 'line-right' edge.
1334 // The alignment axis (column axis) is always orthogonal to the inline axis, hence this value behaves as 'start'.
1335 return GridAxisStart;
1336 case ItemPosition::Center:
1337 return GridAxisCenter;
1338 case ItemPosition::FlexStart: // Only used in flex layout, otherwise equivalent to 'start'.
1339 // Aligns the alignment subject to be flush with the alignment container's 'start' edge (block-start) in the column axis.
1340 case ItemPosition::Start:
1341 return GridAxisStart;
1342 case ItemPosition::FlexEnd: // Only used in flex layout, otherwise equivalent to 'end'.
1343 // Aligns the alignment subject to be flush with the alignment container's 'end' edge (block-end) in the column axis.
1344 case ItemPosition::End:
1345 return GridAxisEnd;
1346 case ItemPosition::Stretch:
1347 return GridAxisStart;
1348 case ItemPosition::Baseline:
1349 case ItemPosition::LastBaseline:
1350 // FIXME: Implement the previous values. For now, we always 'start' align the child.
1351 return GridAxisStart;
1352 case ItemPosition::Legacy:
1353 case ItemPosition::Auto:
1354 case ItemPosition::Normal:
1355 break;
1356 }
1357
1358 ASSERT_NOT_REACHED();
1359 return GridAxisStart;
1360}
1361
1362GridAxisPosition RenderGrid::rowAxisPositionForChild(const RenderBox& child) const
1363{
1364 bool hasSameDirection = child.style().direction() == style().direction();
1365 bool gridIsLTR = style().isLeftToRightDirection();
1366 if (child.isOutOfFlowPositioned() && !hasStaticPositionForChild(child, ForColumns))
1367 return GridAxisStart;
1368
1369 switch (justifySelfForChild(child).position()) {
1370 case ItemPosition::SelfStart:
1371 // FIXME: Should we implement this logic in a generic utility function ?
1372 // Aligns the alignment subject to be flush with the edge of the alignment container
1373 // corresponding to the alignment subject's 'start' side in the row axis.
1374 if (GridLayoutFunctions::isOrthogonalChild(*this, child)) {
1375 // If orthogonal writing-modes, self-start will be based on the child's block-axis
1376 // direction, because it's the one parallel to the row axis.
1377 if (child.style().isFlippedBlocksWritingMode())
1378 return gridIsLTR ? GridAxisEnd : GridAxisStart;
1379 return gridIsLTR ? GridAxisStart : GridAxisEnd;
1380 }
1381 // self-start is based on the child's inline-flow direction. That's why we need to check against the grid container's direction.
1382 return hasSameDirection ? GridAxisStart : GridAxisEnd;
1383 case ItemPosition::SelfEnd:
1384 // FIXME: Should we implement this logic in a generic utility function ?
1385 // Aligns the alignment subject to be flush with the edge of the alignment container
1386 // corresponding to the alignment subject's 'end' side in the row axis.
1387 if (GridLayoutFunctions::isOrthogonalChild(*this, child)) {
1388 // If orthogonal writing-modes, self-end will be based on the child's block-axis
1389 // direction, because it's the one parallel to the row axis.
1390 if (child.style().isFlippedBlocksWritingMode())
1391 return gridIsLTR ? GridAxisStart : GridAxisEnd;
1392 return gridIsLTR ? GridAxisEnd : GridAxisStart;
1393 }
1394 // self-end is based on the child's inline-flow direction. That's why we need to check against the grid container's direction.
1395 return hasSameDirection ? GridAxisEnd : GridAxisStart;
1396 case ItemPosition::Left:
1397 // Aligns the alignment subject to be flush with the alignment container's 'line-left' edge.
1398 // We want the physical 'left' side, so we have to take account, container's inline-flow direction.
1399 return gridIsLTR ? GridAxisStart : GridAxisEnd;
1400 case ItemPosition::Right:
1401 // Aligns the alignment subject to be flush with the alignment container's 'line-right' edge.
1402 // We want the physical 'right' side, so we have to take account, container's inline-flow direction.
1403 return gridIsLTR ? GridAxisEnd : GridAxisStart;
1404 case ItemPosition::Center:
1405 return GridAxisCenter;
1406 case ItemPosition::FlexStart: // Only used in flex layout, otherwise equivalent to 'start'.
1407 // Aligns the alignment subject to be flush with the alignment container's 'start' edge (inline-start) in the row axis.
1408 case ItemPosition::Start:
1409 return GridAxisStart;
1410 case ItemPosition::FlexEnd: // Only used in flex layout, otherwise equivalent to 'end'.
1411 // Aligns the alignment subject to be flush with the alignment container's 'end' edge (inline-end) in the row axis.
1412 case ItemPosition::End:
1413 return GridAxisEnd;
1414 case ItemPosition::Stretch:
1415 return GridAxisStart;
1416 case ItemPosition::Baseline:
1417 case ItemPosition::LastBaseline:
1418 // FIXME: Implement the previous values. For now, we always 'start' align the child.
1419 return GridAxisStart;
1420 case ItemPosition::Legacy:
1421 case ItemPosition::Auto:
1422 case ItemPosition::Normal:
1423 break;
1424 }
1425
1426 ASSERT_NOT_REACHED();
1427 return GridAxisStart;
1428}
1429
1430LayoutUnit RenderGrid::columnAxisOffsetForChild(const RenderBox& child) const
1431{
1432 LayoutUnit startOfRow;
1433 LayoutUnit endOfRow;
1434 gridAreaPositionForChild(child, ForRows, startOfRow, endOfRow);
1435 LayoutUnit startPosition = startOfRow + marginBeforeForChild(child);
1436 if (hasAutoMarginsInColumnAxis(child))
1437 return startPosition;
1438 GridAxisPosition axisPosition = columnAxisPositionForChild(child);
1439 switch (axisPosition) {
1440 case GridAxisStart:
1441 return startPosition + columnAxisBaselineOffsetForChild(child);
1442 case GridAxisEnd:
1443 case GridAxisCenter: {
1444 LayoutUnit columnAxisChildSize = GridLayoutFunctions::isOrthogonalChild(*this, child) ? child.logicalWidth() + child.marginLogicalWidth() : child.logicalHeight() + child.marginLogicalHeight();
1445 auto overflow = alignSelfForChild(child).overflow();
1446 LayoutUnit offsetFromStartPosition = computeOverflowAlignmentOffset(overflow, endOfRow - startOfRow, columnAxisChildSize);
1447 return startPosition + (axisPosition == GridAxisEnd ? offsetFromStartPosition : offsetFromStartPosition / 2);
1448 }
1449 }
1450
1451 ASSERT_NOT_REACHED();
1452 return 0;
1453}
1454
1455LayoutUnit RenderGrid::rowAxisOffsetForChild(const RenderBox& child) const
1456{
1457 LayoutUnit startOfColumn;
1458 LayoutUnit endOfColumn;
1459 gridAreaPositionForChild(child, ForColumns, startOfColumn, endOfColumn);
1460 LayoutUnit startPosition = startOfColumn + marginStartForChild(child);
1461 if (hasAutoMarginsInRowAxis(child))
1462 return startPosition;
1463 GridAxisPosition axisPosition = rowAxisPositionForChild(child);
1464 switch (axisPosition) {
1465 case GridAxisStart:
1466 return startPosition + rowAxisBaselineOffsetForChild(child);
1467 case GridAxisEnd:
1468 case GridAxisCenter: {
1469 LayoutUnit rowAxisChildSize = GridLayoutFunctions::isOrthogonalChild(*this, child) ? child.logicalHeight() + child.marginLogicalHeight() : child.logicalWidth() + child.marginLogicalWidth();
1470 auto overflow = justifySelfForChild(child).overflow();
1471 LayoutUnit offsetFromStartPosition = computeOverflowAlignmentOffset(overflow, endOfColumn - startOfColumn, rowAxisChildSize);
1472 return startPosition + (axisPosition == GridAxisEnd ? offsetFromStartPosition : offsetFromStartPosition / 2);
1473 }
1474 }
1475
1476 ASSERT_NOT_REACHED();
1477 return 0;
1478}
1479
1480LayoutUnit RenderGrid::resolveAutoStartGridPosition(GridTrackSizingDirection direction) const
1481{
1482 if (direction == ForRows || style().isLeftToRightDirection())
1483 return 0_lu;
1484
1485 int lastLine = numTracks(ForColumns, m_grid);
1486 ContentPosition position = style().resolvedJustifyContentPosition(contentAlignmentNormalBehaviorGrid());
1487 if (position == ContentPosition::End)
1488 return m_columnPositions[lastLine] - clientLogicalWidth();
1489 if (position == ContentPosition::Start || style().resolvedJustifyContentDistribution(contentAlignmentNormalBehaviorGrid()) == ContentDistribution::Stretch)
1490 return m_columnPositions[0] - borderAndPaddingLogicalLeft();
1491 return 0_lu;
1492}
1493
1494LayoutUnit RenderGrid::resolveAutoEndGridPosition(GridTrackSizingDirection direction) const
1495{
1496 if (direction == ForRows)
1497 return clientLogicalHeight();
1498 if (style().isLeftToRightDirection())
1499 return clientLogicalWidth();
1500
1501 int lastLine = numTracks(ForColumns, m_grid);
1502 ContentPosition position = style().resolvedJustifyContentPosition(contentAlignmentNormalBehaviorGrid());
1503 if (position == ContentPosition::End)
1504 return m_columnPositions[lastLine];
1505 if (position == ContentPosition::Start || style().resolvedJustifyContentDistribution(contentAlignmentNormalBehaviorGrid()) == ContentDistribution::Stretch)
1506 return m_columnPositions[0] - borderAndPaddingLogicalLeft() + clientLogicalWidth();
1507 return clientLogicalWidth();
1508}
1509
1510LayoutUnit RenderGrid::gridAreaBreadthForOutOfFlowChild(const RenderBox& child, GridTrackSizingDirection direction)
1511{
1512 ASSERT(child.isOutOfFlowPositioned());
1513 bool isRowAxis = direction == ForColumns;
1514 GridSpan span = GridPositionsResolver::resolveGridPositionsFromStyle(style(), child, direction, autoRepeatCountForDirection(direction));
1515 if (span.isIndefinite())
1516 return isRowAxis ? clientLogicalWidth() : clientLogicalHeight();
1517
1518 int smallestStart = abs(m_grid.smallestTrackStart(direction));
1519 int startLine = span.untranslatedStartLine() + smallestStart;
1520 int endLine = span.untranslatedEndLine() + smallestStart;
1521 int lastLine = numTracks(direction, m_grid);
1522 GridPosition startPosition = direction == ForColumns ? child.style().gridItemColumnStart() : child.style().gridItemRowStart();
1523 GridPosition endPosition = direction == ForColumns ? child.style().gridItemColumnEnd() : child.style().gridItemRowEnd();
1524
1525 bool startIsAuto = startPosition.isAuto() || startLine < 0 || startLine > lastLine;
1526 bool endIsAuto = endPosition.isAuto() || endLine < 0 || endLine > lastLine;
1527
1528 if (startIsAuto && endIsAuto)
1529 return isRowAxis ? clientLogicalWidth() : clientLogicalHeight();
1530
1531 LayoutUnit start;
1532 LayoutUnit end;
1533 auto& positions = isRowAxis ? m_columnPositions : m_rowPositions;
1534 auto& outOfFlowItemLine = isRowAxis ? m_outOfFlowItemColumn : m_outOfFlowItemRow;
1535 LayoutUnit borderEdge = isRowAxis ? borderLogicalLeft() : borderBefore();
1536 if (startIsAuto)
1537 start = resolveAutoStartGridPosition(direction) + borderEdge;
1538 else {
1539 outOfFlowItemLine.set(&child, startLine);
1540 start = positions[startLine];
1541 }
1542 if (endIsAuto)
1543 end = resolveAutoEndGridPosition(direction) + borderEdge;
1544 else {
1545 end = positions[endLine];
1546 // These vectors store line positions including gaps, but we shouldn't consider them for the edges of the grid.
1547 Optional<LayoutUnit> availableSizeForGutters = availableSpaceForGutters(direction);
1548 if (endLine > 0 && endLine < lastLine) {
1549 ASSERT(!m_grid.needsItemsPlacement());
1550 end -= guttersSize(m_grid, direction, endLine - 1, 2, availableSizeForGutters);
1551 end -= isRowAxis ? m_offsetBetweenColumns.distributionOffset : m_offsetBetweenRows.distributionOffset;
1552 }
1553 }
1554 return std::max(end - start, 0_lu);
1555}
1556
1557LayoutUnit RenderGrid::logicalOffsetForOutOfFlowChild(const RenderBox& child, GridTrackSizingDirection direction, LayoutUnit trackBreadth) const
1558{
1559 ASSERT(child.isOutOfFlowPositioned());
1560 if (hasStaticPositionForChild(child, direction))
1561 return 0_lu;
1562
1563 bool isRowAxis = direction == ForColumns;
1564 bool isFlowAwareRowAxis = GridLayoutFunctions::flowAwareDirectionForChild(*this, child, direction) == ForColumns;
1565 LayoutUnit childPosition = isFlowAwareRowAxis ? child.logicalLeft() : child.logicalTop();
1566 LayoutUnit gridBorder = isRowAxis ? borderLogicalLeft() : borderBefore();
1567 LayoutUnit childMargin = isFlowAwareRowAxis ? child.marginLogicalLeft() : child.marginBefore();
1568 LayoutUnit offset = childPosition - gridBorder - childMargin;
1569 if (!isRowAxis || style().isLeftToRightDirection())
1570 return offset;
1571
1572 LayoutUnit childBreadth = isFlowAwareRowAxis ? child.logicalWidth() + child.marginLogicalWidth() : child.logicalHeight() + child.marginLogicalHeight();
1573 return trackBreadth - offset - childBreadth;
1574}
1575
1576void RenderGrid::gridAreaPositionForOutOfFlowChild(const RenderBox& child, GridTrackSizingDirection direction, LayoutUnit& start, LayoutUnit& end) const
1577{
1578 ASSERT(child.isOutOfFlowPositioned());
1579 ASSERT(GridLayoutFunctions::hasOverrideContainingBlockContentSizeForChild(child, direction));
1580 LayoutUnit trackBreadth = GridLayoutFunctions::overrideContainingBlockContentSizeForChild(child, direction).value();
1581 bool isRowAxis = direction == ForColumns;
1582 auto& outOfFlowItemLine = isRowAxis ? m_outOfFlowItemColumn : m_outOfFlowItemRow;
1583 start = isRowAxis ? borderLogicalLeft() : borderBefore();
1584 if (auto line = outOfFlowItemLine.get(&child)) {
1585 auto& positions = isRowAxis ? m_columnPositions : m_rowPositions;
1586 start = positions[line.value()];
1587 }
1588 start += logicalOffsetForOutOfFlowChild(child, direction, trackBreadth);
1589 end = start + trackBreadth;
1590}
1591
1592void RenderGrid::gridAreaPositionForInFlowChild(const RenderBox& child, GridTrackSizingDirection direction, LayoutUnit& start, LayoutUnit& end) const
1593{
1594 ASSERT(!child.isOutOfFlowPositioned());
1595 const GridSpan& span = m_grid.gridItemSpan(child, direction);
1596 // FIXME (lajava): This is a common pattern, why not defining a function like
1597 // positions(direction) ?
1598 auto& positions = direction == ForColumns ? m_columnPositions : m_rowPositions;
1599 start = positions[span.startLine()];
1600 end = positions[span.endLine()];
1601 // The 'positions' vector includes distribution offset (because of content
1602 // alignment) and gutters so we need to subtract them to get the actual
1603 // end position for a given track (this does not have to be done for the
1604 // last track as there are no more positions's elements after it, nor for
1605 // collapsed tracks).
1606 if (span.endLine() < positions.size() - 1
1607 && !(m_grid.hasAutoRepeatEmptyTracks(direction)
1608 && m_grid.isEmptyAutoRepeatTrack(direction, span.endLine()))) {
1609 end -= gridGap(direction) + gridItemOffset(direction);
1610 }
1611}
1612
1613void RenderGrid::gridAreaPositionForChild(const RenderBox& child, GridTrackSizingDirection direction, LayoutUnit& start, LayoutUnit& end) const
1614{
1615 if (child.isOutOfFlowPositioned())
1616 gridAreaPositionForOutOfFlowChild(child, direction, start, end);
1617 else
1618 gridAreaPositionForInFlowChild(child, direction, start, end);
1619}
1620
1621ContentPosition static resolveContentDistributionFallback(ContentDistribution distribution)
1622{
1623 switch (distribution) {
1624 case ContentDistribution::SpaceBetween:
1625 return ContentPosition::Start;
1626 case ContentDistribution::SpaceAround:
1627 return ContentPosition::Center;
1628 case ContentDistribution::SpaceEvenly:
1629 return ContentPosition::Center;
1630 case ContentDistribution::Stretch:
1631 return ContentPosition::Start;
1632 case ContentDistribution::Default:
1633 return ContentPosition::Normal;
1634 }
1635
1636 ASSERT_NOT_REACHED();
1637 return ContentPosition::Normal;
1638}
1639
1640static void contentDistributionOffset(ContentAlignmentData& offset, const LayoutUnit& availableFreeSpace, ContentPosition& fallbackPosition, ContentDistribution distribution, unsigned numberOfGridTracks)
1641{
1642 if (distribution != ContentDistribution::Default && fallbackPosition == ContentPosition::Normal)
1643 fallbackPosition = resolveContentDistributionFallback(distribution);
1644
1645 // Initialize to an invalid offset.
1646 offset.positionOffset = -1_lu;
1647 offset.distributionOffset = -1_lu;
1648 if (availableFreeSpace <= 0)
1649 return;
1650
1651 LayoutUnit positionOffset;
1652 LayoutUnit distributionOffset;
1653 switch (distribution) {
1654 case ContentDistribution::SpaceBetween:
1655 if (numberOfGridTracks < 2)
1656 return;
1657 distributionOffset = availableFreeSpace / (numberOfGridTracks - 1);
1658 positionOffset = 0_lu;
1659 break;
1660 case ContentDistribution::SpaceAround:
1661 if (numberOfGridTracks < 1)
1662 return;
1663 distributionOffset = availableFreeSpace / numberOfGridTracks;
1664 positionOffset = distributionOffset / 2;
1665 break;
1666 case ContentDistribution::SpaceEvenly:
1667 distributionOffset = availableFreeSpace / (numberOfGridTracks + 1);
1668 positionOffset = distributionOffset;
1669 break;
1670 case ContentDistribution::Stretch:
1671 case ContentDistribution::Default:
1672 return;
1673 default:
1674 ASSERT_NOT_REACHED();
1675 return;
1676 }
1677
1678 offset.positionOffset = positionOffset;
1679 offset.distributionOffset = distributionOffset;
1680}
1681
1682StyleContentAlignmentData RenderGrid::contentAlignment(GridTrackSizingDirection direction) const
1683{
1684 return direction == ForColumns ? style().resolvedJustifyContent(contentAlignmentNormalBehaviorGrid()) : style().resolvedAlignContent(contentAlignmentNormalBehaviorGrid());
1685}
1686
1687void RenderGrid::computeContentPositionAndDistributionOffset(GridTrackSizingDirection direction, const LayoutUnit& availableFreeSpace, unsigned numberOfGridTracks)
1688{
1689 bool isRowAxis = direction == ForColumns;
1690 auto& offset =
1691 isRowAxis ? m_offsetBetweenColumns : m_offsetBetweenRows;
1692 auto contentAlignmentData = contentAlignment(direction);
1693 auto position = contentAlignmentData.position();
1694 // If <content-distribution> value can't be applied, 'position' will become the associated
1695 // <content-position> fallback value.
1696 contentDistributionOffset(offset, availableFreeSpace, position, contentAlignmentData.distribution(), numberOfGridTracks);
1697 if (offset.isValid())
1698 return;
1699
1700 if (availableFreeSpace <= 0 && contentAlignmentData.overflow() == OverflowAlignment::Safe) {
1701 offset.positionOffset = 0_lu;
1702 offset.distributionOffset = 0_lu;
1703 return;
1704 }
1705
1706 LayoutUnit positionOffset;
1707 switch (position) {
1708 case ContentPosition::Left:
1709 ASSERT(isRowAxis);
1710 break;
1711 case ContentPosition::Right:
1712 ASSERT(isRowAxis);
1713 positionOffset = availableFreeSpace;
1714 break;
1715 case ContentPosition::Center:
1716 positionOffset = availableFreeSpace / 2;
1717 break;
1718 case ContentPosition::FlexEnd: // Only used in flex layout, for other layout, it's equivalent to 'end'.
1719 case ContentPosition::End:
1720 if (isRowAxis)
1721 positionOffset = style().isLeftToRightDirection() ? availableFreeSpace : 0_lu;
1722 else
1723 positionOffset = availableFreeSpace;
1724 break;
1725 case ContentPosition::FlexStart: // Only used in flex layout, for other layout, it's equivalent to 'start'.
1726 case ContentPosition::Start:
1727 if (isRowAxis)
1728 positionOffset = style().isLeftToRightDirection() ? 0_lu : availableFreeSpace;
1729 break;
1730 case ContentPosition::Baseline:
1731 case ContentPosition::LastBaseline:
1732 // FIXME: Implement the previous values. For now, we always 'start' align.
1733 // http://webkit.org/b/145566
1734 if (isRowAxis)
1735 positionOffset = style().isLeftToRightDirection() ? 0_lu : availableFreeSpace;
1736 break;
1737 case ContentPosition::Normal:
1738 default:
1739 ASSERT_NOT_REACHED();
1740 return;
1741 }
1742
1743 offset.positionOffset = positionOffset;
1744 offset.distributionOffset = 0_lu;
1745}
1746
1747LayoutUnit RenderGrid::translateOutOfFlowRTLCoordinate(const RenderBox& child, LayoutUnit coordinate) const
1748{
1749 ASSERT(child.isOutOfFlowPositioned());
1750 ASSERT(!style().isLeftToRightDirection());
1751
1752 if (m_outOfFlowItemColumn.get(&child))
1753 return translateRTLCoordinate(coordinate);
1754
1755 return borderLogicalLeft() + borderLogicalRight() + clientLogicalWidth() - coordinate;
1756}
1757
1758LayoutUnit RenderGrid::translateRTLCoordinate(LayoutUnit coordinate) const
1759{
1760 ASSERT(!style().isLeftToRightDirection());
1761
1762 LayoutUnit alignmentOffset = m_columnPositions[0];
1763 LayoutUnit rightGridEdgePosition = m_columnPositions[m_columnPositions.size() - 1];
1764 return rightGridEdgePosition + alignmentOffset - coordinate;
1765}
1766
1767// FIXME: SetLogicalPositionForChild has only one caller, consider its refactoring in the future.
1768void RenderGrid::setLogicalPositionForChild(RenderBox& child) const
1769{
1770 // "In the positioning phase [...] calculations are performed according to the writing mode of the containing block of the box establishing the
1771 // orthogonal flow." However, 'setLogicalLocation' will only take into account the child's writing-mode, so the position may need to be transposed.
1772 LayoutPoint childLocation(logicalOffsetForChild(child, ForColumns), logicalOffsetForChild(child, ForRows));
1773 child.setLogicalLocation(GridLayoutFunctions::isOrthogonalChild(*this, child) ? childLocation.transposedPoint() : childLocation);
1774}
1775
1776void RenderGrid::setLogicalOffsetForChild(RenderBox& child, GridTrackSizingDirection direction) const
1777{
1778 if (!child.isGridItem() && hasStaticPositionForChild(child, direction))
1779 return;
1780 // 'setLogicalLeft' and 'setLogicalTop' only take into account the child's writing-mode, that's why 'flowAwareDirectionForChild' is needed.
1781 if (GridLayoutFunctions::flowAwareDirectionForChild(*this, child, direction) == ForColumns)
1782 child.setLogicalLeft(logicalOffsetForChild(child, direction));
1783 else
1784 child.setLogicalTop(logicalOffsetForChild(child, direction));
1785}
1786
1787LayoutUnit RenderGrid::logicalOffsetForChild(const RenderBox& child, GridTrackSizingDirection direction) const
1788{
1789 if (direction == ForRows)
1790 return columnAxisOffsetForChild(child);
1791 LayoutUnit rowAxisOffset = rowAxisOffsetForChild(child);
1792 // We stored m_columnPositions's data ignoring the direction, hence we might need now
1793 // to translate positions from RTL to LTR, as it's more convenient for painting.
1794 if (!style().isLeftToRightDirection())
1795 rowAxisOffset = (child.isOutOfFlowPositioned() ? translateOutOfFlowRTLCoordinate(child, rowAxisOffset) : translateRTLCoordinate(rowAxisOffset)) - (GridLayoutFunctions::isOrthogonalChild(*this, child) ? child.logicalHeight() : child.logicalWidth());
1796 return rowAxisOffset;
1797}
1798
1799unsigned RenderGrid::nonCollapsedTracks(GridTrackSizingDirection direction) const
1800{
1801 auto& tracks = m_trackSizingAlgorithm.tracks(direction);
1802 size_t numberOfTracks = tracks.size();
1803 bool hasCollapsedTracks = m_grid.hasAutoRepeatEmptyTracks(direction);
1804 size_t numberOfCollapsedTracks = hasCollapsedTracks ? m_grid.autoRepeatEmptyTracks(direction)->size() : 0;
1805 return numberOfTracks - numberOfCollapsedTracks;
1806}
1807
1808unsigned RenderGrid::numTracks(GridTrackSizingDirection direction, const Grid& grid) const
1809{
1810 // Due to limitations in our internal representation, we cannot know the number of columns from
1811 // m_grid *if* there is no row (because m_grid would be empty). That's why in that case we need
1812 // to get it from the style. Note that we know for sure that there are't any implicit tracks,
1813 // because not having rows implies that there are no "normal" children (out-of-flow children are
1814 // not stored in m_grid).
1815 ASSERT(!grid.needsItemsPlacement());
1816 if (direction == ForRows)
1817 return grid.numTracks(ForRows);
1818
1819 // FIXME: This still requires knowledge about m_grid internals.
1820 return grid.numTracks(ForRows) ? grid.numTracks(ForColumns) : GridPositionsResolver::explicitGridColumnCount(style(), grid.autoRepeatTracks(ForColumns));
1821}
1822
1823void RenderGrid::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& forChild, bool usePrintRect)
1824{
1825 ASSERT(!m_grid.needsItemsPlacement());
1826 for (RenderBox* child = m_grid.orderIterator().first(); child; child = m_grid.orderIterator().next())
1827 paintChild(*child, paintInfo, paintOffset, forChild, usePrintRect, PaintAsInlineBlock);
1828}
1829
1830const char* RenderGrid::renderName() const
1831{
1832 if (isFloating())
1833 return "RenderGrid (floating)";
1834 if (isOutOfFlowPositioned())
1835 return "RenderGrid (positioned)";
1836 if (isAnonymous())
1837 return "RenderGrid (generated)";
1838 if (isRelativelyPositioned())
1839 return "RenderGrid (relative positioned)";
1840 return "RenderGrid";
1841}
1842
1843} // namespace WebCore
1844