Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / layout / LayoutGrid.cpp
blobfc5aa6d6f0a9b1923406f12b6936ff71a7b446b3
1 /*
2 * Copyright (C) 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "config.h"
27 #include "core/layout/LayoutGrid.h"
29 #include "core/layout/LayoutView.h"
30 #include "core/layout/TextAutosizer.h"
31 #include "core/paint/DeprecatedPaintLayer.h"
32 #include "core/paint/GridPainter.h"
33 #include "core/style/ComputedStyle.h"
34 #include "core/style/GridCoordinate.h"
35 #include "platform/LengthFunctions.h"
37 namespace blink {
39 static const int infinity = -1;
41 class GridItemWithSpan;
43 class GridTrack {
44 public:
45 GridTrack()
46 : m_baseSize(0)
47 , m_growthLimit(0)
48 , m_plannedSize(0)
49 , m_sizeDuringDistribution(0)
50 , m_infinitelyGrowable(false)
54 const LayoutUnit& baseSize() const
56 ASSERT(isGrowthLimitBiggerThanBaseSize());
57 return m_baseSize;
60 const LayoutUnit& growthLimit() const
62 ASSERT(isGrowthLimitBiggerThanBaseSize());
63 return m_growthLimit;
66 void setBaseSize(LayoutUnit baseSize)
68 m_baseSize = baseSize;
69 ensureGrowthLimitIsBiggerThanBaseSize();
72 void setGrowthLimit(LayoutUnit growthLimit)
74 m_growthLimit = growthLimit;
75 ensureGrowthLimitIsBiggerThanBaseSize();
78 bool growthLimitIsInfinite() const
80 return m_growthLimit == infinity;
83 bool infiniteGrowthPotential() const
85 return growthLimitIsInfinite() || m_infinitelyGrowable;
88 const LayoutUnit& plannedSize() const { return m_plannedSize; }
90 void setPlannedSize(const LayoutUnit& plannedSize)
92 ASSERT(plannedSize >= 0 || plannedSize == infinity);
93 m_plannedSize = plannedSize;
96 const LayoutUnit& sizeDuringDistribution() { return m_sizeDuringDistribution; }
98 void setSizeDuringDistribution(const LayoutUnit& sizeDuringDistribution)
100 ASSERT(sizeDuringDistribution >= 0);
101 m_sizeDuringDistribution = sizeDuringDistribution;
104 void growSizeDuringDistribution(const LayoutUnit& sizeDuringDistribution)
106 ASSERT(sizeDuringDistribution >= 0);
107 m_sizeDuringDistribution += sizeDuringDistribution;
110 bool infinitelyGrowable() const { return m_infinitelyGrowable; }
111 void setInfinitelyGrowable(bool infinitelyGrowable) { m_infinitelyGrowable = infinitelyGrowable; }
113 private:
114 bool isGrowthLimitBiggerThanBaseSize() const { return growthLimitIsInfinite() || m_growthLimit >= m_baseSize; }
116 void ensureGrowthLimitIsBiggerThanBaseSize()
118 if (m_growthLimit != infinity && m_growthLimit < m_baseSize)
119 m_growthLimit = m_baseSize;
122 LayoutUnit m_baseSize;
123 LayoutUnit m_growthLimit;
124 LayoutUnit m_plannedSize;
125 LayoutUnit m_sizeDuringDistribution;
126 bool m_infinitelyGrowable;
129 struct ContentAlignmentData {
130 STACK_ALLOCATED();
131 public:
132 ContentAlignmentData() {};
133 ContentAlignmentData(LayoutUnit position, LayoutUnit distribution)
134 : positionOffset(position)
135 , distributionOffset(distribution)
139 bool isValid() { return positionOffset >= 0 && distributionOffset >= 0; }
141 LayoutUnit positionOffset = -1;
142 LayoutUnit distributionOffset = -1;
145 struct GridTrackForNormalization {
146 GridTrackForNormalization(const GridTrack& track, double flex)
147 : m_track(&track)
148 , m_flex(flex)
149 , m_normalizedFlexValue(track.baseSize() / flex)
153 // Required by std::sort.
154 GridTrackForNormalization& operator=(const GridTrackForNormalization& o)
156 m_track = o.m_track;
157 m_flex = o.m_flex;
158 m_normalizedFlexValue = o.m_normalizedFlexValue;
159 return *this;
162 const GridTrack* m_track;
163 double m_flex;
164 LayoutUnit m_normalizedFlexValue;
167 enum TrackSizeRestriction {
168 AllowInfinity,
169 ForbidInfinity,
172 class LayoutGrid::GridIterator {
173 WTF_MAKE_NONCOPYABLE(GridIterator);
174 public:
175 // |direction| is the direction that is fixed to |fixedTrackIndex| so e.g
176 // GridIterator(m_grid, ForColumns, 1) will walk over the rows of the 2nd column.
177 GridIterator(const GridRepresentation& grid, GridTrackSizingDirection direction, size_t fixedTrackIndex, size_t varyingTrackIndex = 0)
178 : m_grid(grid)
179 , m_direction(direction)
180 , m_rowIndex((direction == ForColumns) ? varyingTrackIndex : fixedTrackIndex)
181 , m_columnIndex((direction == ForColumns) ? fixedTrackIndex : varyingTrackIndex)
182 , m_childIndex(0)
184 ASSERT(m_rowIndex < m_grid.size());
185 ASSERT(m_columnIndex < m_grid[0].size());
188 LayoutBox* nextGridItem()
190 ASSERT(!m_grid.isEmpty());
192 size_t& varyingTrackIndex = (m_direction == ForColumns) ? m_rowIndex : m_columnIndex;
193 const size_t endOfVaryingTrackIndex = (m_direction == ForColumns) ? m_grid.size() : m_grid[0].size();
194 for (; varyingTrackIndex < endOfVaryingTrackIndex; ++varyingTrackIndex) {
195 const GridCell& children = m_grid[m_rowIndex][m_columnIndex];
196 if (m_childIndex < children.size())
197 return children[m_childIndex++];
199 m_childIndex = 0;
201 return nullptr;
204 bool checkEmptyCells(size_t rowSpan, size_t columnSpan) const
206 // Ignore cells outside current grid as we will grow it later if needed.
207 size_t maxRows = std::min(m_rowIndex + rowSpan, m_grid.size());
208 size_t maxColumns = std::min(m_columnIndex + columnSpan, m_grid[0].size());
210 // This adds a O(N^2) behavior that shouldn't be a big deal as we expect spanning areas to be small.
211 for (size_t row = m_rowIndex; row < maxRows; ++row) {
212 for (size_t column = m_columnIndex; column < maxColumns; ++column) {
213 const GridCell& children = m_grid[row][column];
214 if (!children.isEmpty())
215 return false;
219 return true;
222 PassOwnPtr<GridCoordinate> nextEmptyGridArea(size_t fixedTrackSpan, size_t varyingTrackSpan)
224 ASSERT(!m_grid.isEmpty());
225 ASSERT(fixedTrackSpan >= 1 && varyingTrackSpan >= 1);
227 size_t rowSpan = (m_direction == ForColumns) ? varyingTrackSpan : fixedTrackSpan;
228 size_t columnSpan = (m_direction == ForColumns) ? fixedTrackSpan : varyingTrackSpan;
230 size_t& varyingTrackIndex = (m_direction == ForColumns) ? m_rowIndex : m_columnIndex;
231 const size_t endOfVaryingTrackIndex = (m_direction == ForColumns) ? m_grid.size() : m_grid[0].size();
232 for (; varyingTrackIndex < endOfVaryingTrackIndex; ++varyingTrackIndex) {
233 if (checkEmptyCells(rowSpan, columnSpan)) {
234 OwnPtr<GridCoordinate> result = adoptPtr(new GridCoordinate(GridSpan(m_rowIndex, m_rowIndex + rowSpan - 1), GridSpan(m_columnIndex, m_columnIndex + columnSpan - 1)));
235 // Advance the iterator to avoid an infinite loop where we would return the same grid area over and over.
236 ++varyingTrackIndex;
237 return result.release();
240 return nullptr;
243 private:
244 const GridRepresentation& m_grid;
245 GridTrackSizingDirection m_direction;
246 size_t m_rowIndex;
247 size_t m_columnIndex;
248 size_t m_childIndex;
251 struct LayoutGrid::GridSizingData {
252 WTF_MAKE_NONCOPYABLE(GridSizingData);
253 STACK_ALLOCATED();
254 public:
255 GridSizingData(size_t gridColumnCount, size_t gridRowCount)
256 : columnTracks(gridColumnCount)
257 , rowTracks(gridRowCount)
261 Vector<GridTrack> columnTracks;
262 Vector<GridTrack> rowTracks;
263 Vector<size_t> contentSizedTracksIndex;
265 // Performance optimization: hold onto these Vectors until the end of Layout to avoid repeated malloc / free.
266 Vector<GridTrack*> filteredTracks;
267 Vector<GridItemWithSpan> itemsSortedByIncreasingSpan;
268 Vector<GridTrack*> growBeyondGrowthLimitsTracks;
271 struct GridItemsSpanGroupRange {
272 Vector<GridItemWithSpan>::iterator rangeStart;
273 Vector<GridItemWithSpan>::iterator rangeEnd;
276 LayoutGrid::LayoutGrid(Element* element)
277 : LayoutBlock(element)
278 , m_gridIsDirty(true)
279 , m_orderIterator(this)
281 ASSERT(!childrenInline());
284 LayoutGrid::~LayoutGrid()
288 void LayoutGrid::addChild(LayoutObject* newChild, LayoutObject* beforeChild)
290 LayoutBlock::addChild(newChild, beforeChild);
292 // The grid needs to be recomputed as it might contain auto-placed items that will change their position.
293 dirtyGrid();
296 void LayoutGrid::removeChild(LayoutObject* child)
298 LayoutBlock::removeChild(child);
300 // The grid needs to be recomputed as it might contain auto-placed items that will change their position.
301 dirtyGrid();
304 void LayoutGrid::styleDidChange(StyleDifference diff, const ComputedStyle* oldStyle)
306 LayoutBlock::styleDidChange(diff, oldStyle);
307 if (!oldStyle)
308 return;
310 // FIXME: The following checks could be narrowed down if we kept track of which type of grid items we have:
311 // - explicit grid size changes impact negative explicitely positioned and auto-placed grid items.
312 // - named grid lines only impact grid items with named grid lines.
313 // - auto-flow changes only impacts auto-placed children.
315 if (explicitGridDidResize(*oldStyle)
316 || namedGridLinesDefinitionDidChange(*oldStyle)
317 || oldStyle->gridAutoFlow() != styleRef().gridAutoFlow())
318 dirtyGrid();
321 bool LayoutGrid::explicitGridDidResize(const ComputedStyle& oldStyle) const
323 return oldStyle.gridTemplateColumns().size() != styleRef().gridTemplateColumns().size()
324 || oldStyle.gridTemplateRows().size() != styleRef().gridTemplateRows().size();
327 bool LayoutGrid::namedGridLinesDefinitionDidChange(const ComputedStyle& oldStyle) const
329 return oldStyle.namedGridRowLines() != styleRef().namedGridRowLines()
330 || oldStyle.namedGridColumnLines() != styleRef().namedGridColumnLines();
333 void LayoutGrid::layoutBlock(bool relayoutChildren)
335 ASSERT(needsLayout());
337 if (!relayoutChildren && simplifiedLayout())
338 return;
340 // FIXME: Much of this method is boiler plate that matches LayoutBox::layoutBlock and Layout*FlexibleBox::layoutBlock.
341 // It would be nice to refactor some of the duplicate code.
343 // LayoutState needs this deliberate scope to pop before updating scroll information (which
344 // may trigger relayout).
345 LayoutState state(*this, locationOffset());
347 LayoutSize previousSize = size();
349 setLogicalHeight(0);
350 updateLogicalWidth();
352 TextAutosizer::LayoutScope textAutosizerLayoutScope(this);
354 layoutGridItems();
356 LayoutUnit oldClientAfterEdge = clientLogicalBottom();
357 updateLogicalHeight();
359 if (size() != previousSize)
360 relayoutChildren = true;
362 layoutPositionedObjects(relayoutChildren || isDocumentElement());
364 computeOverflow(oldClientAfterEdge);
367 updateLayerTransformAfterLayout();
368 updateScrollInfoAfterLayout();
370 clearNeedsLayout();
373 void LayoutGrid::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
375 const_cast<LayoutGrid*>(this)->placeItemsOnGrid();
377 GridSizingData sizingData(gridColumnCount(), gridRowCount());
378 LayoutUnit availableLogicalSpace = 0;
379 const_cast<LayoutGrid*>(this)->computeUsedBreadthOfGridTracks(ForColumns, sizingData, availableLogicalSpace);
381 for (const auto& column : sizingData.columnTracks) {
382 const LayoutUnit& minTrackBreadth = column.baseSize();
383 const LayoutUnit& maxTrackBreadth = column.growthLimit();
385 minLogicalWidth += minTrackBreadth;
386 maxLogicalWidth += maxTrackBreadth;
389 LayoutUnit scrollbarWidth = intrinsicScrollbarLogicalWidth();
390 minLogicalWidth += scrollbarWidth;
391 maxLogicalWidth += scrollbarWidth;
394 bool LayoutGrid::gridElementIsShrinkToFit()
396 return isFloatingOrOutOfFlowPositioned();
399 void LayoutGrid::computeUsedBreadthOfGridTracks(GridTrackSizingDirection direction, GridSizingData& sizingData, LayoutUnit& freeSpace)
401 const LayoutUnit initialFreeSpace = freeSpace;
402 Vector<GridTrack>& tracks = (direction == ForColumns) ? sizingData.columnTracks : sizingData.rowTracks;
403 Vector<size_t> flexibleSizedTracksIndex;
404 sizingData.contentSizedTracksIndex.shrink(0);
406 // 1. Initialize per Grid track variables.
407 for (size_t i = 0; i < tracks.size(); ++i) {
408 GridTrack& track = tracks[i];
409 GridTrackSize trackSize = gridTrackSize(direction, i);
410 const GridLength& minTrackBreadth = trackSize.minTrackBreadth();
411 const GridLength& maxTrackBreadth = trackSize.maxTrackBreadth();
413 track.setBaseSize(computeUsedBreadthOfMinLength(direction, minTrackBreadth));
414 track.setGrowthLimit(computeUsedBreadthOfMaxLength(direction, maxTrackBreadth, track.baseSize()));
415 track.setInfinitelyGrowable(false);
417 if (trackSize.isContentSized())
418 sizingData.contentSizedTracksIndex.append(i);
419 if (trackSize.maxTrackBreadth().isFlex())
420 flexibleSizedTracksIndex.append(i);
423 // 2. Resolve content-based TrackSizingFunctions.
424 if (!sizingData.contentSizedTracksIndex.isEmpty())
425 resolveContentBasedTrackSizingFunctions(direction, sizingData);
427 for (const auto& track: tracks) {
428 ASSERT(!track.infiniteGrowthPotential());
429 freeSpace -= track.baseSize();
432 const bool hasUndefinedRemainingSpace = (direction == ForRows) ? style()->logicalHeight().isAuto() : gridElementIsShrinkToFit();
434 if (!hasUndefinedRemainingSpace && freeSpace <= 0)
435 return;
437 // 3. Grow all Grid tracks in GridTracks from their baseSize up to their growthLimit value until freeSpace is exhausted.
438 const size_t tracksSize = tracks.size();
439 if (!hasUndefinedRemainingSpace) {
440 Vector<GridTrack*> tracksForDistribution(tracksSize);
441 for (size_t i = 0; i < tracksSize; ++i) {
442 tracksForDistribution[i] = tracks.data() + i;
443 tracksForDistribution[i]->setPlannedSize(tracksForDistribution[i]->baseSize());
446 distributeSpaceToTracks<MaximizeTracks>(tracksForDistribution, nullptr, sizingData, freeSpace);
448 for (auto* track : tracksForDistribution)
449 track->setBaseSize(track->plannedSize());
450 } else {
451 for (auto& track : tracks)
452 track.setBaseSize(track.growthLimit());
455 if (flexibleSizedTracksIndex.isEmpty())
456 return;
458 // 4. Grow all Grid tracks having a fraction as the MaxTrackSizingFunction.
459 double normalizedFractionBreadth = 0;
460 if (!hasUndefinedRemainingSpace) {
461 normalizedFractionBreadth = computeNormalizedFractionBreadth(tracks, GridSpan(0, tracks.size() - 1), direction, initialFreeSpace);
462 } else {
463 for (const auto& trackIndex : flexibleSizedTracksIndex) {
464 GridTrackSize trackSize = gridTrackSize(direction, trackIndex);
465 normalizedFractionBreadth = std::max(normalizedFractionBreadth, tracks[trackIndex].baseSize() / trackSize.maxTrackBreadth().flex());
468 for (size_t i = 0; i < flexibleSizedTracksIndex.size(); ++i) {
469 GridIterator iterator(m_grid, direction, flexibleSizedTracksIndex[i]);
470 while (LayoutBox* gridItem = iterator.nextGridItem()) {
471 const GridCoordinate coordinate = cachedGridCoordinate(*gridItem);
472 const GridSpan span = (direction == ForColumns) ? coordinate.columns : coordinate.rows;
474 // Do not include already processed items.
475 if (i > 0 && span.resolvedInitialPosition.toInt() <= flexibleSizedTracksIndex[i - 1])
476 continue;
478 double itemNormalizedFlexBreadth = computeNormalizedFractionBreadth(tracks, span, direction, maxContentForChild(*gridItem, direction, sizingData.columnTracks));
479 normalizedFractionBreadth = std::max(normalizedFractionBreadth, itemNormalizedFlexBreadth);
484 for (const auto& trackIndex : flexibleSizedTracksIndex) {
485 GridTrackSize trackSize = gridTrackSize(direction, trackIndex);
487 LayoutUnit baseSize = std::max<LayoutUnit>(tracks[trackIndex].baseSize(), normalizedFractionBreadth * trackSize.maxTrackBreadth().flex());
488 tracks[trackIndex].setBaseSize(baseSize);
489 freeSpace -= baseSize;
492 // FIXME: Should ASSERT flexible tracks exhaust the freeSpace ? (see issue 739613002).
495 LayoutUnit LayoutGrid::computeUsedBreadthOfMinLength(GridTrackSizingDirection direction, const GridLength& gridLength) const
497 if (gridLength.isFlex())
498 return 0;
500 const Length& trackLength = gridLength.length();
501 if (trackLength.isSpecified())
502 return computeUsedBreadthOfSpecifiedLength(direction, trackLength);
504 ASSERT(trackLength.isMinContent() || trackLength.isAuto() || trackLength.isMaxContent());
505 return 0;
508 LayoutUnit LayoutGrid::computeUsedBreadthOfMaxLength(GridTrackSizingDirection direction, const GridLength& gridLength, LayoutUnit usedBreadth) const
510 if (gridLength.isFlex())
511 return usedBreadth;
513 const Length& trackLength = gridLength.length();
514 if (trackLength.isSpecified()) {
515 LayoutUnit computedBreadth = computeUsedBreadthOfSpecifiedLength(direction, trackLength);
516 ASSERT(computedBreadth != infinity);
517 return computedBreadth;
520 ASSERT(trackLength.isMinContent() || trackLength.isAuto() || trackLength.isMaxContent());
521 return infinity;
524 LayoutUnit LayoutGrid::computeUsedBreadthOfSpecifiedLength(GridTrackSizingDirection direction, const Length& trackLength) const
526 ASSERT(trackLength.isSpecified());
527 // FIXME: The -1 here should be replaced by whatever the intrinsic height of the grid is.
528 return valueForLength(trackLength, direction == ForColumns ? contentLogicalWidth() : std::max(LayoutUnit(), computeContentLogicalHeight(MainOrPreferredSize, style()->logicalHeight(), -1)));
531 static bool sortByGridNormalizedFlexValue(const GridTrackForNormalization& track1, const GridTrackForNormalization& track2)
533 return track1.m_normalizedFlexValue < track2.m_normalizedFlexValue;
536 double LayoutGrid::computeNormalizedFractionBreadth(Vector<GridTrack>& tracks, const GridSpan& tracksSpan, GridTrackSizingDirection direction, LayoutUnit spaceToFill) const
538 LayoutUnit allocatedSpace;
539 Vector<GridTrackForNormalization> tracksForNormalization;
540 for (const auto& resolvedPosition : tracksSpan) {
541 GridTrack& track = tracks[resolvedPosition.toInt()];
542 allocatedSpace += track.baseSize();
544 GridTrackSize trackSize = gridTrackSize(direction, resolvedPosition.toInt());
545 if (!trackSize.maxTrackBreadth().isFlex())
546 continue;
548 tracksForNormalization.append(GridTrackForNormalization(track, trackSize.maxTrackBreadth().flex()));
551 // The function is not called if we don't have <flex> grid tracks
552 ASSERT(!tracksForNormalization.isEmpty());
554 std::sort(tracksForNormalization.begin(), tracksForNormalization.end(), sortByGridNormalizedFlexValue);
556 // These values work together: as we walk over our grid tracks, we increase fractionValueBasedOnGridItemsRatio
557 // to match a grid track's usedBreadth to <flex> ratio until the total fractions sized grid tracks wouldn't
558 // fit into availableLogicalSpaceIgnoringFractionTracks.
559 double accumulatedFractions = 0;
560 LayoutUnit fractionValueBasedOnGridItemsRatio = 0;
561 LayoutUnit availableLogicalSpaceIgnoringFractionTracks = spaceToFill - allocatedSpace;
563 for (const auto& track : tracksForNormalization) {
564 if (track.m_normalizedFlexValue > fractionValueBasedOnGridItemsRatio) {
565 // If the normalized flex value (we ordered |tracksForNormalization| by increasing normalized flex value)
566 // will make us overflow our container, then stop. We have the previous step's ratio is the best fit.
567 if (track.m_normalizedFlexValue * accumulatedFractions > availableLogicalSpaceIgnoringFractionTracks)
568 break;
570 fractionValueBasedOnGridItemsRatio = track.m_normalizedFlexValue;
573 accumulatedFractions += track.m_flex;
574 // This item was processed so we re-add its used breadth to the available space to accurately count the remaining space.
575 availableLogicalSpaceIgnoringFractionTracks += track.m_track->baseSize();
578 // Let flex factor sum be the sum of the flex factors of the flexible tracks. If this value
579 // is less than 1, set it to 1 instead.
580 if (accumulatedFractions < 1)
581 return availableLogicalSpaceIgnoringFractionTracks;
583 return availableLogicalSpaceIgnoringFractionTracks / accumulatedFractions;
586 bool LayoutGrid::hasDefiniteLogicalSize(GridTrackSizingDirection direction) const
588 return (direction == ForRows) ? hasDefiniteLogicalHeight() : hasDefiniteLogicalWidth();
591 GridTrackSize LayoutGrid::gridTrackSize(GridTrackSizingDirection direction, size_t i) const
593 bool isForColumns = direction == ForColumns;
594 const Vector<GridTrackSize>& trackStyles = isForColumns ? style()->gridTemplateColumns() : style()->gridTemplateRows();
595 const GridTrackSize& trackSize = (i >= trackStyles.size()) ? (isForColumns ? style()->gridAutoColumns() : style()->gridAutoRows()) : trackStyles[i];
597 GridLength minTrackBreadth = trackSize.minTrackBreadth();
598 GridLength maxTrackBreadth = trackSize.maxTrackBreadth();
600 // If the logical width/height of the grid container is indefinite, percentage values are treated as <auto> (or in
601 // the case of minmax() as min-content for the first position and max-content for the second).
602 if (minTrackBreadth.hasPercentage() || maxTrackBreadth.hasPercentage()) {
603 if (!hasDefiniteLogicalSize(direction)) {
604 if (minTrackBreadth.hasPercentage())
605 minTrackBreadth = Length(MinContent);
606 if (maxTrackBreadth.hasPercentage())
607 maxTrackBreadth = Length(MaxContent);
611 return GridTrackSize(minTrackBreadth, maxTrackBreadth);
614 LayoutUnit LayoutGrid::logicalHeightForChild(LayoutBox& child, Vector<GridTrack>& columnTracks)
616 SubtreeLayoutScope layoutScope(child);
617 LayoutUnit oldOverrideContainingBlockContentLogicalWidth = child.hasOverrideContainingBlockLogicalWidth() ? child.overrideContainingBlockContentLogicalWidth() : LayoutUnit();
618 LayoutUnit overrideContainingBlockContentLogicalWidth = gridAreaBreadthForChild(child, ForColumns, columnTracks);
619 if (child.hasRelativeLogicalHeight() || oldOverrideContainingBlockContentLogicalWidth != overrideContainingBlockContentLogicalWidth) {
620 layoutScope.setNeedsLayout(&child, LayoutInvalidationReason::GridChanged);
623 bool hasOverrideHeight = child.hasOverrideLogicalContentHeight();
624 // We need to clear the stretched height to properly compute logical height during layout.
625 if (hasOverrideHeight && child.needsLayout())
626 child.clearOverrideLogicalContentHeight();
628 child.setOverrideContainingBlockContentLogicalWidth(overrideContainingBlockContentLogicalWidth);
629 // If |child| has a relative logical height, we shouldn't let it override its intrinsic height, which is
630 // what we are interested in here. Thus we need to set the override logical height to -1 (no possible resolution).
631 if (child.hasRelativeLogicalHeight())
632 child.setOverrideContainingBlockContentLogicalHeight(-1);
633 child.layoutIfNeeded();
634 // If the child was stretched we should use its intrinsic height.
635 return (hasOverrideHeight ? childIntrinsicHeight(child) : child.logicalHeight()) + child.marginLogicalHeight();
638 LayoutUnit LayoutGrid::minSizeForChild(LayoutBox& child, GridTrackSizingDirection direction, Vector<GridTrack>& columnTracks)
640 bool hasOrthogonalWritingMode = child.isHorizontalWritingMode() != isHorizontalWritingMode();
641 // TODO(svillar): Properly support orthogonal writing mode.
642 if (hasOrthogonalWritingMode)
643 return LayoutUnit();
645 const Length& childMinSize = direction == ForColumns ? child.style()->logicalMinWidth() : child.style()->logicalMinHeight();
646 if (childMinSize.isAuto()) {
647 // TODO(svillar): Implement intrinsic aspect ratio support (transferred size in specs).
648 return minContentForChild(child, direction, columnTracks);
651 if (direction == ForColumns)
652 return child.computeLogicalWidthUsing(MinSize, childMinSize, contentLogicalWidth(), this);
654 return child.computeContentLogicalHeight(MinSize, childMinSize, child.logicalHeight()) + child.scrollbarLogicalHeight();
657 LayoutUnit LayoutGrid::minContentForChild(LayoutBox& child, GridTrackSizingDirection direction, Vector<GridTrack>& columnTracks)
659 bool hasOrthogonalWritingMode = child.isHorizontalWritingMode() != isHorizontalWritingMode();
660 // FIXME: Properly support orthogonal writing mode.
661 if (hasOrthogonalWritingMode)
662 return 0;
664 if (direction == ForColumns) {
665 // If |child| has a relative logical width, we shouldn't let it override its intrinsic width, which is
666 // what we are interested in here. Thus we need to set the override logical width to -1 (no possible resolution).
667 if (child.hasRelativeLogicalWidth())
668 child.setOverrideContainingBlockContentLogicalWidth(-1);
670 // FIXME: It's unclear if we should return the intrinsic width or the preferred width.
671 // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html
672 return child.minPreferredLogicalWidth() + marginIntrinsicLogicalWidthForChild(child);
675 return logicalHeightForChild(child, columnTracks);
678 LayoutUnit LayoutGrid::maxContentForChild(LayoutBox& child, GridTrackSizingDirection direction, Vector<GridTrack>& columnTracks)
680 bool hasOrthogonalWritingMode = child.isHorizontalWritingMode() != isHorizontalWritingMode();
681 // FIXME: Properly support orthogonal writing mode.
682 if (hasOrthogonalWritingMode)
683 return LayoutUnit();
685 if (direction == ForColumns) {
686 // If |child| has a relative logical width, we shouldn't let it override its intrinsic width, which is
687 // what we are interested in here. Thus we need to set the override logical width to -1 (no possible resolution).
688 if (child.hasRelativeLogicalWidth())
689 child.setOverrideContainingBlockContentLogicalWidth(-1);
691 // FIXME: It's unclear if we should return the intrinsic width or the preferred width.
692 // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html
693 return child.maxPreferredLogicalWidth() + marginIntrinsicLogicalWidthForChild(child);
696 return logicalHeightForChild(child, columnTracks);
699 // We're basically using a class instead of a std::pair for two reasons. First of all, accessing gridItem() or
700 // coordinate() is much more self-explanatory that using .first or .second members in the pair. Secondly the class
701 // allows us to precompute the value of the span, something which is quite convenient for the sorting. Having a
702 // std::pair<LayoutBox*, size_t> does not work either because we still need the GridCoordinate so we'd have to add an
703 // extra hash lookup for each item at the beginning of LayoutGrid::resolveContentBasedTrackSizingFunctionsForItems().
704 class GridItemWithSpan {
705 public:
706 GridItemWithSpan(LayoutBox& gridItem, const GridCoordinate& coordinate, GridTrackSizingDirection direction)
707 : m_gridItem(&gridItem)
708 , m_coordinate(coordinate)
710 const GridSpan& span = (direction == ForRows) ? coordinate.rows : coordinate.columns;
711 m_span = span.resolvedFinalPosition.toInt() - span.resolvedInitialPosition.toInt() + 1;
714 LayoutBox& gridItem() const { return *m_gridItem; }
715 GridCoordinate coordinate() const { return m_coordinate; }
716 #if ENABLE(ASSERT)
717 size_t span() const { return m_span; }
718 #endif
720 bool operator<(const GridItemWithSpan other) const { return m_span < other.m_span; }
722 private:
723 LayoutBox* m_gridItem;
724 GridCoordinate m_coordinate;
725 size_t m_span;
728 bool LayoutGrid::spanningItemCrossesFlexibleSizedTracks(const GridCoordinate& coordinate, GridTrackSizingDirection direction) const
730 const GridResolvedPosition initialTrackPosition = (direction == ForColumns) ? coordinate.columns.resolvedInitialPosition : coordinate.rows.resolvedInitialPosition;
731 const GridResolvedPosition finalTrackPosition = (direction == ForColumns) ? coordinate.columns.resolvedFinalPosition : coordinate.rows.resolvedFinalPosition;
733 for (GridResolvedPosition trackPosition = initialTrackPosition; trackPosition <= finalTrackPosition; ++trackPosition) {
734 const GridTrackSize& trackSize = gridTrackSize(direction, trackPosition.toInt());
735 if (trackSize.minTrackBreadth().isFlex() || trackSize.maxTrackBreadth().isFlex())
736 return true;
739 return false;
742 static inline size_t integerSpanForDirection(const GridCoordinate& coordinate, GridTrackSizingDirection direction)
744 return (direction == ForRows) ? coordinate.rows.integerSpan() : coordinate.columns.integerSpan();
747 void LayoutGrid::resolveContentBasedTrackSizingFunctions(GridTrackSizingDirection direction, GridSizingData& sizingData)
749 sizingData.itemsSortedByIncreasingSpan.shrink(0);
750 HashSet<LayoutBox*> itemsSet;
751 for (const auto& trackIndex : sizingData.contentSizedTracksIndex) {
752 GridIterator iterator(m_grid, direction, trackIndex);
753 GridTrack& track = (direction == ForColumns) ? sizingData.columnTracks[trackIndex] : sizingData.rowTracks[trackIndex];
754 while (LayoutBox* gridItem = iterator.nextGridItem()) {
755 if (itemsSet.add(gridItem).isNewEntry) {
756 const GridCoordinate& coordinate = cachedGridCoordinate(*gridItem);
757 if (integerSpanForDirection(coordinate, direction) == 1) {
758 resolveContentBasedTrackSizingFunctionsForNonSpanningItems(direction, coordinate, *gridItem, track, sizingData.columnTracks);
759 } else if (!spanningItemCrossesFlexibleSizedTracks(coordinate, direction)) {
760 sizingData.itemsSortedByIncreasingSpan.append(GridItemWithSpan(*gridItem, coordinate, direction));
765 std::sort(sizingData.itemsSortedByIncreasingSpan.begin(), sizingData.itemsSortedByIncreasingSpan.end());
767 auto it = sizingData.itemsSortedByIncreasingSpan.begin();
768 auto end = sizingData.itemsSortedByIncreasingSpan.end();
769 while (it != end) {
770 GridItemsSpanGroupRange spanGroupRange = { it, std::upper_bound(it, end, *it) };
771 resolveContentBasedTrackSizingFunctionsForItems<ResolveIntrinsicMinimums>(direction, sizingData, spanGroupRange);
772 resolveContentBasedTrackSizingFunctionsForItems<ResolveContentBasedMinimums>(direction, sizingData, spanGroupRange);
773 resolveContentBasedTrackSizingFunctionsForItems<ResolveMaxContentMinimums>(direction, sizingData, spanGroupRange);
774 resolveContentBasedTrackSizingFunctionsForItems<ResolveIntrinsicMaximums>(direction, sizingData, spanGroupRange);
775 resolveContentBasedTrackSizingFunctionsForItems<ResolveMaxContentMaximums>(direction, sizingData, spanGroupRange);
776 it = spanGroupRange.rangeEnd;
779 for (const auto& trackIndex : sizingData.contentSizedTracksIndex) {
780 GridTrack& track = (direction == ForColumns) ? sizingData.columnTracks[trackIndex] : sizingData.rowTracks[trackIndex];
781 if (track.growthLimitIsInfinite())
782 track.setGrowthLimit(track.baseSize());
786 void LayoutGrid::resolveContentBasedTrackSizingFunctionsForNonSpanningItems(GridTrackSizingDirection direction, const GridCoordinate& coordinate, LayoutBox& gridItem, GridTrack& track, Vector<GridTrack>& columnTracks)
788 const GridResolvedPosition trackPosition = (direction == ForColumns) ? coordinate.columns.resolvedInitialPosition : coordinate.rows.resolvedInitialPosition;
789 GridTrackSize trackSize = gridTrackSize(direction, trackPosition.toInt());
791 if (trackSize.hasMinContentMinTrackBreadth())
792 track.setBaseSize(std::max(track.baseSize(), minContentForChild(gridItem, direction, columnTracks)));
793 else if (trackSize.hasMaxContentMinTrackBreadth())
794 track.setBaseSize(std::max(track.baseSize(), maxContentForChild(gridItem, direction, columnTracks)));
795 else if (trackSize.hasAutoMinTrackBreadth())
796 track.setBaseSize(std::max(track.baseSize(), minSizeForChild(gridItem, direction, columnTracks)));
798 if (trackSize.hasMinContentMaxTrackBreadth())
799 track.setGrowthLimit(std::max(track.growthLimit(), minContentForChild(gridItem, direction, columnTracks)));
800 else if (trackSize.hasMaxContentOrAutoMaxTrackBreadth())
801 track.setGrowthLimit(std::max(track.growthLimit(), maxContentForChild(gridItem, direction, columnTracks)));
804 static const LayoutUnit& trackSizeForTrackSizeComputationPhase(TrackSizeComputationPhase phase, const GridTrack& track, TrackSizeRestriction restriction)
806 switch (phase) {
807 case ResolveIntrinsicMinimums:
808 case ResolveContentBasedMinimums:
809 case ResolveMaxContentMinimums:
810 case MaximizeTracks:
811 return track.baseSize();
812 case ResolveIntrinsicMaximums:
813 case ResolveMaxContentMaximums:
814 const LayoutUnit& growthLimit = track.growthLimit();
815 if (restriction == AllowInfinity)
816 return growthLimit;
817 return growthLimit == infinity ? track.baseSize() : growthLimit;
820 ASSERT_NOT_REACHED();
821 return track.baseSize();
824 static bool shouldProcessTrackForTrackSizeComputationPhase(TrackSizeComputationPhase phase, const GridTrackSize& trackSize)
826 switch (phase) {
827 case ResolveIntrinsicMinimums:
828 return trackSize.hasIntrinsicMinTrackBreadth();
829 case ResolveContentBasedMinimums:
830 return trackSize.hasMinOrMaxContentMinTrackBreadth();
831 case ResolveMaxContentMinimums:
832 return trackSize.hasMaxContentMinTrackBreadth();
833 case ResolveIntrinsicMaximums:
834 return trackSize.hasMinOrMaxContentMaxTrackBreadth();
835 case ResolveMaxContentMaximums:
836 return trackSize.hasMaxContentOrAutoMaxTrackBreadth();
837 case MaximizeTracks:
838 ASSERT_NOT_REACHED();
839 return false;
842 ASSERT_NOT_REACHED();
843 return false;
846 static bool trackShouldGrowBeyondGrowthLimitsForTrackSizeComputationPhase(TrackSizeComputationPhase phase, const GridTrackSize& trackSize)
848 switch (phase) {
849 case ResolveIntrinsicMinimums:
850 case ResolveContentBasedMinimums:
851 return trackSize.hasAutoOrMinContentMinTrackBreadthAndIntrinsicMaxTrackBreadth();
852 case ResolveMaxContentMinimums:
853 return trackSize.hasMaxContentMinTrackBreadthAndMaxContentMaxTrackBreadth();
854 case ResolveIntrinsicMaximums:
855 case ResolveMaxContentMaximums:
856 return true;
857 case MaximizeTracks:
858 ASSERT_NOT_REACHED();
859 return false;
862 ASSERT_NOT_REACHED();
863 return false;
866 static void markAsInfinitelyGrowableForTrackSizeComputationPhase(TrackSizeComputationPhase phase, GridTrack& track)
868 switch (phase) {
869 case ResolveIntrinsicMinimums:
870 case ResolveContentBasedMinimums:
871 case ResolveMaxContentMinimums:
872 return;
873 case ResolveIntrinsicMaximums:
874 if (trackSizeForTrackSizeComputationPhase(phase, track, AllowInfinity) == infinity && track.plannedSize() != infinity)
875 track.setInfinitelyGrowable(true);
876 return;
877 case ResolveMaxContentMaximums:
878 if (track.infinitelyGrowable())
879 track.setInfinitelyGrowable(false);
880 return;
881 case MaximizeTracks:
882 ASSERT_NOT_REACHED();
883 return;
886 ASSERT_NOT_REACHED();
889 static void updateTrackSizeForTrackSizeComputationPhase(TrackSizeComputationPhase phase, GridTrack& track)
891 switch (phase) {
892 case ResolveIntrinsicMinimums:
893 case ResolveContentBasedMinimums:
894 case ResolveMaxContentMinimums:
895 track.setBaseSize(track.plannedSize());
896 return;
897 case ResolveIntrinsicMaximums:
898 case ResolveMaxContentMaximums:
899 track.setGrowthLimit(track.plannedSize());
900 return;
901 case MaximizeTracks:
902 ASSERT_NOT_REACHED();
903 return;
906 ASSERT_NOT_REACHED();
909 LayoutUnit LayoutGrid::currentItemSizeForTrackSizeComputationPhase(TrackSizeComputationPhase phase, LayoutBox& gridItem, GridTrackSizingDirection direction, Vector<GridTrack>& columnTracks)
911 switch (phase) {
912 case ResolveIntrinsicMinimums:
913 return minSizeForChild(gridItem, direction, columnTracks);
914 case ResolveContentBasedMinimums:
915 case ResolveIntrinsicMaximums:
916 return minContentForChild(gridItem, direction, columnTracks);
917 case ResolveMaxContentMinimums:
918 case ResolveMaxContentMaximums:
919 return maxContentForChild(gridItem, direction, columnTracks);
920 case MaximizeTracks:
921 ASSERT_NOT_REACHED();
922 return 0;
925 ASSERT_NOT_REACHED();
926 return 0;
929 template <TrackSizeComputationPhase phase>
930 void LayoutGrid::resolveContentBasedTrackSizingFunctionsForItems(GridTrackSizingDirection direction, GridSizingData& sizingData, const GridItemsSpanGroupRange& gridItemsWithSpan)
932 Vector<GridTrack>& tracks = (direction == ForColumns) ? sizingData.columnTracks : sizingData.rowTracks;
933 for (const auto& trackIndex : sizingData.contentSizedTracksIndex) {
934 GridTrack& track = tracks[trackIndex];
935 track.setPlannedSize(trackSizeForTrackSizeComputationPhase(phase, track, AllowInfinity));
938 for (auto it = gridItemsWithSpan.rangeStart; it != gridItemsWithSpan.rangeEnd; ++it) {
939 GridItemWithSpan& gridItemWithSpan = *it;
940 ASSERT(gridItemWithSpan.span() > 1);
941 const GridCoordinate coordinate = gridItemWithSpan.coordinate();
942 const GridSpan& itemSpan = (direction == ForColumns) ? coordinate.columns : coordinate.rows;
944 sizingData.growBeyondGrowthLimitsTracks.shrink(0);
945 sizingData.filteredTracks.shrink(0);
946 LayoutUnit spanningTracksSize;
947 for (const auto& trackPosition : itemSpan) {
948 GridTrackSize trackSize = gridTrackSize(direction, trackPosition.toInt());
949 GridTrack& track = (direction == ForColumns) ? sizingData.columnTracks[trackPosition.toInt()] : sizingData.rowTracks[trackPosition.toInt()];
950 spanningTracksSize += trackSizeForTrackSizeComputationPhase(phase, track, ForbidInfinity);
951 if (!shouldProcessTrackForTrackSizeComputationPhase(phase, trackSize))
952 continue;
954 sizingData.filteredTracks.append(&track);
956 if (trackShouldGrowBeyondGrowthLimitsForTrackSizeComputationPhase(phase, trackSize))
957 sizingData.growBeyondGrowthLimitsTracks.append(&track);
960 if (sizingData.filteredTracks.isEmpty())
961 continue;
963 LayoutUnit extraSpace = currentItemSizeForTrackSizeComputationPhase(phase, gridItemWithSpan.gridItem(), direction, sizingData.columnTracks) - spanningTracksSize;
964 extraSpace = std::max<LayoutUnit>(extraSpace, 0);
965 auto& tracksToGrowBeyondGrowthLimits = sizingData.growBeyondGrowthLimitsTracks.isEmpty() ? sizingData.filteredTracks : sizingData.growBeyondGrowthLimitsTracks;
966 distributeSpaceToTracks<phase>(sizingData.filteredTracks, &tracksToGrowBeyondGrowthLimits, sizingData, extraSpace);
969 for (const auto& trackIndex : sizingData.contentSizedTracksIndex) {
970 GridTrack& track = tracks[trackIndex];
971 markAsInfinitelyGrowableForTrackSizeComputationPhase(phase, track);
972 updateTrackSizeForTrackSizeComputationPhase(phase, track);
976 static bool sortByGridTrackGrowthPotential(const GridTrack* track1, const GridTrack* track2)
978 // This check ensures that we respect the irreflexivity property of the strict weak ordering required by std::sort
979 // (forall x: NOT x < x).
980 if (track1->infiniteGrowthPotential() && track2->infiniteGrowthPotential())
981 return false;
983 if (track1->infiniteGrowthPotential() || track2->infiniteGrowthPotential())
984 return track2->infiniteGrowthPotential();
986 return (track1->growthLimit() - track1->baseSize()) < (track2->growthLimit() - track2->baseSize());
989 template <TrackSizeComputationPhase phase>
990 void LayoutGrid::distributeSpaceToTracks(Vector<GridTrack*>& tracks, const Vector<GridTrack*>* growBeyondGrowthLimitsTracks, GridSizingData& sizingData, LayoutUnit& availableLogicalSpace)
992 ASSERT(availableLogicalSpace >= 0);
994 for (auto* track : tracks)
995 track->setSizeDuringDistribution(trackSizeForTrackSizeComputationPhase(phase, *track, ForbidInfinity));
997 if (availableLogicalSpace > 0) {
998 std::sort(tracks.begin(), tracks.end(), sortByGridTrackGrowthPotential);
1000 size_t tracksSize = tracks.size();
1001 for (size_t i = 0; i < tracksSize; ++i) {
1002 GridTrack& track = *tracks[i];
1003 LayoutUnit availableLogicalSpaceShare = availableLogicalSpace / (tracksSize - i);
1004 const LayoutUnit& trackBreadth = trackSizeForTrackSizeComputationPhase(phase, track, ForbidInfinity);
1005 LayoutUnit growthShare = track.infiniteGrowthPotential() ? availableLogicalSpaceShare : std::min(availableLogicalSpaceShare, track.growthLimit() - trackBreadth);
1006 ASSERT_WITH_MESSAGE(growthShare >= 0, "We must never shrink any grid track or else we can't guarantee we abide by our min-sizing function.");
1007 track.growSizeDuringDistribution(growthShare);
1008 availableLogicalSpace -= growthShare;
1012 if (availableLogicalSpace > 0 && growBeyondGrowthLimitsTracks) {
1013 size_t tracksGrowingAboveMaxBreadthSize = growBeyondGrowthLimitsTracks->size();
1014 for (size_t i = 0; i < tracksGrowingAboveMaxBreadthSize; ++i) {
1015 GridTrack* track = growBeyondGrowthLimitsTracks->at(i);
1016 LayoutUnit growthShare = availableLogicalSpace / (tracksGrowingAboveMaxBreadthSize - i);
1017 track->growSizeDuringDistribution(growthShare);
1018 availableLogicalSpace -= growthShare;
1022 for (auto* track : tracks)
1023 track->setPlannedSize(track->plannedSize() == infinity ? track->sizeDuringDistribution() : std::max(track->plannedSize(), track->sizeDuringDistribution()));
1026 #if ENABLE(ASSERT)
1027 bool LayoutGrid::tracksAreWiderThanMinTrackBreadth(GridTrackSizingDirection direction, const Vector<GridTrack>& tracks)
1029 for (size_t i = 0; i < tracks.size(); ++i) {
1030 GridTrackSize trackSize = gridTrackSize(direction, i);
1031 const GridLength& minTrackBreadth = trackSize.minTrackBreadth();
1032 if (computeUsedBreadthOfMinLength(direction, minTrackBreadth) > tracks[i].baseSize())
1033 return false;
1035 return true;
1037 #endif
1039 void LayoutGrid::ensureGridSize(size_t maximumRowIndex, size_t maximumColumnIndex)
1041 const size_t oldRowSize = gridRowCount();
1042 if (maximumRowIndex >= oldRowSize) {
1043 m_grid.grow(maximumRowIndex + 1);
1044 for (size_t row = oldRowSize; row < gridRowCount(); ++row)
1045 m_grid[row].grow(gridColumnCount());
1048 if (maximumColumnIndex >= gridColumnCount()) {
1049 for (size_t row = 0; row < gridRowCount(); ++row)
1050 m_grid[row].grow(maximumColumnIndex + 1);
1054 void LayoutGrid::insertItemIntoGrid(LayoutBox& child, const GridCoordinate& coordinate)
1056 ensureGridSize(coordinate.rows.resolvedFinalPosition.toInt(), coordinate.columns.resolvedFinalPosition.toInt());
1058 for (GridSpan::iterator row = coordinate.rows.begin(); row != coordinate.rows.end(); ++row) {
1059 for (GridSpan::iterator column = coordinate.columns.begin(); column != coordinate.columns.end(); ++column)
1060 m_grid[row.toInt()][column.toInt()].append(&child);
1063 RELEASE_ASSERT(!m_gridItemCoordinate.contains(&child));
1064 m_gridItemCoordinate.set(&child, coordinate);
1067 void LayoutGrid::placeItemsOnGrid()
1069 if (!m_gridIsDirty)
1070 return;
1072 ASSERT(m_gridItemCoordinate.isEmpty());
1074 populateExplicitGridAndOrderIterator();
1076 // We clear the dirty bit here as the grid sizes have been updated.
1077 m_gridIsDirty = false;
1079 Vector<LayoutBox*> autoMajorAxisAutoGridItems;
1080 Vector<LayoutBox*> specifiedMajorAxisAutoGridItems;
1081 for (LayoutBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
1082 if (child->isOutOfFlowPositioned())
1083 continue;
1085 OwnPtr<GridSpan> rowPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *child, ForRows);
1086 OwnPtr<GridSpan> columnPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *child, ForColumns);
1087 if (!rowPositions || !columnPositions) {
1088 GridSpan* majorAxisPositions = (autoPlacementMajorAxisDirection() == ForColumns) ? columnPositions.get() : rowPositions.get();
1089 if (!majorAxisPositions)
1090 autoMajorAxisAutoGridItems.append(child);
1091 else
1092 specifiedMajorAxisAutoGridItems.append(child);
1093 continue;
1095 insertItemIntoGrid(*child, GridCoordinate(*rowPositions, *columnPositions));
1098 ASSERT(gridRowCount() >= GridResolvedPosition::explicitGridRowCount(*style()));
1099 ASSERT(gridColumnCount() >= GridResolvedPosition::explicitGridColumnCount(*style()));
1101 placeSpecifiedMajorAxisItemsOnGrid(specifiedMajorAxisAutoGridItems);
1102 placeAutoMajorAxisItemsOnGrid(autoMajorAxisAutoGridItems);
1104 m_grid.shrinkToFit();
1107 void LayoutGrid::populateExplicitGridAndOrderIterator()
1109 OrderIteratorPopulator populator(m_orderIterator);
1111 size_t maximumRowIndex = std::max<size_t>(1, GridResolvedPosition::explicitGridRowCount(*style()));
1112 size_t maximumColumnIndex = std::max<size_t>(1, GridResolvedPosition::explicitGridColumnCount(*style()));
1114 ASSERT(m_gridItemsIndexesMap.isEmpty());
1115 size_t childIndex = 0;
1116 for (LayoutBox* child = firstChildBox(); child; child = child->nextInFlowSiblingBox()) {
1117 populator.collectChild(child);
1118 m_gridItemsIndexesMap.set(child, childIndex++);
1120 // This function bypasses the cache (cachedGridCoordinate()) as it is used to build it.
1121 OwnPtr<GridSpan> rowPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *child, ForRows);
1122 OwnPtr<GridSpan> columnPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *child, ForColumns);
1124 // |positions| is 0 if we need to run the auto-placement algorithm.
1125 if (rowPositions) {
1126 maximumRowIndex = std::max<size_t>(maximumRowIndex, rowPositions->resolvedFinalPosition.next().toInt());
1127 } else {
1128 // Grow the grid for items with a definite row span, getting the largest such span.
1129 GridSpan positions = GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(*style(), *child, ForRows, GridResolvedPosition(0));
1130 maximumRowIndex = std::max<size_t>(maximumRowIndex, positions.resolvedFinalPosition.next().toInt());
1133 if (columnPositions) {
1134 maximumColumnIndex = std::max<size_t>(maximumColumnIndex, columnPositions->resolvedFinalPosition.next().toInt());
1135 } else {
1136 // Grow the grid for items with a definite column span, getting the largest such span.
1137 GridSpan positions = GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(*style(), *child, ForColumns, GridResolvedPosition(0));
1138 maximumColumnIndex = std::max<size_t>(maximumColumnIndex, positions.resolvedFinalPosition.next().toInt());
1142 m_grid.grow(maximumRowIndex);
1143 for (auto& column : m_grid)
1144 column.grow(maximumColumnIndex);
1147 PassOwnPtr<GridCoordinate> LayoutGrid::createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(const LayoutBox& gridItem, GridTrackSizingDirection specifiedDirection, const GridSpan& specifiedPositions) const
1149 GridTrackSizingDirection crossDirection = specifiedDirection == ForColumns ? ForRows : ForColumns;
1150 const size_t endOfCrossDirection = crossDirection == ForColumns ? gridColumnCount() : gridRowCount();
1151 GridSpan crossDirectionPositions = GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(*style(), gridItem, crossDirection, GridResolvedPosition(endOfCrossDirection));
1152 return adoptPtr(new GridCoordinate(specifiedDirection == ForColumns ? crossDirectionPositions : specifiedPositions, specifiedDirection == ForColumns ? specifiedPositions : crossDirectionPositions));
1155 void LayoutGrid::placeSpecifiedMajorAxisItemsOnGrid(const Vector<LayoutBox*>& autoGridItems)
1157 bool isForColumns = autoPlacementMajorAxisDirection() == ForColumns;
1158 bool isGridAutoFlowDense = style()->isGridAutoFlowAlgorithmDense();
1160 // Mapping between the major axis tracks (rows or columns) and the last auto-placed item's position inserted on
1161 // that track. This is needed to implement "sparse" packing for items locked to a given track.
1162 // See http://dev.w3.org/csswg/css-grid/#auto-placement-algo
1163 HashMap<unsigned, unsigned, DefaultHash<unsigned>::Hash, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> minorAxisCursors;
1165 for (const auto& autoGridItem : autoGridItems) {
1166 OwnPtr<GridSpan> majorAxisPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *autoGridItem, autoPlacementMajorAxisDirection());
1167 GridSpan minorAxisPositions = GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(*style(), *autoGridItem, autoPlacementMinorAxisDirection(), GridResolvedPosition(0));
1168 unsigned majorAxisInitialPosition = majorAxisPositions->resolvedInitialPosition.toInt();
1170 GridIterator iterator(m_grid, autoPlacementMajorAxisDirection(), majorAxisPositions->resolvedInitialPosition.toInt(), isGridAutoFlowDense ? 0 : minorAxisCursors.get(majorAxisInitialPosition));
1171 OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea(majorAxisPositions->integerSpan(), minorAxisPositions.integerSpan());
1172 if (!emptyGridArea)
1173 emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(*autoGridItem, autoPlacementMajorAxisDirection(), *majorAxisPositions);
1174 insertItemIntoGrid(*autoGridItem, *emptyGridArea);
1176 if (!isGridAutoFlowDense)
1177 minorAxisCursors.set(majorAxisInitialPosition, isForColumns ? emptyGridArea->rows.resolvedInitialPosition.toInt() : emptyGridArea->columns.resolvedInitialPosition.toInt());
1181 void LayoutGrid::placeAutoMajorAxisItemsOnGrid(const Vector<LayoutBox*>& autoGridItems)
1183 std::pair<size_t, size_t> autoPlacementCursor = std::make_pair(0, 0);
1184 bool isGridAutoFlowDense = style()->isGridAutoFlowAlgorithmDense();
1186 for (const auto& autoGridItem : autoGridItems) {
1187 placeAutoMajorAxisItemOnGrid(*autoGridItem, autoPlacementCursor);
1189 // If grid-auto-flow is dense, reset auto-placement cursor.
1190 if (isGridAutoFlowDense) {
1191 autoPlacementCursor.first = 0;
1192 autoPlacementCursor.second = 0;
1197 void LayoutGrid::placeAutoMajorAxisItemOnGrid(LayoutBox& gridItem, std::pair<size_t, size_t>& autoPlacementCursor)
1199 OwnPtr<GridSpan> minorAxisPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), gridItem, autoPlacementMinorAxisDirection());
1200 ASSERT(!GridResolvedPosition::resolveGridPositionsFromStyle(*style(), gridItem, autoPlacementMajorAxisDirection()));
1201 GridSpan majorAxisPositions = GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(*style(), gridItem, autoPlacementMajorAxisDirection(), GridResolvedPosition(0));
1203 const size_t endOfMajorAxis = (autoPlacementMajorAxisDirection() == ForColumns) ? gridColumnCount() : gridRowCount();
1204 size_t majorAxisAutoPlacementCursor = autoPlacementMajorAxisDirection() == ForColumns ? autoPlacementCursor.second : autoPlacementCursor.first;
1205 size_t minorAxisAutoPlacementCursor = autoPlacementMajorAxisDirection() == ForColumns ? autoPlacementCursor.first : autoPlacementCursor.second;
1207 OwnPtr<GridCoordinate> emptyGridArea;
1208 if (minorAxisPositions) {
1209 // Move to the next track in major axis if initial position in minor axis is before auto-placement cursor.
1210 if (minorAxisPositions->resolvedInitialPosition.toInt() < minorAxisAutoPlacementCursor)
1211 majorAxisAutoPlacementCursor++;
1213 if (majorAxisAutoPlacementCursor < endOfMajorAxis) {
1214 GridIterator iterator(m_grid, autoPlacementMinorAxisDirection(), minorAxisPositions->resolvedInitialPosition.toInt(), majorAxisAutoPlacementCursor);
1215 emptyGridArea = iterator.nextEmptyGridArea(minorAxisPositions->integerSpan(), majorAxisPositions.integerSpan());
1218 if (!emptyGridArea)
1219 emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(gridItem, autoPlacementMinorAxisDirection(), *minorAxisPositions);
1220 } else {
1221 GridSpan minorAxisPositions = GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(*style(), gridItem, autoPlacementMinorAxisDirection(), GridResolvedPosition(0));
1223 for (size_t majorAxisIndex = majorAxisAutoPlacementCursor; majorAxisIndex < endOfMajorAxis; ++majorAxisIndex) {
1224 GridIterator iterator(m_grid, autoPlacementMajorAxisDirection(), majorAxisIndex, minorAxisAutoPlacementCursor);
1225 emptyGridArea = iterator.nextEmptyGridArea(majorAxisPositions.integerSpan(), minorAxisPositions.integerSpan());
1227 if (emptyGridArea) {
1228 // Check that it fits in the minor axis direction, as we shouldn't grow in that direction here (it was already managed in populateExplicitGridAndOrderIterator()).
1229 GridResolvedPosition minorAxisFinalPositionIndex = autoPlacementMinorAxisDirection() == ForColumns ? emptyGridArea->columns.resolvedFinalPosition : emptyGridArea->rows.resolvedFinalPosition;
1230 const size_t endOfMinorAxis = autoPlacementMinorAxisDirection() == ForColumns ? gridColumnCount() : gridRowCount();
1231 if (minorAxisFinalPositionIndex.toInt() < endOfMinorAxis)
1232 break;
1234 // Discard empty grid area as it does not fit in the minor axis direction.
1235 // We don't need to create a new empty grid area yet as we might find a valid one in the next iteration.
1236 emptyGridArea = nullptr;
1239 // As we're moving to the next track in the major axis we should reset the auto-placement cursor in the minor axis.
1240 minorAxisAutoPlacementCursor = 0;
1243 if (!emptyGridArea)
1244 emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(gridItem, autoPlacementMinorAxisDirection(), minorAxisPositions);
1247 insertItemIntoGrid(gridItem, *emptyGridArea);
1248 // Move auto-placement cursor to the new position.
1249 autoPlacementCursor.first = emptyGridArea->rows.resolvedInitialPosition.toInt();
1250 autoPlacementCursor.second = emptyGridArea->columns.resolvedInitialPosition.toInt();
1253 GridTrackSizingDirection LayoutGrid::autoPlacementMajorAxisDirection() const
1255 return style()->isGridAutoFlowDirectionColumn() ? ForColumns : ForRows;
1258 GridTrackSizingDirection LayoutGrid::autoPlacementMinorAxisDirection() const
1260 return style()->isGridAutoFlowDirectionColumn() ? ForRows : ForColumns;
1263 void LayoutGrid::dirtyGrid()
1265 if (m_gridIsDirty)
1266 return;
1268 // Even if this could be redundant, it could be seen as a defensive strategy against
1269 // style changes events happening during the layout phase or even while the painting process
1270 // is still ongoing.
1271 // Forcing a new layout for the Grid layout would cancel any ongoing painting and ensure
1272 // the grid and its children are correctly laid out according to the new style rules.
1273 setNeedsLayout(LayoutInvalidationReason::GridChanged);
1275 m_grid.resize(0);
1276 m_gridItemCoordinate.clear();
1277 m_gridItemsOverflowingGridArea.resize(0);
1278 m_gridItemsIndexesMap.clear();
1279 m_gridIsDirty = true;
1282 void LayoutGrid::applyStretchAlignmentToTracksIfNeeded(GridTrackSizingDirection direction, GridSizingData& sizingData, LayoutUnit availableSpace)
1284 if (availableSpace <= 0
1285 || (direction == ForColumns && styleRef().justifyContentDistribution() != ContentDistributionStretch)
1286 || (direction == ForRows && styleRef().alignContentDistribution() != ContentDistributionStretch))
1287 return;
1289 // Spec defines auto-sized tracks as the ones with an 'auto' max-sizing function.
1290 Vector<GridTrack>& tracks = (direction == ForColumns) ? sizingData.columnTracks : sizingData.rowTracks;
1291 Vector<unsigned> autoSizedTracksIndex;
1292 for (unsigned i = 0; i < tracks.size(); ++i) {
1293 const GridTrackSize& trackSize = gridTrackSize(direction, i);
1294 // If there is some flexible-sized track, they should have exhausted available space during sizing algorithm.
1295 ASSERT(!trackSize.maxTrackBreadth().isFlex());
1296 if (trackSize.hasAutoMaxTrackBreadth())
1297 autoSizedTracksIndex.append(i);
1300 unsigned numberOfAutoSizedTracks = autoSizedTracksIndex.size();
1301 if (numberOfAutoSizedTracks < 1)
1302 return;
1304 LayoutUnit sizeToIncrease = availableSpace / numberOfAutoSizedTracks;
1305 for (const auto& trackIndex : autoSizedTracksIndex) {
1306 GridTrack* track = tracks.data() + trackIndex;
1307 LayoutUnit baseSize = track->baseSize() + sizeToIncrease;
1308 track->setBaseSize(baseSize);
1312 void LayoutGrid::layoutGridItems()
1314 placeItemsOnGrid();
1316 LayoutUnit availableSpaceForColumns = availableLogicalWidth();
1317 LayoutUnit availableSpaceForRows = availableLogicalHeight(IncludeMarginBorderPadding);
1318 GridSizingData sizingData(gridColumnCount(), gridRowCount());
1319 computeUsedBreadthOfGridTracks(ForColumns, sizingData, availableSpaceForColumns);
1320 ASSERT(tracksAreWiderThanMinTrackBreadth(ForColumns, sizingData.columnTracks));
1321 computeUsedBreadthOfGridTracks(ForRows, sizingData, availableSpaceForRows);
1322 ASSERT(tracksAreWiderThanMinTrackBreadth(ForRows, sizingData.rowTracks));
1324 applyStretchAlignmentToTracksIfNeeded(ForColumns, sizingData, availableSpaceForColumns);
1325 applyStretchAlignmentToTracksIfNeeded(ForRows, sizingData, availableSpaceForRows);
1327 populateGridPositions(sizingData, availableSpaceForColumns, availableSpaceForRows);
1328 m_gridItemsOverflowingGridArea.resize(0);
1330 for (LayoutBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
1331 if (child->isOutOfFlowPositioned()) {
1332 prepareChildForPositionedLayout(*child);
1333 continue;
1336 // Because the grid area cannot be styled, we don't need to adjust
1337 // the grid breadth to account for 'box-sizing'.
1338 LayoutUnit oldOverrideContainingBlockContentLogicalWidth = child->hasOverrideContainingBlockLogicalWidth() ? child->overrideContainingBlockContentLogicalWidth() : LayoutUnit();
1339 LayoutUnit oldOverrideContainingBlockContentLogicalHeight = child->hasOverrideContainingBlockLogicalHeight() ? child->overrideContainingBlockContentLogicalHeight() : LayoutUnit();
1341 LayoutUnit overrideContainingBlockContentLogicalWidth = gridAreaBreadthForChildIncludingAlignmentOffsets(*child, ForColumns, sizingData);
1342 LayoutUnit overrideContainingBlockContentLogicalHeight = gridAreaBreadthForChildIncludingAlignmentOffsets(*child, ForRows, sizingData);
1344 SubtreeLayoutScope layoutScope(*child);
1345 if (oldOverrideContainingBlockContentLogicalWidth != overrideContainingBlockContentLogicalWidth || (oldOverrideContainingBlockContentLogicalHeight != overrideContainingBlockContentLogicalHeight && child->hasRelativeLogicalHeight()))
1346 layoutScope.setNeedsLayout(child, LayoutInvalidationReason::GridChanged);
1348 child->setOverrideContainingBlockContentLogicalWidth(overrideContainingBlockContentLogicalWidth);
1349 child->setOverrideContainingBlockContentLogicalHeight(overrideContainingBlockContentLogicalHeight);
1351 // Stretching logic might force a child layout, so we need to run it before the layoutIfNeeded
1352 // call to avoid unnecessary relayouts. This might imply that child margins, needed to correctly
1353 // determine the available space before stretching, are not set yet.
1354 applyStretchAlignmentToChildIfNeeded(*child);
1356 child->layoutIfNeeded();
1358 #if ENABLE(ASSERT)
1359 const GridCoordinate& coordinate = cachedGridCoordinate(*child);
1360 ASSERT(coordinate.columns.resolvedInitialPosition.toInt() < sizingData.columnTracks.size());
1361 ASSERT(coordinate.rows.resolvedInitialPosition.toInt() < sizingData.rowTracks.size());
1362 #endif
1363 child->setLogicalLocation(findChildLogicalPosition(*child, sizingData));
1365 // Keep track of children overflowing their grid area as we might need to paint them even if the grid-area is
1366 // not visible
1367 if (child->logicalHeight() > overrideContainingBlockContentLogicalHeight
1368 || child->logicalWidth() > overrideContainingBlockContentLogicalWidth)
1369 m_gridItemsOverflowingGridArea.append(child);
1372 for (const auto& row : sizingData.rowTracks)
1373 setLogicalHeight(logicalHeight() + row.baseSize());
1375 LayoutUnit height = logicalHeight() + borderAndPaddingLogicalHeight() + scrollbarLogicalHeight();
1376 if (hasLineIfEmpty())
1377 height = std::max(height, minimumLogicalHeightForEmptyLine());
1379 // Min / max logical height is handled by the call to updateLogicalHeight in layoutBlock.
1380 setLogicalHeight(height);
1383 void LayoutGrid::prepareChildForPositionedLayout(LayoutBox& child)
1385 ASSERT(child.isOutOfFlowPositioned());
1386 child.containingBlock()->insertPositionedObject(&child);
1388 DeprecatedPaintLayer* childLayer = child.layer();
1389 childLayer->setStaticInlinePosition(borderAndPaddingStart());
1390 childLayer->setStaticBlockPosition(borderAndPaddingBefore());
1393 void LayoutGrid::layoutPositionedObjects(bool relayoutChildren, PositionedLayoutBehavior info)
1395 TrackedLayoutBoxListHashSet* positionedDescendants = positionedObjects();
1396 if (!positionedDescendants)
1397 return;
1399 bool containerHasHorizontalWritingMode = isHorizontalWritingMode();
1400 for (auto* child : *positionedDescendants) {
1401 bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != containerHasHorizontalWritingMode;
1402 if (hasOrthogonalWritingMode) {
1403 // FIXME: Properly support orthogonal writing mode.
1404 continue;
1407 // FIXME: Detect properly if start/end is auto for inexistent named grid lines.
1408 bool columnStartIsAuto = child->style()->gridColumnStart().isAuto();
1409 LayoutUnit columnOffset = LayoutUnit();
1410 LayoutUnit columnBreadth = LayoutUnit();
1411 offsetAndBreadthForPositionedChild(*child, ForColumns, columnStartIsAuto, child->style()->gridColumnEnd().isAuto(), columnOffset, columnBreadth);
1412 bool rowStartIsAuto = child->style()->gridRowStart().isAuto();
1413 LayoutUnit rowOffset = LayoutUnit();
1414 LayoutUnit rowBreadth = LayoutUnit();
1415 offsetAndBreadthForPositionedChild(*child, ForRows, rowStartIsAuto, child->style()->gridRowEnd().isAuto(), rowOffset, rowBreadth);
1417 child->setOverrideContainingBlockContentLogicalWidth(columnBreadth);
1418 child->setOverrideContainingBlockContentLogicalHeight(rowBreadth);
1419 child->setExtraInlineOffset(columnOffset);
1420 child->setExtraBlockOffset(rowOffset);
1422 if (child->parent() == this) {
1423 // If column/row start is "auto" the static position has been already set in prepareChildForPositionedLayout().
1424 // If column/row start is not "auto" the padding has been already computed in offsetAndBreadthForPositionedChild().
1425 DeprecatedPaintLayer* childLayer = child->layer();
1426 if (!columnStartIsAuto)
1427 childLayer->setStaticInlinePosition(borderStart() + columnOffset);
1428 if (!rowStartIsAuto)
1429 childLayer->setStaticBlockPosition(borderBefore() + rowOffset);
1433 LayoutBlock::layoutPositionedObjects(relayoutChildren, info);
1436 void LayoutGrid::offsetAndBreadthForPositionedChild(const LayoutBox& child, GridTrackSizingDirection direction, bool startIsAuto, bool endIsAuto, LayoutUnit& offset, LayoutUnit& breadth)
1438 ASSERT(child.isHorizontalWritingMode() == isHorizontalWritingMode());
1440 OwnPtr<GridSpan> positions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), child, direction);
1441 if (!positions) {
1442 offset = LayoutUnit();
1443 breadth = (direction == ForColumns) ? clientLogicalWidth() : clientLogicalHeight();
1444 return;
1447 GridResolvedPosition firstPosition = GridResolvedPosition(0);
1448 GridResolvedPosition initialPosition = startIsAuto ? firstPosition : positions->resolvedInitialPosition;
1449 GridResolvedPosition lastPosition = GridResolvedPosition((direction == ForColumns ? gridColumnCount() : gridRowCount()) - 1);
1450 GridResolvedPosition finalPosition = endIsAuto ? lastPosition : positions->resolvedFinalPosition;
1452 // Positioned children do not grow the grid, so we need to clamp the positions to avoid ending up outside of it.
1453 initialPosition = std::min<GridResolvedPosition>(initialPosition, lastPosition);
1454 finalPosition = std::min<GridResolvedPosition>(finalPosition, lastPosition);
1456 LayoutUnit start = startIsAuto ? LayoutUnit() : (direction == ForColumns) ? m_columnPositions[initialPosition.toInt()] : m_rowPositions[initialPosition.toInt()];
1457 LayoutUnit end = endIsAuto ? (direction == ForColumns) ? logicalWidth() : logicalHeight() : (direction == ForColumns) ? m_columnPositions[finalPosition.next().toInt()] : m_rowPositions[finalPosition.next().toInt()];
1459 breadth = end - start;
1461 if (startIsAuto)
1462 breadth -= (direction == ForColumns) ? borderStart() : borderBefore();
1463 else
1464 start -= ((direction == ForColumns) ? borderStart() : borderBefore());
1466 if (endIsAuto) {
1467 breadth -= (direction == ForColumns) ? borderEnd() : borderAfter();
1468 breadth -= scrollbarLogicalWidth();
1471 offset = start;
1474 GridCoordinate LayoutGrid::cachedGridCoordinate(const LayoutBox& gridItem) const
1476 ASSERT(m_gridItemCoordinate.contains(&gridItem));
1477 return m_gridItemCoordinate.get(&gridItem);
1480 LayoutUnit LayoutGrid::gridAreaBreadthForChild(const LayoutBox& child, GridTrackSizingDirection direction, const Vector<GridTrack>& tracks) const
1482 const GridCoordinate& coordinate = cachedGridCoordinate(child);
1483 const GridSpan& span = (direction == ForColumns) ? coordinate.columns : coordinate.rows;
1484 LayoutUnit gridAreaBreadth = 0;
1485 for (GridSpan::iterator trackPosition = span.begin(); trackPosition != span.end(); ++trackPosition)
1486 gridAreaBreadth += tracks[trackPosition.toInt()].baseSize();
1487 return gridAreaBreadth;
1490 LayoutUnit LayoutGrid::gridAreaBreadthForChildIncludingAlignmentOffsets(const LayoutBox& child, GridTrackSizingDirection direction, const GridSizingData& sizingData) const
1492 // We need the cached value when available because Content Distribution alignment properties
1493 // may have some influence in the final grid area breadth.
1494 const Vector<GridTrack>& tracks = (direction == ForColumns) ? sizingData.columnTracks : sizingData.rowTracks;
1495 const GridCoordinate& coordinate = cachedGridCoordinate(child);
1496 const GridSpan& span = (direction == ForColumns) ? coordinate.columns : coordinate.rows;
1497 const Vector<LayoutUnit>& linePositions = (direction == ForColumns) ? m_columnPositions : m_rowPositions;
1498 LayoutUnit initialTrackPosition = linePositions[span.resolvedInitialPosition.toInt()];
1499 LayoutUnit finalTrackPosition = linePositions[span.resolvedFinalPosition.toInt()];
1500 // Track Positions vector stores the 'start' grid line of each track, so w have to add last track's baseSize.
1501 return finalTrackPosition - initialTrackPosition + tracks[span.resolvedFinalPosition.toInt()].baseSize();
1504 void LayoutGrid::populateGridPositions(GridSizingData& sizingData, LayoutUnit availableSpaceForColumns, LayoutUnit availableSpaceForRows)
1506 // Since we add alignment offsets, grid lines are not always adjacent. Hence we will have to
1507 // assume from now on that we just store positions of the initial grid lines of each track,
1508 // except the last one, which is the only one considered as a final grid line of a track.
1509 // FIXME: This will affect the computed style value of grid tracks size, since we are
1510 // using these positions to compute them.
1512 unsigned numberOfTracks = sizingData.columnTracks.size();
1513 unsigned numberOfLines = numberOfTracks + 1;
1514 unsigned lastLine = numberOfLines - 1;
1515 unsigned nextToLastLine = numberOfLines - 2;
1516 ContentAlignmentData offset = computeContentPositionAndDistributionOffset(ForColumns, availableSpaceForColumns, numberOfTracks);
1517 m_columnPositions.resize(numberOfLines);
1518 m_columnPositions[0] = borderAndPaddingStart() + offset.positionOffset;
1519 for (unsigned i = 0; i < lastLine; ++i)
1520 m_columnPositions[i + 1] = m_columnPositions[i] + offset.distributionOffset + sizingData.columnTracks[i].baseSize();
1521 m_columnPositions[lastLine] = m_columnPositions[nextToLastLine] + sizingData.columnTracks[nextToLastLine].baseSize();
1523 numberOfTracks = sizingData.rowTracks.size();
1524 numberOfLines = numberOfTracks + 1;
1525 lastLine = numberOfLines - 1;
1526 nextToLastLine = numberOfLines - 2;
1527 offset = computeContentPositionAndDistributionOffset(ForRows, availableSpaceForRows, numberOfTracks);
1528 m_rowPositions.resize(numberOfLines);
1529 m_rowPositions[0] = borderAndPaddingBefore() + offset.positionOffset;
1530 for (unsigned i = 0; i < lastLine; ++i)
1531 m_rowPositions[i + 1] = m_rowPositions[i] + offset.distributionOffset + sizingData.rowTracks[i].baseSize();
1532 m_rowPositions[lastLine] = m_rowPositions[nextToLastLine] + sizingData.rowTracks[nextToLastLine].baseSize();
1535 static LayoutUnit computeOverflowAlignmentOffset(OverflowAlignment overflow, LayoutUnit trackBreadth, LayoutUnit childBreadth)
1537 LayoutUnit offset = trackBreadth - childBreadth;
1538 switch (overflow) {
1539 case OverflowAlignmentSafe:
1540 // If overflow is 'safe', we have to make sure we don't overflow the 'start'
1541 // edge (potentially cause some data loss as the overflow is unreachable).
1542 return std::max<LayoutUnit>(0, offset);
1543 case OverflowAlignmentTrue:
1544 case OverflowAlignmentDefault:
1545 // If we overflow our alignment container and overflow is 'true' (default), we
1546 // ignore the overflow and just return the value regardless (which may cause data
1547 // loss as we overflow the 'start' edge).
1548 return offset;
1551 ASSERT_NOT_REACHED();
1552 return 0;
1555 static inline LayoutUnit constrainedChildIntrinsicContentLogicalHeight(const LayoutBox& child)
1557 LayoutUnit childIntrinsicContentLogicalHeight = child.intrinsicContentLogicalHeight();
1558 return child.constrainLogicalHeightByMinMax(childIntrinsicContentLogicalHeight + child.borderAndPaddingLogicalHeight(), childIntrinsicContentLogicalHeight);
1561 // FIXME: This logic is shared by LayoutFlexibleBox, so it should be moved to LayoutBox.
1562 bool LayoutGrid::needToStretchChildLogicalHeight(const LayoutBox& child) const
1564 if (ComputedStyle::resolveAlignment(styleRef(), child.styleRef(), ItemPositionStretch) != ItemPositionStretch)
1565 return false;
1567 return isHorizontalWritingMode() && child.style()->height().isAuto();
1570 // FIXME: This logic is shared by LayoutFlexibleBox, so it should be moved to LayoutBox.
1571 LayoutUnit LayoutGrid::childIntrinsicHeight(const LayoutBox& child) const
1573 if (child.isHorizontalWritingMode() && needToStretchChildLogicalHeight(child))
1574 return constrainedChildIntrinsicContentLogicalHeight(child);
1575 return child.size().height();
1578 // FIXME: This logic is shared by LayoutFlexibleBox, so it should be moved to LayoutBox.
1579 LayoutUnit LayoutGrid::childIntrinsicWidth(const LayoutBox& child) const
1581 if (!child.isHorizontalWritingMode() && needToStretchChildLogicalHeight(child))
1582 return constrainedChildIntrinsicContentLogicalHeight(child);
1583 return child.size().width();
1586 // FIXME: This logic is shared by LayoutFlexibleBox, so it should be moved to LayoutBox.
1587 LayoutUnit LayoutGrid::intrinsicLogicalHeightForChild(const LayoutBox& child) const
1589 return isHorizontalWritingMode() ? childIntrinsicHeight(child) : childIntrinsicWidth(child);
1592 // FIXME: This logic is shared by LayoutFlexibleBox, so it should be moved to LayoutBox.
1593 LayoutUnit LayoutGrid::marginLogicalHeightForChild(const LayoutBox& child) const
1595 return isHorizontalWritingMode() ? child.marginHeight() : child.marginWidth();
1598 LayoutUnit LayoutGrid::computeMarginLogicalHeightForChild(const LayoutBox& child) const
1600 if (!child.styleRef().hasMargin())
1601 return 0;
1603 LayoutUnit marginBefore;
1604 LayoutUnit marginAfter;
1605 child.computeMarginsForDirection(BlockDirection, this, child.containingBlockLogicalWidthForContent(), child.logicalHeight(), marginBefore, marginAfter,
1606 child.style()->marginBeforeUsing(style()),
1607 child.style()->marginAfterUsing(style()));
1609 return marginBefore + marginAfter;
1612 LayoutUnit LayoutGrid::availableAlignmentSpaceForChildBeforeStretching(LayoutUnit gridAreaBreadthForChild, const LayoutBox& child) const
1614 LayoutUnit childMarginLogicalHeight = marginLogicalHeightForChild(child);
1616 // Because we want to avoid multiple layouts, stretching logic might be performed before
1617 // children are laid out, so we can't use the child cached values. Hence, we need to
1618 // compute margins in order to determine the available height before stretching.
1619 if (childMarginLogicalHeight == 0)
1620 childMarginLogicalHeight = computeMarginLogicalHeightForChild(child);
1622 return gridAreaBreadthForChild - childMarginLogicalHeight;
1625 // FIXME: This logic is shared by LayoutFlexibleBox, so it should be moved to LayoutBox.
1626 void LayoutGrid::applyStretchAlignmentToChildIfNeeded(LayoutBox& child)
1628 // We clear both width and height override values because we will decide now whether they
1629 // are allowed or not, evaluating the conditions which might have changed since the old
1630 // values were set.
1631 child.clearOverrideSize();
1633 auto& childStyle = child.styleRef();
1634 bool isHorizontalMode = isHorizontalWritingMode();
1635 bool hasAutoSizeInRowAxis = isHorizontalMode ? childStyle.width().isAuto() : childStyle.height().isAuto();
1636 bool allowedToStretchChildAlongRowAxis = hasAutoSizeInRowAxis && !childStyle.marginStartUsing(style()).isAuto() && !childStyle.marginEndUsing(style()).isAuto();
1637 if (!allowedToStretchChildAlongRowAxis || ComputedStyle::resolveJustification(styleRef(), childStyle, ItemPositionStretch) != ItemPositionStretch) {
1638 bool hasAutoMinSizeInRowAxis = isHorizontalMode ? childStyle.minWidth().isAuto() : childStyle.minHeight().isAuto();
1639 bool canShrinkToFitInRowAxisForChild = !hasAutoMinSizeInRowAxis || child.minPreferredLogicalWidth() <= child.overrideContainingBlockContentLogicalWidth();
1640 // TODO(lajava): how to handle orthogonality in this case ?.
1641 // TODO(lajava): grid track sizing and positioning do not support orthogonal modes yet.
1642 if (hasAutoSizeInRowAxis && canShrinkToFitInRowAxisForChild) {
1643 LayoutUnit childWidthToFitContent = std::max(std::min(child.maxPreferredLogicalWidth(), child.overrideContainingBlockContentLogicalWidth() - child.marginLogicalWidth()), child.minPreferredLogicalWidth());
1644 LayoutUnit desiredLogicalWidth = child.constrainLogicalHeightByMinMax(childWidthToFitContent, -1);
1645 child.setOverrideLogicalContentWidth(desiredLogicalWidth - child.borderAndPaddingLogicalWidth());
1646 if (desiredLogicalWidth != child.logicalWidth())
1647 child.setNeedsLayout(LayoutInvalidationReason::GridChanged);
1651 bool hasAutoSizeInColumnAxis = isHorizontalMode ? childStyle.height().isAuto() : childStyle.width().isAuto();
1652 bool allowedToStretchChildAlongColumnAxis = hasAutoSizeInColumnAxis && !childStyle.marginBeforeUsing(style()).isAuto() && !childStyle.marginAfterUsing(style()).isAuto();
1653 if (allowedToStretchChildAlongColumnAxis && ComputedStyle::resolveAlignment(styleRef(), childStyle, ItemPositionStretch) == ItemPositionStretch) {
1654 // TODO (lajava): If the child has orthogonal flow, then it already has an override height set, so use it.
1655 // TODO (lajava): grid track sizing and positioning do not support orthogonal modes yet.
1656 if (child.isHorizontalWritingMode() == isHorizontalMode) {
1657 LayoutUnit stretchedLogicalHeight = availableAlignmentSpaceForChildBeforeStretching(child.overrideContainingBlockContentLogicalHeight(), child);
1658 LayoutUnit desiredLogicalHeight = child.constrainLogicalHeightByMinMax(stretchedLogicalHeight, -1);
1659 child.setOverrideLogicalContentHeight(desiredLogicalHeight - child.borderAndPaddingLogicalHeight());
1660 if (desiredLogicalHeight != child.logicalHeight()) {
1661 // TODO (lajava): Can avoid laying out here in some cases. See https://webkit.org/b/87905.
1662 child.setLogicalHeight(0);
1663 child.setNeedsLayout(LayoutInvalidationReason::GridChanged);
1669 GridAxisPosition LayoutGrid::columnAxisPositionForChild(const LayoutBox& child) const
1671 bool hasOrthogonalWritingMode = child.isHorizontalWritingMode() != isHorizontalWritingMode();
1672 bool hasSameWritingMode = child.styleRef().writingMode() == styleRef().writingMode();
1674 switch (ComputedStyle::resolveAlignment(styleRef(), child.styleRef(), ItemPositionStretch)) {
1675 case ItemPositionSelfStart:
1676 // If orthogonal writing-modes, this computes to 'start'.
1677 // FIXME: grid track sizing and positioning do not support orthogonal modes yet.
1678 // self-start is based on the child's block axis direction. That's why we need to check against the grid container's block flow.
1679 return (hasOrthogonalWritingMode || hasSameWritingMode) ? GridAxisStart : GridAxisEnd;
1680 case ItemPositionSelfEnd:
1681 // If orthogonal writing-modes, this computes to 'end'.
1682 // FIXME: grid track sizing and positioning do not support orthogonal modes yet.
1683 // self-end is based on the child's block axis direction. That's why we need to check against the grid container's block flow.
1684 return (hasOrthogonalWritingMode || hasSameWritingMode) ? GridAxisEnd : GridAxisStart;
1685 case ItemPositionLeft:
1686 // The alignment axis (column axis) and the inline axis are parallell in
1687 // orthogonal writing mode. Otherwise this this is equivalent to 'start'.
1688 // FIXME: grid track sizing and positioning do not support orthogonal modes yet.
1689 return GridAxisStart;
1690 case ItemPositionRight:
1691 // The alignment axis (column axis) and the inline axis are parallell in
1692 // orthogonal writing mode. Otherwise this this is equivalent to 'start'.
1693 // FIXME: grid track sizing and positioning do not support orthogonal modes yet.
1694 return hasOrthogonalWritingMode ? GridAxisEnd : GridAxisStart;
1695 case ItemPositionCenter:
1696 return GridAxisCenter;
1697 case ItemPositionFlexStart: // Only used in flex layout, otherwise equivalent to 'start'.
1698 case ItemPositionStart:
1699 return GridAxisStart;
1700 case ItemPositionFlexEnd: // Only used in flex layout, otherwise equivalent to 'end'.
1701 case ItemPositionEnd:
1702 return GridAxisEnd;
1703 case ItemPositionStretch:
1704 return GridAxisStart;
1705 case ItemPositionBaseline:
1706 case ItemPositionLastBaseline:
1707 // FIXME: These two require implementing Baseline Alignment. For now, we always 'start' align the child.
1708 // crbug.com/234191
1709 return GridAxisStart;
1710 case ItemPositionAuto:
1711 break;
1714 ASSERT_NOT_REACHED();
1715 return GridAxisStart;
1718 GridAxisPosition LayoutGrid::rowAxisPositionForChild(const LayoutBox& child) const
1720 bool hasOrthogonalWritingMode = child.isHorizontalWritingMode() != isHorizontalWritingMode();
1721 bool hasSameDirection = child.styleRef().direction() == styleRef().direction();
1722 bool isLTR = styleRef().isLeftToRightDirection();
1724 switch (ComputedStyle::resolveJustification(styleRef(), child.styleRef(), ItemPositionStretch)) {
1725 case ItemPositionSelfStart:
1726 // For orthogonal writing-modes, this computes to 'start'
1727 // FIXME: grid track sizing and positioning do not support orthogonal modes yet.
1728 // self-start is based on the child's direction. That's why we need to check against the grid container's direction.
1729 return (hasOrthogonalWritingMode || hasSameDirection) ? GridAxisStart : GridAxisEnd;
1730 case ItemPositionSelfEnd:
1731 // For orthogonal writing-modes, this computes to 'start'
1732 // FIXME: grid track sizing and positioning do not support orthogonal modes yet.
1733 return (hasOrthogonalWritingMode || hasSameDirection) ? GridAxisEnd : GridAxisStart;
1734 case ItemPositionLeft:
1735 return isLTR ? GridAxisStart : GridAxisEnd;
1736 case ItemPositionRight:
1737 return isLTR ? GridAxisEnd : GridAxisStart;
1738 case ItemPositionCenter:
1739 return GridAxisCenter;
1740 case ItemPositionFlexStart: // Only used in flex layout, otherwise equivalent to 'start'.
1741 case ItemPositionStart:
1742 return GridAxisStart;
1743 case ItemPositionFlexEnd: // Only used in flex layout, otherwise equivalent to 'end'.
1744 case ItemPositionEnd:
1745 return GridAxisEnd;
1746 case ItemPositionStretch:
1747 return GridAxisStart;
1748 case ItemPositionBaseline:
1749 case ItemPositionLastBaseline:
1750 // FIXME: These two require implementing Baseline Alignment. For now, we always 'start' align the child.
1751 // crbug.com/234191
1752 return GridAxisStart;
1753 case ItemPositionAuto:
1754 break;
1757 ASSERT_NOT_REACHED();
1758 return GridAxisStart;
1761 LayoutUnit LayoutGrid::columnAxisOffsetForChild(const LayoutBox& child) const
1763 const GridCoordinate& coordinate = cachedGridCoordinate(child);
1764 LayoutUnit startOfRow = m_rowPositions[coordinate.rows.resolvedInitialPosition.toInt()];
1765 LayoutUnit startPosition = startOfRow + marginBeforeForChild(child);
1766 GridAxisPosition axisPosition = columnAxisPositionForChild(child);
1767 switch (axisPosition) {
1768 case GridAxisStart:
1769 return startPosition;
1770 case GridAxisEnd:
1771 case GridAxisCenter: {
1772 LayoutUnit endOfRow = m_rowPositions[coordinate.rows.resolvedFinalPosition.next().toInt()];
1773 LayoutUnit offsetFromStartPosition = computeOverflowAlignmentOffset(child.styleRef().alignSelfOverflowAlignment(), endOfRow - startOfRow, child.logicalHeight() + child.marginLogicalHeight());
1774 return startPosition + (axisPosition == GridAxisEnd ? offsetFromStartPosition : offsetFromStartPosition / 2);
1778 ASSERT_NOT_REACHED();
1779 return 0;
1782 LayoutUnit LayoutGrid::rowAxisOffsetForChild(const LayoutBox& child) const
1784 const GridCoordinate& coordinate = cachedGridCoordinate(child);
1785 LayoutUnit startOfColumn = m_columnPositions[coordinate.columns.resolvedInitialPosition.toInt()];
1786 LayoutUnit startPosition = startOfColumn + marginStartForChild(child);
1787 GridAxisPosition axisPosition = rowAxisPositionForChild(child);
1788 switch (axisPosition) {
1789 case GridAxisStart:
1790 return startPosition;
1791 case GridAxisEnd:
1792 case GridAxisCenter: {
1793 LayoutUnit endOfColumn = m_columnPositions[coordinate.columns.resolvedFinalPosition.next().toInt()];
1794 LayoutUnit offsetFromStartPosition = computeOverflowAlignmentOffset(child.styleRef().justifySelfOverflowAlignment(), endOfColumn - startOfColumn, child.logicalWidth() + child.marginLogicalWidth());
1795 return startPosition + (axisPosition == GridAxisEnd ? offsetFromStartPosition : offsetFromStartPosition / 2);
1799 ASSERT_NOT_REACHED();
1800 return 0;
1803 ContentPosition static resolveContentDistributionFallback(ContentDistributionType distribution)
1805 switch (distribution) {
1806 case ContentDistributionSpaceBetween:
1807 return ContentPositionStart;
1808 case ContentDistributionSpaceAround:
1809 return ContentPositionCenter;
1810 case ContentDistributionSpaceEvenly:
1811 return ContentPositionCenter;
1812 case ContentDistributionStretch:
1813 return ContentPositionStart;
1814 case ContentDistributionDefault:
1815 return ContentPositionAuto;
1818 ASSERT_NOT_REACHED();
1819 return ContentPositionAuto;
1822 static inline LayoutUnit offsetToStartEdge(bool isLeftToRight, LayoutUnit availableSpace)
1824 return isLeftToRight ? LayoutUnit() : availableSpace;
1827 static inline LayoutUnit offsetToEndEdge(bool isLeftToRight, LayoutUnit availableSpace)
1829 return !isLeftToRight ? LayoutUnit() : availableSpace;
1833 static ContentAlignmentData contentDistributionOffset(LayoutUnit availableFreeSpace, ContentPosition& fallbackPosition, ContentDistributionType distribution, unsigned numberOfGridTracks)
1835 if (distribution != ContentDistributionDefault && fallbackPosition == ContentPositionAuto)
1836 fallbackPosition = resolveContentDistributionFallback(distribution);
1838 if (availableFreeSpace <= 0)
1839 return {};
1841 LayoutUnit distributionOffset;
1842 switch (distribution) {
1843 case ContentDistributionSpaceBetween:
1844 if (numberOfGridTracks < 2)
1845 return {};
1846 return {0, availableFreeSpace / (numberOfGridTracks - 1)};
1847 case ContentDistributionSpaceAround:
1848 if (numberOfGridTracks < 1)
1849 return {};
1850 distributionOffset = availableFreeSpace / numberOfGridTracks;
1851 return {distributionOffset / 2, distributionOffset};
1852 case ContentDistributionSpaceEvenly:
1853 distributionOffset = availableFreeSpace / (numberOfGridTracks + 1);
1854 return {distributionOffset, distributionOffset};
1855 case ContentDistributionStretch:
1856 return {0, 0};
1857 case ContentDistributionDefault:
1858 return {};
1861 ASSERT_NOT_REACHED();
1862 return {};
1865 ContentAlignmentData LayoutGrid::computeContentPositionAndDistributionOffset(GridTrackSizingDirection direction, LayoutUnit availableFreeSpace, unsigned numberOfGridTracks) const
1867 bool isRowAxis = direction == ForColumns;
1868 ContentPosition position = isRowAxis ? styleRef().justifyContentPosition() : styleRef().alignContentPosition();
1869 ContentDistributionType distribution = isRowAxis ? styleRef().justifyContentDistribution() : styleRef().alignContentDistribution();
1870 // If <content-distribution> value can't be applied, 'position' will become the associated
1871 // <content-position> fallback value.
1872 ContentAlignmentData contentAlignment = contentDistributionOffset(availableFreeSpace, position, distribution, numberOfGridTracks);
1873 if (contentAlignment.isValid())
1874 return contentAlignment;
1876 OverflowAlignment overflow = isRowAxis ? styleRef().justifyContentOverflowAlignment() : styleRef().alignContentOverflowAlignment();
1877 if (availableFreeSpace <= 0 && overflow == OverflowAlignmentSafe)
1878 return {0, 0};
1880 switch (position) {
1881 case ContentPositionLeft:
1882 // The align-content's axis is always orthogonal to the inline-axis.
1883 return {0, 0};
1884 case ContentPositionRight:
1885 if (isRowAxis)
1886 return {availableFreeSpace, 0};
1887 // The align-content's axis is always orthogonal to the inline-axis.
1888 return {0, 0};
1889 case ContentPositionCenter:
1890 return {availableFreeSpace / 2, 0};
1891 case ContentPositionFlexEnd: // Only used in flex layout, for other layout, it's equivalent to 'End'.
1892 case ContentPositionEnd:
1893 if (isRowAxis)
1894 return {offsetToEndEdge(styleRef().isLeftToRightDirection(), availableFreeSpace), 0};
1895 return {availableFreeSpace, 0};
1896 case ContentPositionFlexStart: // Only used in flex layout, for other layout, it's equivalent to 'Start'.
1897 case ContentPositionStart:
1898 if (isRowAxis)
1899 return {offsetToStartEdge(styleRef().isLeftToRightDirection(), availableFreeSpace), 0};
1900 return {0, 0};
1901 case ContentPositionBaseline:
1902 case ContentPositionLastBaseline:
1903 // FIXME: These two require implementing Baseline Alignment. For now, we always 'start' align the child.
1904 // crbug.com/234191
1905 if (isRowAxis)
1906 return {offsetToStartEdge(styleRef().isLeftToRightDirection(), availableFreeSpace), 0};
1907 return {0, 0};
1908 case ContentPositionAuto:
1909 break;
1912 ASSERT_NOT_REACHED();
1913 return {0, 0};
1916 LayoutPoint LayoutGrid::findChildLogicalPosition(const LayoutBox& child, GridSizingData& sizingData) const
1918 LayoutUnit rowAxisOffset = rowAxisOffsetForChild(child);
1919 // We stored m_columnPosition s's data ignoring the direction, hence we might need now
1920 // to translate positions from RTL to LTR, as it's more convenient for painting.
1921 if (!style()->isLeftToRightDirection()) {
1922 LayoutUnit alignmentOffset = m_columnPositions[0] - borderAndPaddingStart();
1923 LayoutUnit rightGridEdgePosition = m_columnPositions[m_columnPositions.size() - 1] + alignmentOffset + borderAndPaddingLogicalLeft();
1924 rowAxisOffset = rightGridEdgePosition - (rowAxisOffset + child.logicalWidth());
1927 return LayoutPoint(rowAxisOffset, columnAxisOffsetForChild(child));
1930 void LayoutGrid::paintChildren(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1932 GridPainter(*this).paintChildren(paintInfo, paintOffset);
1935 } // namespace blink