summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/RenderGrid.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/RenderGrid.cpp')
-rw-r--r--Source/WebCore/rendering/RenderGrid.cpp2065
1 files changed, 1358 insertions, 707 deletions
diff --git a/Source/WebCore/rendering/RenderGrid.cpp b/Source/WebCore/rendering/RenderGrid.cpp
index 8f2ff4241..4d89feaba 100644
--- a/Source/WebCore/rendering/RenderGrid.cpp
+++ b/Source/WebCore/rendering/RenderGrid.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2011 Apple Inc. All rights reserved.
- * Copyright (C) 2013 Igalia S.L. All rights reserved.
+ * Copyright (C) 2013-2017 Igalia S.L.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -14,7 +14,7 @@
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -27,148 +27,203 @@
#include "config.h"
#include "RenderGrid.h"
-#include "GridCoordinate.h"
+#include "GridArea.h"
+#include "GridPositionsResolver.h"
+#include "GridTrackSizingAlgorithm.h"
#include "LayoutRepainter.h"
-#include "NotImplemented.h"
#include "RenderLayer.h"
#include "RenderView.h"
+#include <cstdlib>
namespace WebCore {
-static const int infinity = -1;
+static constexpr ItemPosition selfAlignmentNormalBehavior = ItemPositionStretch;
-class GridTrack {
-public:
- GridTrack()
- : m_usedBreadth(0)
- , m_maxBreadth(0)
- {
- }
+enum TrackSizeRestriction {
+ AllowInfinity,
+ ForbidInfinity,
+};
- void growUsedBreadth(LayoutUnit growth)
- {
- ASSERT(growth >= 0);
- m_usedBreadth += growth;
- }
- LayoutUnit usedBreadth() const { return m_usedBreadth; }
-
- void growMaxBreadth(LayoutUnit growth)
- {
- if (m_maxBreadth == infinity)
- m_maxBreadth = m_usedBreadth + growth;
- else
- m_maxBreadth += growth;
- }
- LayoutUnit maxBreadthIfNotInfinite() const
- {
- return (m_maxBreadth == infinity) ? m_usedBreadth : m_maxBreadth;
- }
+struct ContentAlignmentData {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ bool isValid() { return positionOffset >= 0 && distributionOffset >= 0; }
+ static ContentAlignmentData defaultOffsets() { return {-1, -1}; }
- LayoutUnit m_usedBreadth;
- LayoutUnit m_maxBreadth;
+ LayoutUnit positionOffset;
+ LayoutUnit distributionOffset;
};
-struct GridTrackForNormalization {
- GridTrackForNormalization(const GridTrack& track, double flex)
- : m_track(&track)
- , m_flex(flex)
- , m_normalizedFlexValue(track.m_usedBreadth / flex)
- {
- }
+RenderGrid::RenderGrid(Element& element, RenderStyle&& style)
+ : RenderBlock(element, WTFMove(style), 0)
+ , m_grid(*this)
+ , m_trackSizingAlgorithm(this, m_grid)
+{
+ // All of our children must be block level.
+ setChildrenInline(false);
+}
- const GridTrack* m_track;
- double m_flex;
- LayoutUnit m_normalizedFlexValue;
-};
+RenderGrid::~RenderGrid()
+{
+}
-class RenderGrid::GridIterator {
- WTF_MAKE_NONCOPYABLE(GridIterator);
-public:
- // |direction| is the direction that is fixed to |fixedTrackIndex| so e.g
- // GridIterator(m_grid, ForColumns, 1) will walk over the rows of the 2nd column.
- GridIterator(const Vector<Vector<Vector<RenderBox*, 1>>>& grid, TrackSizingDirection direction, size_t fixedTrackIndex)
- : m_grid(grid)
- , m_direction(direction)
- , m_rowIndex((direction == ForColumns) ? 0 : fixedTrackIndex)
- , m_columnIndex((direction == ForColumns) ? fixedTrackIndex : 0)
- , m_childIndex(0)
- {
- ASSERT(m_rowIndex < m_grid.size());
- ASSERT(m_columnIndex < m_grid[0].size());
- }
+static inline bool defaultAlignmentIsStretch(ItemPosition position)
+{
+ return position == ItemPositionStretch || position == ItemPositionAuto;
+}
- RenderBox* nextGridItem()
- {
- if (!m_grid.size())
- return 0;
+static inline bool defaultAlignmentChangedToStretchInRowAxis(const RenderStyle& oldStyle, const RenderStyle& newStyle)
+{
+ return !defaultAlignmentIsStretch(oldStyle.justifyItems().position()) && defaultAlignmentIsStretch(newStyle.justifyItems().position());
+}
- size_t& varyingTrackIndex = (m_direction == ForColumns) ? m_rowIndex : m_columnIndex;
- const size_t endOfVaryingTrackIndex = (m_direction == ForColumns) ? m_grid.size() : m_grid[0].size();
- for (; varyingTrackIndex < endOfVaryingTrackIndex; ++varyingTrackIndex) {
- const Vector<RenderBox*>& children = m_grid[m_rowIndex][m_columnIndex];
- if (m_childIndex < children.size())
- return children[m_childIndex++];
+static inline bool defaultAlignmentChangedFromStretchInRowAxis(const RenderStyle& oldStyle, const RenderStyle& newStyle)
+{
+ return defaultAlignmentIsStretch(oldStyle.justifyItems().position()) && !defaultAlignmentIsStretch(newStyle.justifyItems().position());
+}
- m_childIndex = 0;
- }
- return 0;
- }
+static inline bool defaultAlignmentChangedFromStretchInColumnAxis(const RenderStyle& oldStyle, const RenderStyle& newStyle)
+{
+ return defaultAlignmentIsStretch(oldStyle.alignItems().position()) && !defaultAlignmentIsStretch(newStyle.alignItems().position());
+}
- PassOwnPtr<GridCoordinate> nextEmptyGridArea()
- {
- if (m_grid.isEmpty())
- return nullptr;
-
- size_t& varyingTrackIndex = (m_direction == ForColumns) ? m_rowIndex : m_columnIndex;
- const size_t endOfVaryingTrackIndex = (m_direction == ForColumns) ? m_grid.size() : m_grid[0].size();
- for (; varyingTrackIndex < endOfVaryingTrackIndex; ++varyingTrackIndex) {
- const Vector<RenderBox*>& children = m_grid[m_rowIndex][m_columnIndex];
- if (children.isEmpty()) {
- OwnPtr<GridCoordinate> result = adoptPtr(new GridCoordinate(GridSpan(m_rowIndex, m_rowIndex), GridSpan(m_columnIndex, m_columnIndex)));
- // Advance the iterator to avoid an infinite loop where we would return the same grid area over and over.
- ++varyingTrackIndex;
- return result.release();
+static inline bool selfAlignmentChangedToStretchInRowAxis(const RenderStyle& oldStyle, const RenderStyle& newStyle, const RenderStyle& childStyle)
+{
+ return childStyle.resolvedJustifySelf(oldStyle, selfAlignmentNormalBehavior).position() != ItemPositionStretch
+ && childStyle.resolvedJustifySelf(newStyle, selfAlignmentNormalBehavior).position() == ItemPositionStretch;
+}
+
+static inline bool selfAlignmentChangedFromStretchInRowAxis(const RenderStyle& oldStyle, const RenderStyle& newStyle, const RenderStyle& childStyle)
+{
+ return childStyle.resolvedJustifySelf(oldStyle, selfAlignmentNormalBehavior).position() == ItemPositionStretch
+ && childStyle.resolvedJustifySelf(newStyle, selfAlignmentNormalBehavior).position() != ItemPositionStretch;
+}
+
+static inline bool selfAlignmentChangedFromStretchInColumnAxis(const RenderStyle& oldStyle, const RenderStyle& newStyle, const RenderStyle& childStyle)
+{
+ return childStyle.resolvedAlignSelf(oldStyle, selfAlignmentNormalBehavior).position() == ItemPositionStretch
+ && childStyle.resolvedAlignSelf(newStyle, selfAlignmentNormalBehavior).position() != ItemPositionStretch;
+}
+
+void RenderGrid::addChild(RenderObject* newChild, RenderObject* beforeChild)
+{
+ RenderBlock::addChild(newChild, beforeChild);
+
+ // Positioned grid items do not take up space or otherwise participate in the layout of the grid,
+ // for that reason we don't need to mark the grid as dirty when they are added.
+ if (newChild->isOutOfFlowPositioned())
+ return;
+
+ // The grid needs to be recomputed as it might contain auto-placed items that
+ // will change their position.
+ dirtyGrid();
+}
+
+void RenderGrid::removeChild(RenderObject& child)
+{
+ RenderBlock::removeChild(child);
+
+ // Positioned grid items do not take up space or otherwise participate in the layout of the grid,
+ // for that reason we don't need to mark the grid as dirty when they are removed.
+ if (child.isOutOfFlowPositioned())
+ return;
+
+ // The grid needs to be recomputed as it might contain auto-placed items that
+ // will change their position.
+ dirtyGrid();
+}
+
+void RenderGrid::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderBlock::styleDidChange(diff, oldStyle);
+ if (!oldStyle || diff != StyleDifferenceLayout)
+ return;
+
+ const RenderStyle& newStyle = style();
+ if (defaultAlignmentChangedToStretchInRowAxis(*oldStyle, newStyle) || defaultAlignmentChangedFromStretchInRowAxis(*oldStyle, newStyle)
+ || defaultAlignmentChangedFromStretchInColumnAxis(*oldStyle, newStyle)) {
+ // Grid items that were not previously stretched in row-axis need to be relayed out so we can compute new available space.
+ // Grid items that were previously stretching in column-axis need to be relayed out so we can compute new available space.
+ // This is only necessary for stretching since other alignment values don't change the size of the box.
+ for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
+ if (child->isOutOfFlowPositioned())
+ continue;
+ if (selfAlignmentChangedToStretchInRowAxis(*oldStyle, newStyle, child->style()) || selfAlignmentChangedFromStretchInRowAxis(*oldStyle, newStyle, child->style())
+ || selfAlignmentChangedFromStretchInColumnAxis(*oldStyle, newStyle, child->style())) {
+ child->setChildNeedsLayout(MarkOnlyThis);
}
}
- return nullptr;
}
-private:
- const Vector<Vector<Vector<RenderBox*, 1>>>& m_grid;
- TrackSizingDirection m_direction;
- size_t m_rowIndex;
- size_t m_columnIndex;
- size_t m_childIndex;
-};
+ if (explicitGridDidResize(*oldStyle) || namedGridLinesDefinitionDidChange(*oldStyle) || oldStyle->gridAutoFlow() != style().gridAutoFlow()
+ || (style().gridAutoRepeatColumns().size() || style().gridAutoRepeatRows().size()))
+ dirtyGrid();
+}
-class RenderGrid::GridSizingData {
- WTF_MAKE_NONCOPYABLE(GridSizingData);
-public:
- GridSizingData(size_t gridColumnCount, size_t gridRowCount)
- : columnTracks(gridColumnCount)
- , rowTracks(gridRowCount)
- {
- }
+bool RenderGrid::explicitGridDidResize(const RenderStyle& oldStyle) const
+{
+ return oldStyle.gridColumns().size() != style().gridColumns().size()
+ || oldStyle.gridRows().size() != style().gridRows().size()
+ || oldStyle.namedGridAreaColumnCount() != style().namedGridAreaColumnCount()
+ || oldStyle.namedGridAreaRowCount() != style().namedGridAreaRowCount()
+ || oldStyle.gridAutoRepeatColumns().size() != style().gridAutoRepeatColumns().size()
+ || oldStyle.gridAutoRepeatRows().size() != style().gridAutoRepeatRows().size();
+}
- Vector<GridTrack> columnTracks;
- Vector<GridTrack> rowTracks;
- Vector<size_t> contentSizedTracksIndex;
+bool RenderGrid::namedGridLinesDefinitionDidChange(const RenderStyle& oldStyle) const
+{
+ return oldStyle.namedGridRowLines() != style().namedGridRowLines()
+ || oldStyle.namedGridColumnLines() != style().namedGridColumnLines();
+}
- // Performance optimization: hold onto these Vectors until the end of Layout to avoid repeated malloc / free.
- Vector<LayoutUnit> distributeTrackVector;
- Vector<GridTrack*> filteredTracks;
-};
+LayoutUnit RenderGrid::computeTrackBasedLogicalHeight() const
+{
+ LayoutUnit logicalHeight;
-RenderGrid::RenderGrid(Element& element, PassRef<RenderStyle> style)
- : RenderBlock(element, std::move(style), 0)
- , m_orderIterator(*this)
+ auto& allRows = m_trackSizingAlgorithm.tracks(ForRows);
+ for (const auto& row : allRows)
+ logicalHeight += row.baseSize();
+
+ logicalHeight += guttersSize(m_grid, ForRows, 0, allRows.size());
+
+ return logicalHeight;
+}
+
+void RenderGrid::computeTrackSizesForDefiniteSize(GridTrackSizingDirection direction, LayoutUnit availableSpace)
{
- // All of our children must be block level.
- setChildrenInline(false);
+ LayoutUnit totalGuttersSize = guttersSize(m_grid, direction, 0, m_grid.numTracks(direction));
+ LayoutUnit freeSpace = availableSpace - totalGuttersSize;
+
+ m_trackSizingAlgorithm.setup(direction, numTracks(direction, m_grid), TrackSizing, availableSpace, freeSpace);
+ m_trackSizingAlgorithm.run();
+
+ ASSERT(m_trackSizingAlgorithm.tracksAreWiderThanMinTrackBreadth());
}
-RenderGrid::~RenderGrid()
+void RenderGrid::repeatTracksSizingIfNeeded(LayoutUnit availableSpaceForColumns, LayoutUnit availableSpaceForRows)
{
+ // In orthogonal flow cases column track's size is determined by using the computed
+ // row track's size, which it was estimated during the first cycle of the sizing
+ // algorithm. Hence we need to repeat computeUsedBreadthOfGridTracks for both,
+ // columns and rows, to determine the final values.
+ // TODO (lajava): orthogonal flows is just one of the cases which may require
+ // a new cycle of the sizing algorithm; there may be more. In addition, not all the
+ // cases with orthogonal flows require this extra cycle; we need a more specific
+ // condition to detect whether child's min-content contribution has changed or not.
+ if (m_grid.hasAnyOrthogonalGridItem()) {
+ computeTrackSizesForDefiniteSize(ForColumns, availableSpaceForColumns);
+ computeTrackSizesForDefiniteSize(ForRows, availableSpaceForRows);
+ }
+}
+
+bool RenderGrid::canPerformSimplifiedLayout() const
+{
+ // We cannot perform a simplified layout if we need to position the items and we have some
+ // positioned items to be laid out.
+ if (m_grid.needsItemsPlacement() && posChildNeedsLayout())
+ return false;
+
+ return RenderBlock::canPerformSimplifiedLayout();
}
void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit)
@@ -178,29 +233,88 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit)
if (!relayoutChildren && simplifiedLayout())
return;
- // FIXME: Much of this method is boiler plate that matches RenderBox::layoutBlock and Render*FlexibleBox::layoutBlock.
- // It would be nice to refactor some of the duplicate code.
LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
- prepareShapesAndPaginationBeforeBlockLayout(relayoutChildren);
+ preparePaginationBeforeBlockLayout(relayoutChildren);
LayoutSize previousSize = size();
+ // We need to clear both own and containingBlock override sizes of orthogonal items to ensure we get the
+ // same result when grid's intrinsic size is computed again in the updateLogicalWidth call bellow.
+ if (sizesLogicalWidthToFitContent(MaxSize) || style().logicalWidth().isIntrinsicOrAuto()) {
+ for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) {
+ if (child->isOutOfFlowPositioned() || !isOrthogonalChild(*child))
+ continue;
+ child->clearOverrideSize();
+ child->clearContainingBlockOverrideSize();
+ child->setNeedsLayout();
+ child->layoutIfNeeded();
+ }
+ }
+
setLogicalHeight(0);
updateLogicalWidth();
- layoutGridItems();
+ placeItemsOnGrid(m_grid, TrackSizing);
+
+ // At this point the logical width is always definite as the above call to updateLogicalWidth()
+ // properly resolves intrinsic sizes. We cannot do the same for heights though because many code
+ // paths inside updateLogicalHeight() require a previous call to setLogicalHeight() to resolve
+ // heights properly (like for positioned items for example).
+ LayoutUnit availableSpaceForColumns = availableLogicalWidth();
+ computeTrackSizesForDefiniteSize(ForColumns, availableSpaceForColumns);
+
+ // FIXME: We should use RenderBlock::hasDefiniteLogicalHeight() but it does not work for positioned stuff.
+ // FIXME: Consider caching the hasDefiniteLogicalHeight value throughout the layout.
+ bool hasDefiniteLogicalHeight = hasOverrideLogicalContentHeight() || computeContentLogicalHeight(MainOrPreferredSize, style().logicalHeight(), std::nullopt);
+ if (!hasDefiniteLogicalHeight) {
+ m_minContentHeight = LayoutUnit();
+ m_maxContentHeight = LayoutUnit();
+ computeTrackSizesForIndefiniteSize(m_trackSizingAlgorithm, ForRows, m_grid, *m_minContentHeight, *m_maxContentHeight);
+ // FIXME: This should be really added to the intrinsic height in RenderBox::computeContentAndScrollbarLogicalHeightUsing().
+ // Remove this when that is fixed.
+ ASSERT(m_minContentHeight);
+ ASSERT(m_maxContentHeight);
+ LayoutUnit scrollbarHeight = scrollbarLogicalHeight();
+ *m_minContentHeight += scrollbarHeight;
+ *m_maxContentHeight += scrollbarHeight;
+ } else
+ computeTrackSizesForDefiniteSize(ForRows, availableLogicalHeight(ExcludeMarginBorderPadding));
+ LayoutUnit trackBasedLogicalHeight = computeTrackBasedLogicalHeight() + borderAndPaddingLogicalHeight() + scrollbarLogicalHeight();
+ setLogicalHeight(trackBasedLogicalHeight);
LayoutUnit oldClientAfterEdge = clientLogicalBottom();
updateLogicalHeight();
+ // Once grid's indefinite height is resolved, we can compute the
+ // available free space for Content Alignment.
+ if (!hasDefiniteLogicalHeight)
+ m_trackSizingAlgorithm.setFreeSpace(ForRows, logicalHeight() - trackBasedLogicalHeight);
+
+ // 3- If the min-content contribution of any grid items have changed based on the row
+ // sizes calculated in step 2, steps 1 and 2 are repeated with the new min-content
+ // contribution (once only).
+ repeatTracksSizingIfNeeded(availableSpaceForColumns, contentLogicalHeight());
+
+ // Grid container should have the minimum height of a line if it's editable. That does not affect track sizing though.
+ if (hasLineIfEmpty()) {
+ LayoutUnit minHeightForEmptyLine = borderAndPaddingLogicalHeight()
+ + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)
+ + scrollbarLogicalHeight();
+ setLogicalHeight(std::max(logicalHeight(), minHeightForEmptyLine));
+ }
+
+ applyStretchAlignmentToTracksIfNeeded(ForColumns);
+ applyStretchAlignmentToTracksIfNeeded(ForRows);
+
+ layoutGridItems();
+ m_trackSizingAlgorithm.reset();
+
if (size() != previousSize)
relayoutChildren = true;
- layoutPositionedObjects(relayoutChildren || isRoot());
-
- updateShapesAfterBlockLayout();
+ layoutPositionedObjects(relayoutChildren || isDocumentElementRenderer());
computeOverflow(oldClientAfterEdge);
statePusher.pop();
@@ -216,551 +330,580 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit)
clearNeedsLayout();
}
-void RenderGrid::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
+LayoutUnit RenderGrid::gridGapForDirection(GridTrackSizingDirection direction) const
{
- const_cast<RenderGrid*>(this)->placeItemsOnGrid();
-
- GridSizingData sizingData(gridColumnCount(), gridRowCount());
- LayoutUnit availableLogicalSpace = 0;
- const_cast<RenderGrid*>(this)->computedUsedBreadthOfGridTracks(ForColumns, sizingData, availableLogicalSpace);
-
- for (size_t i = 0; i < sizingData.columnTracks.size(); ++i) {
- LayoutUnit minTrackBreadth = sizingData.columnTracks[i].m_usedBreadth;
- LayoutUnit maxTrackBreadth = sizingData.columnTracks[i].m_maxBreadth;
- maxTrackBreadth = std::max(maxTrackBreadth, minTrackBreadth);
+ return valueForLength(direction == ForColumns ? style().gridColumnGap() : style().gridRowGap(), LayoutUnit());
+}
- minLogicalWidth += minTrackBreadth;
- maxLogicalWidth += maxTrackBreadth;
+LayoutUnit RenderGrid::guttersSize(const Grid& grid, GridTrackSizingDirection direction, unsigned startLine, unsigned span) const
+{
+ if (span <= 1)
+ return { };
- // FIXME: This should add in the scrollbarWidth (e.g. see RenderFlexibleBox).
- }
+ LayoutUnit gap = gridGapForDirection(direction);
- const_cast<RenderGrid*>(this)->clearGrid();
-}
+ // Fast path, no collapsing tracks.
+ if (!grid.hasAutoRepeatEmptyTracks(direction))
+ return gap * (span - 1);
-void RenderGrid::computePreferredLogicalWidths()
-{
- ASSERT(preferredLogicalWidthsDirty());
+ // If there are collapsing tracks we need to be sure that gutters are properly collapsed. Apart
+ // from that, if we have a collapsed track in the edges of the span we're considering, we need
+ // to move forward (or backwards) in order to know whether the collapsed tracks reach the end of
+ // the grid (so the gap becomes 0) or there is a non empty track before that.
- m_minPreferredLogicalWidth = 0;
- m_maxPreferredLogicalWidth = 0;
+ LayoutUnit gapAccumulator;
+ unsigned endLine = startLine + span;
- // FIXME: We don't take our own logical width into account. Once we do, we need to make sure
- // we apply (and test the interaction with) min-width / max-width.
+ for (unsigned line = startLine; line < endLine - 1; ++line) {
+ if (!grid.isEmptyAutoRepeatTrack(direction, line))
+ gapAccumulator += gap;
+ }
- computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
+ // The above loop adds one extra gap for trailing collapsed tracks.
+ if (gapAccumulator && grid.isEmptyAutoRepeatTrack(direction, endLine - 1)) {
+ ASSERT(gapAccumulator >= gap);
+ gapAccumulator -= gap;
+ }
- LayoutUnit borderAndPaddingInInlineDirection = borderAndPaddingLogicalWidth();
- m_minPreferredLogicalWidth += borderAndPaddingInInlineDirection;
- m_maxPreferredLogicalWidth += borderAndPaddingInInlineDirection;
+ // If the startLine is the start line of a collapsed track we need to go backwards till we reach
+ // a non collapsed track. If we find a non collapsed track we need to add that gap.
+ if (startLine && grid.isEmptyAutoRepeatTrack(direction, startLine)) {
+ unsigned nonEmptyTracksBeforeStartLine = startLine;
+ auto begin = grid.autoRepeatEmptyTracks(direction)->begin();
+ for (auto it = begin; *it != startLine; ++it) {
+ ASSERT(nonEmptyTracksBeforeStartLine);
+ --nonEmptyTracksBeforeStartLine;
+ }
+ if (nonEmptyTracksBeforeStartLine)
+ gapAccumulator += gap;
+ }
- setPreferredLogicalWidthsDirty(false);
-}
+ // If the endLine is the end line of a collapsed track we need to go forward till we reach a non
+ // collapsed track. If we find a non collapsed track we need to add that gap.
+ if (grid.isEmptyAutoRepeatTrack(direction, endLine - 1)) {
+ unsigned nonEmptyTracksAfterEndLine = grid.numTracks(direction) - endLine;
+ auto currentEmptyTrack = grid.autoRepeatEmptyTracks(direction)->find(endLine - 1);
+ auto endEmptyTrack = grid.autoRepeatEmptyTracks(direction)->end();
+ // HashSet iterators do not implement operator- so we have to manually iterate to know the number of remaining empty tracks.
+ for (auto it = ++currentEmptyTrack; it != endEmptyTrack; ++it) {
+ ASSERT(nonEmptyTracksAfterEndLine >= 1);
+ --nonEmptyTracksAfterEndLine;
+ }
+ if (nonEmptyTracksAfterEndLine)
+ gapAccumulator += gap;
+ }
-void RenderGrid::computedUsedBreadthOfGridTracks(TrackSizingDirection direction, GridSizingData& sizingData)
-{
- LayoutUnit availableLogicalSpace = (direction == ForColumns) ? availableLogicalWidth() : availableLogicalHeight(IncludeMarginBorderPadding);
- computedUsedBreadthOfGridTracks(direction, sizingData, availableLogicalSpace);
+ return gapAccumulator;
}
-void RenderGrid::computedUsedBreadthOfGridTracks(TrackSizingDirection direction, GridSizingData& sizingData, LayoutUnit& availableLogicalSpace)
+void RenderGrid::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
{
- Vector<GridTrack>& tracks = (direction == ForColumns) ? sizingData.columnTracks : sizingData.rowTracks;
- sizingData.contentSizedTracksIndex.shrink(0);
- for (size_t i = 0; i < tracks.size(); ++i) {
- GridTrack& track = tracks[i];
- const GridTrackSize& trackSize = gridTrackSize(direction, i);
- const GridLength& minTrackBreadth = trackSize.minTrackBreadth();
- const GridLength& maxTrackBreadth = trackSize.maxTrackBreadth();
+ Grid grid(const_cast<RenderGrid&>(*this));
+ placeItemsOnGrid(grid, IntrinsicSizeComputation);
- track.m_usedBreadth = computeUsedBreadthOfMinLength(direction, minTrackBreadth);
- track.m_maxBreadth = computeUsedBreadthOfMaxLength(direction, maxTrackBreadth, track.m_usedBreadth);
+ GridTrackSizingAlgorithm algorithm(this, grid);
+ computeTrackSizesForIndefiniteSize(algorithm, ForColumns, grid, minLogicalWidth, maxLogicalWidth);
- track.m_maxBreadth = std::max(track.m_maxBreadth, track.m_usedBreadth);
-
- if (trackSize.isContentSized())
- sizingData.contentSizedTracksIndex.append(i);
- }
+ LayoutUnit scrollbarWidth = intrinsicScrollbarLogicalWidth();
+ minLogicalWidth += scrollbarWidth;
+ maxLogicalWidth += scrollbarWidth;
+}
- if (!sizingData.contentSizedTracksIndex.isEmpty())
- resolveContentBasedTrackSizingFunctions(direction, sizingData);
+void RenderGrid::computeTrackSizesForIndefiniteSize(GridTrackSizingAlgorithm& algorithm, GridTrackSizingDirection direction, Grid& grid, LayoutUnit& minIntrinsicSize, LayoutUnit& maxIntrinsicSize) const
+{
+ algorithm.setup(direction, numTracks(direction, grid), IntrinsicSizeComputation, std::nullopt, std::nullopt);
+ algorithm.run();
- for (size_t i = 0; i < tracks.size(); ++i) {
- ASSERT(tracks[i].m_maxBreadth != infinity);
- availableLogicalSpace -= tracks[i].m_usedBreadth;
- }
+ size_t numberOfTracks = algorithm.tracks(direction).size();
+ LayoutUnit totalGuttersSize = guttersSize(grid, direction, 0, numberOfTracks);
- if (availableLogicalSpace <= 0)
- return;
+ minIntrinsicSize = algorithm.minContentSize() + totalGuttersSize;
+ maxIntrinsicSize = algorithm.maxContentSize() + totalGuttersSize;
- const size_t tracksSize = tracks.size();
- Vector<GridTrack*> tracksForDistribution(tracksSize);
- for (size_t i = 0; i < tracksSize; ++i)
- tracksForDistribution[i] = tracks.data() + i;
+ ASSERT(algorithm.tracksAreWiderThanMinTrackBreadth());
+}
- distributeSpaceToTracks(tracksForDistribution, 0, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth, sizingData, availableLogicalSpace);
+std::optional<LayoutUnit> RenderGrid::computeIntrinsicLogicalContentHeightUsing(Length logicalHeightLength, std::optional<LayoutUnit> intrinsicLogicalHeight, LayoutUnit borderAndPadding) const
+{
+ if (!intrinsicLogicalHeight)
+ return std::nullopt;
- // 4. Grow all Grid tracks having a fraction as the MaxTrackSizingFunction.
+ if (logicalHeightLength.isMinContent())
+ return m_minContentHeight;
- // FIXME: Handle the case where RemainingSpace is not defined.
- double normalizedFractionBreadth = computeNormalizedFractionBreadth(tracks, direction, availableLogicalSpace);
- for (size_t i = 0; i < tracksSize; ++i) {
- const GridTrackSize& trackSize = gridTrackSize(direction, i);
- if (!trackSize.maxTrackBreadth().isFlex())
- continue;
+ if (logicalHeightLength.isMaxContent())
+ return m_maxContentHeight;
- tracks[i].m_usedBreadth = std::max<LayoutUnit>(tracks[i].m_usedBreadth, normalizedFractionBreadth * trackSize.maxTrackBreadth().flex());
+ if (logicalHeightLength.isFitContent()) {
+ LayoutUnit fillAvailableExtent = containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding);
+ return std::min(m_maxContentHeight.value_or(0), std::max(m_minContentHeight.value_or(0), fillAvailableExtent));
}
+
+ if (logicalHeightLength.isFillAvailable())
+ return containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding) - borderAndPadding;
+ ASSERT_NOT_REACHED();
+ return std::nullopt;
}
-LayoutUnit RenderGrid::computeUsedBreadthOfMinLength(TrackSizingDirection direction, const GridLength& gridLength) const
+static std::optional<LayoutUnit> overrideContainingBlockContentSizeForChild(const RenderBox& child, GridTrackSizingDirection direction)
{
- if (gridLength.isFlex())
- return 0;
-
- const Length& trackLength = gridLength.length();
- ASSERT(!trackLength.isAuto());
- if (trackLength.isFixed() || trackLength.isPercent() || trackLength.isViewportPercentage())
- return computeUsedBreadthOfSpecifiedLength(direction, trackLength);
-
- ASSERT(trackLength.isMinContent() || trackLength.isMaxContent());
- return 0;
+ return direction == ForColumns ? child.overrideContainingBlockContentLogicalWidth() : child.overrideContainingBlockContentLogicalHeight();
}
-LayoutUnit RenderGrid::computeUsedBreadthOfMaxLength(TrackSizingDirection direction, const GridLength& gridLength, LayoutUnit usedBreadth) const
+bool RenderGrid::isOrthogonalChild(const RenderBox& child) const
{
- if (gridLength.isFlex())
- return usedBreadth;
-
- const Length& trackLength = gridLength.length();
- ASSERT(!trackLength.isAuto());
- if (trackLength.isFixed() || trackLength.isPercent() || trackLength.isViewportPercentage()) {
- LayoutUnit computedBreadth = computeUsedBreadthOfSpecifiedLength(direction, trackLength);
- ASSERT(computedBreadth != infinity);
- return computedBreadth;
- }
-
- ASSERT(trackLength.isMinContent() || trackLength.isMaxContent());
- return infinity;
+ return child.isHorizontalWritingMode() != isHorizontalWritingMode();
}
-LayoutUnit RenderGrid::computeUsedBreadthOfSpecifiedLength(TrackSizingDirection direction, const Length& trackLength) const
+GridTrackSizingDirection RenderGrid::flowAwareDirectionForChild(const RenderBox& child, GridTrackSizingDirection direction) const
{
- // FIXME: We still need to support calc() here (https://webkit.org/b/103761).
- ASSERT(trackLength.isFixed() || trackLength.isPercent() || trackLength.isViewportPercentage());
- return valueForLength(trackLength, direction == ForColumns ? logicalWidth() : computeContentLogicalHeight(style().logicalHeight()));
+ return !isOrthogonalChild(child) ? direction : (direction == ForColumns ? ForRows : ForColumns);
}
-double RenderGrid::computeNormalizedFractionBreadth(Vector<GridTrack>& tracks, TrackSizingDirection direction, LayoutUnit availableLogicalSpace) const
+unsigned RenderGrid::computeAutoRepeatTracksCount(GridTrackSizingDirection direction, SizingOperation sizingOperation) const
{
- // |availableLogicalSpace| already accounts for the used breadths so no need to remove it here.
+ bool isRowAxis = direction == ForColumns;
+ const auto& autoRepeatTracks = isRowAxis ? style().gridAutoRepeatColumns() : style().gridAutoRepeatRows();
+ unsigned autoRepeatTrackListLength = autoRepeatTracks.size();
- Vector<GridTrackForNormalization> tracksForNormalization;
- for (size_t i = 0; i < tracks.size(); ++i) {
- const GridTrackSize& trackSize = gridTrackSize(direction, i);
- if (!trackSize.maxTrackBreadth().isFlex())
- continue;
+ if (!autoRepeatTrackListLength)
+ return 0;
- tracksForNormalization.append(GridTrackForNormalization(tracks[i], trackSize.maxTrackBreadth().flex()));
+ std::optional<LayoutUnit> availableSize;
+ if (isRowAxis) {
+ if (sizingOperation != IntrinsicSizeComputation)
+ availableSize = availableLogicalWidth();
+ } else {
+ availableSize = computeContentLogicalHeight(MainOrPreferredSize, style().logicalHeight(), std::nullopt);
+ if (!availableSize) {
+ const Length& maxLength = style().logicalMaxHeight();
+ if (!maxLength.isUndefined())
+ availableSize = computeContentLogicalHeight(MaxSize, maxLength, std::nullopt);
+ }
+ if (availableSize)
+ availableSize = constrainContentBoxLogicalHeightByMinMax(availableSize.value(), std::nullopt);
}
- // FIXME: Ideally we shouldn't come here without any <flex> grid track.
- if (tracksForNormalization.isEmpty())
- return LayoutUnit();
-
- std::sort(tracksForNormalization.begin(), tracksForNormalization.end(),
- [](const GridTrackForNormalization& track1, const GridTrackForNormalization& track2) {
- return track1.m_normalizedFlexValue < track2.m_normalizedFlexValue;
- });
-
- // These values work together: as we walk over our grid tracks, we increase fractionValueBasedOnGridItemsRatio
- // to match a grid track's usedBreadth to <flex> ratio until the total fractions sized grid tracks wouldn't
- // fit into availableLogicalSpaceIgnoringFractionTracks.
- double accumulatedFractions = 0;
- LayoutUnit fractionValueBasedOnGridItemsRatio = 0;
- LayoutUnit availableLogicalSpaceIgnoringFractionTracks = availableLogicalSpace;
-
- for (size_t i = 0; i < tracksForNormalization.size(); ++i) {
- const GridTrackForNormalization& track = tracksForNormalization[i];
- if (track.m_normalizedFlexValue > fractionValueBasedOnGridItemsRatio) {
- // If the normalized flex value (we ordered |tracksForNormalization| by increasing normalized flex value)
- // will make us overflow our container, then stop. We have the previous step's ratio is the best fit.
- if (track.m_normalizedFlexValue * accumulatedFractions > availableLogicalSpaceIgnoringFractionTracks)
- break;
+ bool needsToFulfillMinimumSize = false;
+ if (!availableSize) {
+ const Length& minSize = isRowAxis ? style().logicalMinWidth() : style().logicalMinHeight();
+ if (!minSize.isSpecified())
+ return autoRepeatTrackListLength;
- fractionValueBasedOnGridItemsRatio = track.m_normalizedFlexValue;
- }
+ LayoutUnit containingBlockAvailableSize = isRowAxis ? containingBlockLogicalWidthForContent() : containingBlockLogicalHeightForContent(ExcludeMarginBorderPadding);
+ availableSize = valueForLength(minSize, containingBlockAvailableSize);
+ needsToFulfillMinimumSize = true;
+ }
- accumulatedFractions += track.m_flex;
- // This item was processed so we re-add its used breadth to the available space to accurately count the remaining space.
- availableLogicalSpaceIgnoringFractionTracks += track.m_track->m_usedBreadth;
+ LayoutUnit autoRepeatTracksSize;
+ for (auto& autoTrackSize : autoRepeatTracks) {
+ ASSERT(autoTrackSize.minTrackBreadth().isLength());
+ ASSERT(!autoTrackSize.minTrackBreadth().isFlex());
+ bool hasDefiniteMaxTrackSizingFunction = autoTrackSize.maxTrackBreadth().isLength() && !autoTrackSize.maxTrackBreadth().isContentSized();
+ auto trackLength = hasDefiniteMaxTrackSizingFunction ? autoTrackSize.maxTrackBreadth().length() : autoTrackSize.minTrackBreadth().length();
+ autoRepeatTracksSize += valueForLength(trackLength, availableSize.value());
+ }
+ // For the purpose of finding the number of auto-repeated tracks, the UA must floor the track size to a UA-specified
+ // value to avoid division by zero. It is suggested that this floor be 1px.
+ autoRepeatTracksSize = std::max<LayoutUnit>(LayoutUnit(1), autoRepeatTracksSize);
+
+ // There will be always at least 1 auto-repeat track, so take it already into account when computing the total track size.
+ LayoutUnit tracksSize = autoRepeatTracksSize;
+ auto& trackSizes = isRowAxis ? style().gridColumns() : style().gridRows();
+
+ for (const auto& track : trackSizes) {
+ bool hasDefiniteMaxTrackBreadth = track.maxTrackBreadth().isLength() && !track.maxTrackBreadth().isContentSized();
+ ASSERT(hasDefiniteMaxTrackBreadth || (track.minTrackBreadth().isLength() && !track.minTrackBreadth().isContentSized()));
+ tracksSize += valueForLength(hasDefiniteMaxTrackBreadth ? track.maxTrackBreadth().length() : track.minTrackBreadth().length(), availableSize.value());
}
- return availableLogicalSpaceIgnoringFractionTracks / accumulatedFractions;
-}
+ // Add gutters as if there where only 1 auto repeat track. Gaps between auto repeat tracks will be added later when
+ // computing the repetitions.
+ LayoutUnit gapSize = gridGapForDirection(direction);
+ tracksSize += gapSize * trackSizes.size();
-const GridTrackSize& RenderGrid::gridTrackSize(TrackSizingDirection direction, size_t i) const
-{
- const Vector<GridTrackSize>& trackStyles = (direction == ForColumns) ? style().gridColumns() : style().gridRows();
- if (i >= trackStyles.size())
- return (direction == ForColumns) ? style().gridAutoColumns() : style().gridAutoRows();
+ LayoutUnit freeSpace = availableSize.value() - tracksSize;
+ if (freeSpace <= 0)
+ return autoRepeatTrackListLength;
- return trackStyles[i];
-}
+ unsigned repetitions = 1 + (freeSpace / (autoRepeatTracksSize + gapSize)).toInt();
-size_t RenderGrid::explicitGridColumnCount() const
-{
- return style().gridColumns().size();
-}
+ // Provided the grid container does not have a definite size or max-size in the relevant axis,
+ // if the min size is definite then the number of repetitions is the largest possible positive
+ // integer that fulfills that minimum requirement.
+ if (needsToFulfillMinimumSize)
+ ++repetitions;
-size_t RenderGrid::explicitGridRowCount() const
-{
- return style().gridRows().size();
+ return repetitions * autoRepeatTrackListLength;
}
-size_t RenderGrid::explicitGridSizeForSide(GridPositionSide side) const
-{
- return (side == ColumnStartSide || side == ColumnEndSide) ? explicitGridColumnCount() : explicitGridRowCount();
-}
-LayoutUnit RenderGrid::logicalContentHeightForChild(RenderBox* child, Vector<GridTrack>& columnTracks)
+std::unique_ptr<OrderedTrackIndexSet> RenderGrid::computeEmptyTracksForAutoRepeat(Grid& grid, GridTrackSizingDirection direction) const
{
- if (child->style().logicalHeight().isPercent())
- child->setNeedsLayout(MarkOnlyThis);
+ bool isRowAxis = direction == ForColumns;
+ if ((isRowAxis && style().gridAutoRepeatColumnsType() != AutoFit)
+ || (!isRowAxis && style().gridAutoRepeatRowsType() != AutoFit))
+ return nullptr;
+
+ std::unique_ptr<OrderedTrackIndexSet> emptyTrackIndexes;
+ unsigned insertionPoint = isRowAxis ? style().gridAutoRepeatColumnsInsertionPoint() : style().gridAutoRepeatRowsInsertionPoint();
+ unsigned firstAutoRepeatTrack = insertionPoint + std::abs(grid.smallestTrackStart(direction));
+ unsigned lastAutoRepeatTrack = firstAutoRepeatTrack + grid.autoRepeatTracks(direction);
- child->setOverrideContainingBlockContentLogicalWidth(gridAreaBreadthForChild(child, ForColumns, columnTracks));
- // If |child| has a percentage logical height, we shouldn't let it override its intrinsic height, which is
- // what we are interested in here. Thus we need to set the override logical height to -1 (no possible resolution).
- child->setOverrideContainingBlockContentLogicalHeight(-1);
- child->layoutIfNeeded();
- return child->logicalHeight();
+ if (!grid.hasGridItems()) {
+ emptyTrackIndexes = std::make_unique<OrderedTrackIndexSet>();
+ for (unsigned trackIndex = firstAutoRepeatTrack; trackIndex < lastAutoRepeatTrack; ++trackIndex)
+ emptyTrackIndexes->add(trackIndex);
+ } else {
+ for (unsigned trackIndex = firstAutoRepeatTrack; trackIndex < lastAutoRepeatTrack; ++trackIndex) {
+ GridIterator iterator(grid, direction, trackIndex);
+ if (!iterator.nextGridItem()) {
+ if (!emptyTrackIndexes)
+ emptyTrackIndexes = std::make_unique<OrderedTrackIndexSet>();
+ emptyTrackIndexes->add(trackIndex);
+ }
+ }
+ }
+ return emptyTrackIndexes;
}
-LayoutUnit RenderGrid::minContentForChild(RenderBox* child, TrackSizingDirection direction, Vector<GridTrack>& columnTracks)
+void RenderGrid::placeItemsOnGrid(Grid& grid, SizingOperation sizingOperation) const
{
- bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode();
- // FIXME: Properly support orthogonal writing mode.
- if (hasOrthogonalWritingMode)
- return 0;
-
- if (direction == ForColumns) {
- // FIXME: It's unclear if we should return the intrinsic width or the preferred width.
- // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html
- return child->minPreferredLogicalWidth() + marginIntrinsicLogicalWidthForChild(*child);
+ unsigned autoRepeatColumns = computeAutoRepeatTracksCount(ForColumns, sizingOperation);
+ unsigned autoRepeatRows = computeAutoRepeatTracksCount(ForRows, sizingOperation);
+ if (autoRepeatColumns != grid.autoRepeatTracks(ForColumns) || autoRepeatRows != grid.autoRepeatTracks(ForRows)) {
+ grid.setNeedsItemsPlacement(true);
+ grid.setAutoRepeatTracks(autoRepeatRows, autoRepeatColumns);
}
- return logicalContentHeightForChild(child, columnTracks);
-}
+ if (!grid.needsItemsPlacement())
+ return;
-LayoutUnit RenderGrid::maxContentForChild(RenderBox* child, TrackSizingDirection direction, Vector<GridTrack>& columnTracks)
-{
- bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode();
- // FIXME: Properly support orthogonal writing mode.
- if (hasOrthogonalWritingMode)
- return LayoutUnit();
+ ASSERT(!grid.hasGridItems());
+ populateExplicitGridAndOrderIterator(grid);
- if (direction == ForColumns) {
- // FIXME: It's unclear if we should return the intrinsic width or the preferred width.
- // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html
- return child->maxPreferredLogicalWidth() + marginIntrinsicLogicalWidthForChild(*child);
- }
+ Vector<RenderBox*> autoMajorAxisAutoGridItems;
+ Vector<RenderBox*> specifiedMajorAxisAutoGridItems;
+ bool hasAnyOrthogonalGridItem = false;
+ for (auto* child = grid.orderIterator().first(); child; child = grid.orderIterator().next()) {
+ if (child->isOutOfFlowPositioned())
+ continue;
- return logicalContentHeightForChild(child, columnTracks);
-}
+ hasAnyOrthogonalGridItem = hasAnyOrthogonalGridItem || isOrthogonalChild(*child);
-void RenderGrid::resolveContentBasedTrackSizingFunctions(TrackSizingDirection direction, GridSizingData& sizingData)
-{
- // FIXME: Split the grid tracks into groups that doesn't overlap a <flex> grid track.
+ GridArea area = grid.gridItemArea(*child);
+ if (!area.rows.isIndefinite())
+ area.rows.translate(std::abs(grid.smallestTrackStart(ForRows)));
+ if (!area.columns.isIndefinite())
+ area.columns.translate(std::abs(grid.smallestTrackStart(ForColumns)));
- for (size_t i = 0; i < sizingData.contentSizedTracksIndex.size(); ++i) {
- GridIterator iterator(m_grid, direction, sizingData.contentSizedTracksIndex[i]);
- while (RenderBox* gridItem = iterator.nextGridItem()) {
- resolveContentBasedTrackSizingFunctionsForItems(direction, sizingData, gridItem, &GridTrackSize::hasMinOrMaxContentMinTrackBreadth, &RenderGrid::minContentForChild, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth);
- resolveContentBasedTrackSizingFunctionsForItems(direction, sizingData, gridItem, &GridTrackSize::hasMaxContentMinTrackBreadth, &RenderGrid::maxContentForChild, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth);
- resolveContentBasedTrackSizingFunctionsForItems(direction, sizingData, gridItem, &GridTrackSize::hasMinOrMaxContentMaxTrackBreadth, &RenderGrid::minContentForChild, &GridTrack::maxBreadthIfNotInfinite, &GridTrack::growMaxBreadth);
- resolveContentBasedTrackSizingFunctionsForItems(direction, sizingData, gridItem, &GridTrackSize::hasMaxContentMaxTrackBreadth, &RenderGrid::maxContentForChild, &GridTrack::maxBreadthIfNotInfinite, &GridTrack::growMaxBreadth);
+ if (area.rows.isIndefinite() || area.columns.isIndefinite()) {
+ grid.setGridItemArea(*child, area);
+ bool majorAxisDirectionIsForColumns = autoPlacementMajorAxisDirection() == ForColumns;
+ if ((majorAxisDirectionIsForColumns && area.columns.isIndefinite())
+ || (!majorAxisDirectionIsForColumns && area.rows.isIndefinite()))
+ autoMajorAxisAutoGridItems.append(child);
+ else
+ specifiedMajorAxisAutoGridItems.append(child);
+ continue;
}
+ grid.insert(*child, { area.rows, area.columns });
+ }
+ grid.setHasAnyOrthogonalGridItem(hasAnyOrthogonalGridItem);
- GridTrack& track = (direction == ForColumns) ? sizingData.columnTracks[i] : sizingData.rowTracks[i];
- if (track.m_maxBreadth == infinity)
- track.m_maxBreadth = track.m_usedBreadth;
+#if ENABLE(ASSERT)
+ if (grid.hasGridItems()) {
+ ASSERT(grid.numTracks(ForRows) >= GridPositionsResolver::explicitGridRowCount(style(), grid.autoRepeatTracks(ForRows)));
+ ASSERT(grid.numTracks(ForColumns) >= GridPositionsResolver::explicitGridColumnCount(style(), grid.autoRepeatTracks(ForColumns)));
}
-}
+#endif
-void RenderGrid::resolveContentBasedTrackSizingFunctionsForItems(TrackSizingDirection direction, GridSizingData& sizingData, RenderBox* gridItem, FilterFunction filterFunction, SizingFunction sizingFunction, AccumulatorGetter trackGetter, AccumulatorGrowFunction trackGrowthFunction)
-{
- const GridCoordinate coordinate = cachedGridCoordinate(gridItem);
- const size_t initialTrackIndex = (direction == ForColumns) ? coordinate.columns.initialPositionIndex : coordinate.rows.initialPositionIndex;
- const size_t finalTrackIndex = (direction == ForColumns) ? coordinate.columns.finalPositionIndex : coordinate.rows.finalPositionIndex;
+ placeSpecifiedMajorAxisItemsOnGrid(grid, specifiedMajorAxisAutoGridItems);
+ placeAutoMajorAxisItemsOnGrid(grid, autoMajorAxisAutoGridItems);
- sizingData.filteredTracks.shrink(0);
- for (size_t trackIndex = initialTrackIndex; trackIndex <= finalTrackIndex; ++trackIndex) {
- const GridTrackSize& trackSize = gridTrackSize(direction, trackIndex);
- if (!(trackSize.*filterFunction)())
- continue;
+ // Compute collapsible tracks for auto-fit.
+ grid.setAutoRepeatEmptyColumns(computeEmptyTracksForAutoRepeat(grid, ForColumns));
+ grid.setAutoRepeatEmptyRows(computeEmptyTracksForAutoRepeat(grid, ForRows));
- GridTrack& track = (direction == ForColumns) ? sizingData.columnTracks[trackIndex] : sizingData.rowTracks[trackIndex];
- sizingData.filteredTracks.append(&track);
- }
+ grid.setNeedsItemsPlacement(false);
- if (sizingData.filteredTracks.isEmpty())
- return;
+#if ENABLE(ASSERT)
+ for (auto* child = grid.orderIterator().first(); child; child = grid.orderIterator().next()) {
+ if (child->isOutOfFlowPositioned())
+ continue;
- LayoutUnit additionalBreadthSpace = (this->*sizingFunction)(gridItem, direction, sizingData.columnTracks);
- for (size_t trackIndexForSpace = initialTrackIndex; trackIndexForSpace <= finalTrackIndex; ++trackIndexForSpace) {
- GridTrack& track = (direction == ForColumns) ? sizingData.columnTracks[trackIndexForSpace] : sizingData.rowTracks[trackIndexForSpace];
- additionalBreadthSpace -= (track.*trackGetter)();
+ GridArea area = grid.gridItemArea(*child);
+ ASSERT(area.rows.isTranslatedDefinite() && area.columns.isTranslatedDefinite());
}
-
- // FIXME: We should pass different values for |tracksForGrowthAboveMaxBreadth|.
- distributeSpaceToTracks(sizingData.filteredTracks, &sizingData.filteredTracks, trackGetter, trackGrowthFunction, sizingData, additionalBreadthSpace);
+#endif
}
-static bool sortByGridTrackGrowthPotential(const GridTrack* track1, const GridTrack* track2)
+void RenderGrid::populateExplicitGridAndOrderIterator(Grid& grid) const
{
- return (track1->m_maxBreadth - track1->m_usedBreadth) < (track2->m_maxBreadth - track2->m_usedBreadth);
-}
+ OrderIteratorPopulator populator(grid.orderIterator());
+ int smallestRowStart = 0;
+ int smallestColumnStart = 0;
+ unsigned autoRepeatRows = grid.autoRepeatTracks(ForRows);
+ unsigned autoRepeatColumns = grid.autoRepeatTracks(ForColumns);
+ unsigned maximumRowIndex = GridPositionsResolver::explicitGridRowCount(style(), autoRepeatRows);
+ unsigned maximumColumnIndex = GridPositionsResolver::explicitGridColumnCount(style(), autoRepeatColumns);
-void RenderGrid::distributeSpaceToTracks(Vector<GridTrack*>& tracks, Vector<GridTrack*>* tracksForGrowthAboveMaxBreadth, AccumulatorGetter trackGetter, AccumulatorGrowFunction trackGrowthFunction, GridSizingData& sizingData, LayoutUnit& availableLogicalSpace)
-{
- std::sort(tracks.begin(), tracks.end(), sortByGridTrackGrowthPotential);
+ for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
+ if (child->isOutOfFlowPositioned())
+ continue;
- size_t tracksSize = tracks.size();
- sizingData.distributeTrackVector.resize(tracksSize);
+ populator.collectChild(*child);
- for (size_t i = 0; i < tracksSize; ++i) {
- GridTrack& track = *tracks[i];
- LayoutUnit availableLogicalSpaceShare = availableLogicalSpace / (tracksSize - i);
- LayoutUnit trackBreadth = (tracks[i]->*trackGetter)();
- LayoutUnit growthShare = std::max(LayoutUnit(), std::min(availableLogicalSpaceShare, track.m_maxBreadth - trackBreadth));
- // We should never shrink any grid track or else we can't guarantee we abide by our min-sizing function.
- sizingData.distributeTrackVector[i] = trackBreadth + growthShare;
- availableLogicalSpace -= growthShare;
- }
+ GridSpan rowPositions = GridPositionsResolver::resolveGridPositionsFromStyle(style(), *child, ForRows, autoRepeatRows);
+ if (!rowPositions.isIndefinite()) {
+ smallestRowStart = std::min(smallestRowStart, rowPositions.untranslatedStartLine());
+ maximumRowIndex = std::max<int>(maximumRowIndex, rowPositions.untranslatedEndLine());
+ } else {
+ // Grow the grid for items with a definite row span, getting the largest such span.
+ unsigned spanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(style(), *child, ForRows);
+ maximumRowIndex = std::max(maximumRowIndex, spanSize);
+ }
- if (availableLogicalSpace > 0 && tracksForGrowthAboveMaxBreadth) {
- tracksSize = tracksForGrowthAboveMaxBreadth->size();
- for (size_t i = 0; i < tracksSize; ++i) {
- LayoutUnit growthShare = availableLogicalSpace / (tracksSize - i);
- sizingData.distributeTrackVector[i] += growthShare;
- availableLogicalSpace -= growthShare;
+ GridSpan columnPositions = GridPositionsResolver::resolveGridPositionsFromStyle(style(), *child, ForColumns, autoRepeatColumns);
+ if (!columnPositions.isIndefinite()) {
+ smallestColumnStart = std::min(smallestColumnStart, columnPositions.untranslatedStartLine());
+ maximumColumnIndex = std::max<int>(maximumColumnIndex, columnPositions.untranslatedEndLine());
+ } else {
+ // Grow the grid for items with a definite column span, getting the largest such span.
+ unsigned spanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(style(), *child, ForColumns);
+ maximumColumnIndex = std::max(maximumColumnIndex, spanSize);
}
- }
- for (size_t i = 0; i < tracksSize; ++i) {
- LayoutUnit growth = sizingData.distributeTrackVector[i] - (tracks[i]->*trackGetter)();
- if (growth >= 0)
- (tracks[i]->*trackGrowthFunction)(growth);
+ grid.setGridItemArea(*child, { rowPositions, columnPositions });
}
-}
-#ifndef NDEBUG
-bool RenderGrid::tracksAreWiderThanMinTrackBreadth(TrackSizingDirection direction, const Vector<GridTrack>& tracks)
-{
- for (size_t i = 0; i < tracks.size(); ++i) {
- const GridTrackSize& trackSize = gridTrackSize(direction, i);
- const GridLength& minTrackBreadth = trackSize.minTrackBreadth();
- if (computeUsedBreadthOfMinLength(direction, minTrackBreadth) > tracks[i].m_usedBreadth)
- return false;
- }
- return true;
+ grid.setSmallestTracksStart(smallestRowStart, smallestColumnStart);
+ grid.ensureGridSize(maximumRowIndex + std::abs(smallestRowStart), maximumColumnIndex + std::abs(smallestColumnStart));
}
-#endif
-void RenderGrid::growGrid(TrackSizingDirection direction)
+std::unique_ptr<GridArea> RenderGrid::createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(Grid& grid, const RenderBox& gridItem, GridTrackSizingDirection specifiedDirection, const GridSpan& specifiedPositions) const
{
- if (direction == ForColumns) {
- const size_t oldColumnSize = m_grid[0].size();
- for (size_t row = 0; row < m_grid.size(); ++row)
- m_grid[row].grow(oldColumnSize + 1);
- } else {
- const size_t oldRowSize = m_grid.size();
- m_grid.grow(oldRowSize + 1);
- m_grid[oldRowSize].grow(m_grid[0].size());
- }
+ GridTrackSizingDirection crossDirection = specifiedDirection == ForColumns ? ForRows : ForColumns;
+ const unsigned endOfCrossDirection = grid.numTracks(crossDirection);
+ unsigned crossDirectionSpanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(style(), gridItem, crossDirection);
+ GridSpan crossDirectionPositions = GridSpan::translatedDefiniteGridSpan(endOfCrossDirection, endOfCrossDirection + crossDirectionSpanSize);
+ return std::make_unique<GridArea>(specifiedDirection == ForColumns ? crossDirectionPositions : specifiedPositions, specifiedDirection == ForColumns ? specifiedPositions : crossDirectionPositions);
}
-void RenderGrid::insertItemIntoGrid(RenderBox* child, const GridCoordinate& coordinate)
+void RenderGrid::placeSpecifiedMajorAxisItemsOnGrid(Grid& grid, const Vector<RenderBox*>& autoGridItems) const
{
- for (size_t row = coordinate.rows.initialPositionIndex; row <= coordinate.rows.finalPositionIndex; ++row) {
- for (size_t column = coordinate.columns.initialPositionIndex; column <= coordinate.columns.finalPositionIndex; ++column)
- m_grid[row][column].append(child);
+ bool isForColumns = autoPlacementMajorAxisDirection() == ForColumns;
+ bool isGridAutoFlowDense = style().isGridAutoFlowAlgorithmDense();
+
+ // Mapping between the major axis tracks (rows or columns) and the last auto-placed item's position inserted on
+ // that track. This is needed to implement "sparse" packing for items locked to a given track.
+ // See http://dev.w3.org/csswg/css-grid/#auto-placement-algorithm
+ HashMap<unsigned, unsigned, DefaultHash<unsigned>::Hash, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> minorAxisCursors;
+
+ for (auto& autoGridItem : autoGridItems) {
+ GridSpan majorAxisPositions = grid.gridItemSpan(*autoGridItem, autoPlacementMajorAxisDirection());
+ ASSERT(majorAxisPositions.isTranslatedDefinite());
+ ASSERT(grid.gridItemSpan(*autoGridItem, autoPlacementMinorAxisDirection()).isIndefinite());
+ unsigned minorAxisSpanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(style(), *autoGridItem, autoPlacementMinorAxisDirection());
+ unsigned majorAxisInitialPosition = majorAxisPositions.startLine();
+
+ GridIterator iterator(grid, autoPlacementMajorAxisDirection(), majorAxisPositions.startLine(), isGridAutoFlowDense ? 0 : minorAxisCursors.get(majorAxisInitialPosition));
+ std::unique_ptr<GridArea> emptyGridArea = iterator.nextEmptyGridArea(majorAxisPositions.integerSpan(), minorAxisSpanSize);
+ if (!emptyGridArea)
+ emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(grid, *autoGridItem, autoPlacementMajorAxisDirection(), majorAxisPositions);
+
+ grid.insert(*autoGridItem, *emptyGridArea);
+
+ if (!isGridAutoFlowDense)
+ minorAxisCursors.set(majorAxisInitialPosition, isForColumns ? emptyGridArea->rows.startLine() : emptyGridArea->columns.startLine());
}
- m_gridItemCoordinate.set(child, coordinate);
}
-void RenderGrid::insertItemIntoGrid(RenderBox* child, size_t rowTrack, size_t columnTrack)
+void RenderGrid::placeAutoMajorAxisItemsOnGrid(Grid& grid, const Vector<RenderBox*>& autoGridItems) const
{
- const GridSpan& rowSpan = resolveGridPositionsFromAutoPlacementPosition(child, ForRows, rowTrack);
- const GridSpan& columnSpan = resolveGridPositionsFromAutoPlacementPosition(child, ForColumns, columnTrack);
- insertItemIntoGrid(child, GridCoordinate(rowSpan, columnSpan));
+ AutoPlacementCursor autoPlacementCursor = {0, 0};
+ bool isGridAutoFlowDense = style().isGridAutoFlowAlgorithmDense();
+
+ for (auto& autoGridItem : autoGridItems) {
+ placeAutoMajorAxisItemOnGrid(grid, *autoGridItem, autoPlacementCursor);
+
+ if (isGridAutoFlowDense) {
+ autoPlacementCursor.first = 0;
+ autoPlacementCursor.second = 0;
+ }
+ }
}
-void RenderGrid::placeItemsOnGrid()
+void RenderGrid::placeAutoMajorAxisItemOnGrid(Grid& grid, RenderBox& gridItem, AutoPlacementCursor& autoPlacementCursor) const
{
- ASSERT(!gridWasPopulated());
- ASSERT(m_gridItemCoordinate.isEmpty());
+ ASSERT(grid.gridItemSpan(gridItem, autoPlacementMajorAxisDirection()).isIndefinite());
+ unsigned majorAxisSpanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(style(), gridItem, autoPlacementMajorAxisDirection());
+
+ const unsigned endOfMajorAxis = grid.numTracks(autoPlacementMajorAxisDirection());
+ unsigned majorAxisAutoPlacementCursor = autoPlacementMajorAxisDirection() == ForColumns ? autoPlacementCursor.second : autoPlacementCursor.first;
+ unsigned minorAxisAutoPlacementCursor = autoPlacementMajorAxisDirection() == ForColumns ? autoPlacementCursor.first : autoPlacementCursor.second;
+
+ std::unique_ptr<GridArea> emptyGridArea;
+ GridSpan minorAxisPositions = grid.gridItemSpan(gridItem, autoPlacementMinorAxisDirection());
+ if (minorAxisPositions.isTranslatedDefinite()) {
+ // Move to the next track in major axis if initial position in minor axis is before auto-placement cursor.
+ if (minorAxisPositions.startLine() < minorAxisAutoPlacementCursor)
+ majorAxisAutoPlacementCursor++;
+
+ if (majorAxisAutoPlacementCursor < endOfMajorAxis) {
+ GridIterator iterator(grid, autoPlacementMinorAxisDirection(), minorAxisPositions.startLine(), majorAxisAutoPlacementCursor);
+ emptyGridArea = iterator.nextEmptyGridArea(minorAxisPositions.integerSpan(), majorAxisSpanSize);
+ }
- populateExplicitGridAndOrderIterator();
+ if (!emptyGridArea)
+ emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(grid, gridItem, autoPlacementMinorAxisDirection(), minorAxisPositions);
+ } else {
+ unsigned minorAxisSpanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(style(), gridItem, autoPlacementMinorAxisDirection());
+
+ for (unsigned majorAxisIndex = majorAxisAutoPlacementCursor; majorAxisIndex < endOfMajorAxis; ++majorAxisIndex) {
+ GridIterator iterator(grid, autoPlacementMajorAxisDirection(), majorAxisIndex, minorAxisAutoPlacementCursor);
+ emptyGridArea = iterator.nextEmptyGridArea(majorAxisSpanSize, minorAxisSpanSize);
+
+ if (emptyGridArea) {
+ // Check that it fits in the minor axis direction, as we shouldn't grow in that direction here (it was already managed in populateExplicitGridAndOrderIterator()).
+ unsigned minorAxisFinalPositionIndex = autoPlacementMinorAxisDirection() == ForColumns ? emptyGridArea->columns.endLine() : emptyGridArea->rows.endLine();
+ const unsigned endOfMinorAxis = grid.numTracks(autoPlacementMinorAxisDirection());
+ if (minorAxisFinalPositionIndex <= endOfMinorAxis)
+ break;
+
+ // Discard empty grid area as it does not fit in the minor axis direction.
+ // We don't need to create a new empty grid area yet as we might find a valid one in the next iteration.
+ emptyGridArea = nullptr;
+ }
- Vector<RenderBox*> autoMajorAxisAutoGridItems;
- Vector<RenderBox*> specifiedMajorAxisAutoGridItems;
- GridAutoFlow autoFlow = style().gridAutoFlow();
- for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
- // FIXME: We never re-resolve positions if the grid is grown during auto-placement which may lead auto / <integer>
- // positions to not match the author's intent. The specification is unclear on what should be done in this case.
- OwnPtr<GridSpan> rowPositions = resolveGridPositionsFromStyle(child, ForRows);
- OwnPtr<GridSpan> columnPositions = resolveGridPositionsFromStyle(child, ForColumns);
- if (!rowPositions || !columnPositions) {
- GridSpan* majorAxisPositions = (autoPlacementMajorAxisDirection() == ForColumns) ? columnPositions.get() : rowPositions.get();
- if (!majorAxisPositions)
- autoMajorAxisAutoGridItems.append(child);
- else
- specifiedMajorAxisAutoGridItems.append(child);
- continue;
+ // As we're moving to the next track in the major axis we should reset the auto-placement cursor in the minor axis.
+ minorAxisAutoPlacementCursor = 0;
}
- insertItemIntoGrid(child, GridCoordinate(*rowPositions, *columnPositions));
- }
-
- ASSERT(gridRowCount() >= style().gridRows().size());
- ASSERT(gridColumnCount() >= style().gridColumns().size());
- if (autoFlow == AutoFlowNone) {
- // If we did collect some grid items, they won't be placed thus never laid out.
- ASSERT(!autoMajorAxisAutoGridItems.size());
- ASSERT(!specifiedMajorAxisAutoGridItems.size());
- return;
+ if (!emptyGridArea)
+ emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(grid, gridItem, autoPlacementMinorAxisDirection(), GridSpan::translatedDefiniteGridSpan(0, minorAxisSpanSize));
}
- placeSpecifiedMajorAxisItemsOnGrid(specifiedMajorAxisAutoGridItems);
- placeAutoMajorAxisItemsOnGrid(autoMajorAxisAutoGridItems);
+ grid.insert(gridItem, *emptyGridArea);
+ autoPlacementCursor.first = emptyGridArea->rows.startLine();
+ autoPlacementCursor.second = emptyGridArea->columns.startLine();
}
-void RenderGrid::populateExplicitGridAndOrderIterator()
+GridTrackSizingDirection RenderGrid::autoPlacementMajorAxisDirection() const
{
- // FIXME: We should find a way to share OrderValues's initialization code with RenderFlexibleBox.
- OrderIterator::OrderValues orderValues;
- size_t maximumRowIndex = std::max<size_t>(1, explicitGridRowCount());
- size_t maximumColumnIndex = std::max<size_t>(1, explicitGridColumnCount());
-
- for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
- // Avoid growing the vector for the common-case default value of 0. This optimizes the most common case which is
- // one or a few values with the default order 0
- int order = child->style().order();
- if (orderValues.isEmpty() || orderValues.last() != order)
- orderValues.append(order);
-
- // This function bypasses the cache (cachedGridCoordinate()) as it is used to build it.
- OwnPtr<GridSpan> rowPositions = resolveGridPositionsFromStyle(child, ForRows);
- OwnPtr<GridSpan> columnPositions = resolveGridPositionsFromStyle(child, ForColumns);
-
- // |positions| is 0 if we need to run the auto-placement algorithm. Our estimation ignores
- // this case as the auto-placement algorithm will grow the grid as needed.
- if (rowPositions)
- maximumRowIndex = std::max(maximumRowIndex, rowPositions->finalPositionIndex + 1);
- if (columnPositions)
- maximumColumnIndex = std::max(maximumColumnIndex, columnPositions->finalPositionIndex + 1);
- }
-
- m_grid.grow(maximumRowIndex);
- for (size_t i = 0; i < m_grid.size(); ++i)
- m_grid[i].grow(maximumColumnIndex);
-
- m_orderIterator.setOrderValues(std::move(orderValues));
+ return style().isGridAutoFlowDirectionColumn() ? ForColumns : ForRows;
}
-void RenderGrid::placeSpecifiedMajorAxisItemsOnGrid(Vector<RenderBox*> autoGridItems)
+GridTrackSizingDirection RenderGrid::autoPlacementMinorAxisDirection() const
{
- for (size_t i = 0; i < autoGridItems.size(); ++i) {
- OwnPtr<GridSpan> majorAxisPositions = resolveGridPositionsFromStyle(autoGridItems[i], autoPlacementMajorAxisDirection());
- GridIterator iterator(m_grid, autoPlacementMajorAxisDirection(), majorAxisPositions->initialPositionIndex);
- if (OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea()) {
- insertItemIntoGrid(autoGridItems[i], emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex);
- continue;
- }
-
- growGrid(autoPlacementMinorAxisDirection());
- OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea();
- ASSERT(emptyGridArea);
- insertItemIntoGrid(autoGridItems[i], emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex);
- }
+ return style().isGridAutoFlowDirectionColumn() ? ForRows : ForColumns;
}
-void RenderGrid::placeAutoMajorAxisItemsOnGrid(Vector<RenderBox*> autoGridItems)
+void RenderGrid::dirtyGrid()
{
- for (size_t i = 0; i < autoGridItems.size(); ++i)
- placeAutoMajorAxisItemOnGrid(autoGridItems[i]);
+ if (m_grid.needsItemsPlacement())
+ return;
+
+ m_grid.setNeedsItemsPlacement(true);
}
-void RenderGrid::placeAutoMajorAxisItemOnGrid(RenderBox* gridItem)
+Vector<LayoutUnit> RenderGrid::trackSizesForComputedStyle(GridTrackSizingDirection direction) const
{
- OwnPtr<GridSpan> minorAxisPositions = resolveGridPositionsFromStyle(gridItem, autoPlacementMinorAxisDirection());
- ASSERT(!resolveGridPositionsFromStyle(gridItem, autoPlacementMajorAxisDirection()));
- size_t minorAxisIndex = 0;
- if (minorAxisPositions) {
- minorAxisIndex = minorAxisPositions->initialPositionIndex;
- GridIterator iterator(m_grid, autoPlacementMinorAxisDirection(), minorAxisIndex);
- if (OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea()) {
- insertItemIntoGrid(gridItem, emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex);
- return;
- }
- } else {
- const size_t endOfMajorAxis = (autoPlacementMajorAxisDirection() == ForColumns) ? gridColumnCount() : gridRowCount();
- for (size_t majorAxisIndex = 0; majorAxisIndex < endOfMajorAxis; ++majorAxisIndex) {
- GridIterator iterator(m_grid, autoPlacementMajorAxisDirection(), majorAxisIndex);
- if (OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea()) {
- insertItemIntoGrid(gridItem, emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex);
- return;
- }
+ bool isRowAxis = direction == ForColumns;
+ auto& positions = isRowAxis ? m_columnPositions : m_rowPositions;
+ size_t numPositions = positions.size();
+ LayoutUnit offsetBetweenTracks = isRowAxis ? m_offsetBetweenColumns : m_offsetBetweenRows;
+
+ Vector<LayoutUnit> tracks;
+ if (numPositions < 2)
+ return tracks;
+
+ ASSERT(!m_grid.needsItemsPlacement());
+ bool hasCollapsedTracks = m_grid.hasAutoRepeatEmptyTracks(direction);
+ LayoutUnit gap = !hasCollapsedTracks ? gridGapForDirection(direction) : LayoutUnit();
+ tracks.reserveCapacity(numPositions - 1);
+ for (size_t i = 0; i < numPositions - 2; ++i)
+ tracks.append(positions[i + 1] - positions[i] - offsetBetweenTracks - gap);
+ tracks.append(positions[numPositions - 1] - positions[numPositions - 2]);
+
+ if (!hasCollapsedTracks)
+ return tracks;
+
+ size_t remainingEmptyTracks = m_grid.autoRepeatEmptyTracks(direction)->size();
+ size_t lastLine = tracks.size();
+ gap = gridGapForDirection(direction);
+ for (size_t i = 1; i < lastLine; ++i) {
+ if (m_grid.isEmptyAutoRepeatTrack(direction, i - 1))
+ --remainingEmptyTracks;
+ else {
+ // Remove the gap between consecutive non empty tracks. Remove it also just once for an
+ // arbitrary number of empty tracks between two non empty ones.
+ bool allRemainingTracksAreEmpty = remainingEmptyTracks == (lastLine - i);
+ if (!allRemainingTracksAreEmpty || !m_grid.isEmptyAutoRepeatTrack(direction, i))
+ tracks[i - 1] -= gap;
}
}
- // We didn't find an empty grid area so we need to create an extra major axis line and insert our gridItem in it.
- const size_t columnIndex = (autoPlacementMajorAxisDirection() == ForColumns) ? m_grid[0].size() : minorAxisIndex;
- const size_t rowIndex = (autoPlacementMajorAxisDirection() == ForColumns) ? minorAxisIndex : m_grid.size();
- growGrid(autoPlacementMajorAxisDirection());
- insertItemIntoGrid(gridItem, rowIndex, columnIndex);
+ return tracks;
}
-RenderGrid::TrackSizingDirection RenderGrid::autoPlacementMajorAxisDirection() const
+static const StyleContentAlignmentData& contentAlignmentNormalBehaviorGrid()
{
- GridAutoFlow flow = style().gridAutoFlow();
- ASSERT(flow != AutoFlowNone);
- return (flow == AutoFlowColumn) ? ForColumns : ForRows;
+ static const StyleContentAlignmentData normalBehavior = {ContentPositionNormal, ContentDistributionStretch};
+ return normalBehavior;
}
-RenderGrid::TrackSizingDirection RenderGrid::autoPlacementMinorAxisDirection() const
+void RenderGrid::applyStretchAlignmentToTracksIfNeeded(GridTrackSizingDirection direction)
{
- GridAutoFlow flow = style().gridAutoFlow();
- ASSERT(flow != AutoFlowNone);
- return (flow == AutoFlowColumn) ? ForRows : ForColumns;
-}
+ std::optional<LayoutUnit> freeSpace = m_trackSizingAlgorithm.freeSpace(direction);
+ if (!freeSpace
+ || freeSpace.value() <= 0
+ || (direction == ForColumns && style().resolvedJustifyContentDistribution(contentAlignmentNormalBehaviorGrid()) != ContentDistributionStretch)
+ || (direction == ForRows && style().resolvedAlignContentDistribution(contentAlignmentNormalBehaviorGrid()) != ContentDistributionStretch))
+ return;
-void RenderGrid::clearGrid()
-{
- m_grid.clear();
- m_gridItemCoordinate.clear();
+ // Spec defines auto-sized tracks as the ones with an 'auto' max-sizing function.
+ Vector<GridTrack>& allTracks = m_trackSizingAlgorithm.tracks(direction);
+ Vector<unsigned> autoSizedTracksIndex;
+ for (unsigned i = 0; i < allTracks.size(); ++i) {
+ const GridTrackSize& trackSize = m_trackSizingAlgorithm.gridTrackSize(direction, i, TrackSizing);
+ if (trackSize.hasAutoMaxTrackBreadth())
+ autoSizedTracksIndex.append(i);
+ }
+
+ unsigned numberOfAutoSizedTracks = autoSizedTracksIndex.size();
+ if (numberOfAutoSizedTracks < 1)
+ return;
+
+ LayoutUnit sizeToIncrease = freeSpace.value() / numberOfAutoSizedTracks;
+ for (const auto& trackIndex : autoSizedTracksIndex) {
+ auto& track = allTracks[trackIndex];
+ track.setBaseSize(track.baseSize() + sizeToIncrease);
+ }
+ m_trackSizingAlgorithm.setFreeSpace(direction, LayoutUnit());
}
void RenderGrid::layoutGridItems()
{
- placeItemsOnGrid();
-
- GridSizingData sizingData(gridColumnCount(), gridRowCount());
- computedUsedBreadthOfGridTracks(ForColumns, sizingData);
- ASSERT(tracksAreWiderThanMinTrackBreadth(ForColumns, sizingData.columnTracks));
- computedUsedBreadthOfGridTracks(ForRows, sizingData);
- ASSERT(tracksAreWiderThanMinTrackBreadth(ForRows, sizingData.rowTracks));
+ populateGridPositionsForDirection(ForColumns);
+ populateGridPositionsForDirection(ForRows);
for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
+ if (child->isOutOfFlowPositioned()) {
+ prepareChildForPositionedLayout(*child);
+ continue;
+ }
+
// Because the grid area cannot be styled, we don't need to adjust
// the grid breadth to account for 'box-sizing'.
- LayoutUnit oldOverrideContainingBlockContentLogicalWidth = child->hasOverrideContainingBlockLogicalWidth() ? child->overrideContainingBlockContentLogicalWidth() : LayoutUnit();
- LayoutUnit oldOverrideContainingBlockContentLogicalHeight = child->hasOverrideContainingBlockLogicalHeight() ? child->overrideContainingBlockContentLogicalHeight() : LayoutUnit();
-
- LayoutUnit overrideContainingBlockContentLogicalWidth = gridAreaBreadthForChild(child, ForColumns, sizingData.columnTracks);
- LayoutUnit overrideContainingBlockContentLogicalHeight = gridAreaBreadthForChild(child, ForRows, sizingData.rowTracks);
- if (oldOverrideContainingBlockContentLogicalWidth != overrideContainingBlockContentLogicalWidth || (oldOverrideContainingBlockContentLogicalHeight != overrideContainingBlockContentLogicalHeight && (child->hasRelativeLogicalHeight() || child->hasViewportPercentageLogicalHeight())))
+ std::optional<LayoutUnit> oldOverrideContainingBlockContentLogicalWidth = child->hasOverrideContainingBlockLogicalWidth() ? child->overrideContainingBlockContentLogicalWidth() : LayoutUnit();
+ std::optional<LayoutUnit> oldOverrideContainingBlockContentLogicalHeight = child->hasOverrideContainingBlockLogicalHeight() ? child->overrideContainingBlockContentLogicalHeight() : LayoutUnit();
+
+ LayoutUnit overrideContainingBlockContentLogicalWidth = gridAreaBreadthForChildIncludingAlignmentOffsets(*child, ForColumns);
+ LayoutUnit overrideContainingBlockContentLogicalHeight = gridAreaBreadthForChildIncludingAlignmentOffsets(*child, ForRows);
+ if (!oldOverrideContainingBlockContentLogicalWidth || oldOverrideContainingBlockContentLogicalWidth.value() != overrideContainingBlockContentLogicalWidth
+ || ((!oldOverrideContainingBlockContentLogicalHeight || oldOverrideContainingBlockContentLogicalHeight.value() != overrideContainingBlockContentLogicalHeight)
+ && child->hasRelativeLogicalHeight()))
child->setNeedsLayout(MarkOnlyThis);
child->setOverrideContainingBlockContentLogicalWidth(overrideContainingBlockContentLogicalWidth);
@@ -768,12 +911,18 @@ void RenderGrid::layoutGridItems()
LayoutRect oldChildRect = child->frameRect();
- // FIXME: Grid items should stretch to fill their cells. Once we
- // implement grid-{column,row}-align, we can also shrink to fit. For
- // now, just size as if we were a regular child.
+ // Stretching logic might force a child layout, so we need to run it before the layoutIfNeeded
+ // call to avoid unnecessary relayouts. This might imply that child margins, needed to correctly
+ // determine the available space before stretching, are not set yet.
+ applyStretchAlignmentToChildIfNeeded(*child);
+
child->layoutIfNeeded();
- child->setLogicalLocation(findChildLogicalPosition(child, sizingData));
+ // We need pending layouts to be done in order to compute auto-margins properly.
+ updateAutoMarginsInColumnAxisIfNeeded(*child);
+ updateAutoMarginsInRowAxisIfNeeded(*child);
+
+ child->setLogicalLocation(findChildLogicalPosition(*child));
// If the child moved, we have to repaint it as well as any floating/positioned
// descendants. An exception is if we need a layout. In this case, we know we're going to
@@ -781,257 +930,759 @@ void RenderGrid::layoutGridItems()
if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
child->repaintDuringLayoutIfMoved(oldChildRect);
}
+}
- for (size_t i = 0; i < sizingData.rowTracks.size(); ++i)
- setLogicalHeight(logicalHeight() + sizingData.rowTracks[i].m_usedBreadth);
-
- // FIXME: We should handle min / max logical height.
+void RenderGrid::prepareChildForPositionedLayout(RenderBox& child)
+{
+ ASSERT(child.isOutOfFlowPositioned());
+ child.containingBlock()->insertPositionedObject(child);
- setLogicalHeight(logicalHeight() + borderAndPaddingLogicalHeight());
- clearGrid();
+ RenderLayer* childLayer = child.layer();
+ childLayer->setStaticInlinePosition(borderAndPaddingStart());
+ childLayer->setStaticBlockPosition(borderAndPaddingBefore());
}
-GridCoordinate RenderGrid::cachedGridCoordinate(const RenderBox* gridItem) const
+void RenderGrid::layoutPositionedObject(RenderBox& child, bool relayoutChildren, bool fixedPositionObjectsOnly)
{
- ASSERT(m_gridItemCoordinate.contains(gridItem));
- return m_gridItemCoordinate.get(gridItem);
+ // FIXME: Properly support orthogonal writing mode.
+ if (!isOrthogonalChild(child)) {
+ LayoutUnit columnOffset = LayoutUnit();
+ LayoutUnit columnBreadth = LayoutUnit();
+ offsetAndBreadthForPositionedChild(child, ForColumns, columnOffset, columnBreadth);
+ LayoutUnit rowOffset = LayoutUnit();
+ LayoutUnit rowBreadth = LayoutUnit();
+ offsetAndBreadthForPositionedChild(child, ForRows, rowOffset, rowBreadth);
+
+ child.setOverrideContainingBlockContentLogicalWidth(columnBreadth);
+ child.setOverrideContainingBlockContentLogicalHeight(rowBreadth);
+ child.setExtraInlineOffset(columnOffset);
+ child.setExtraBlockOffset(rowOffset);
+
+ if (child.parent() == this) {
+ auto& childLayer = *child.layer();
+ childLayer.setStaticInlinePosition(borderStart() + columnOffset);
+ childLayer.setStaticBlockPosition(borderBefore() + rowOffset);
+ }
+ }
+
+ RenderBlock::layoutPositionedObject(child, relayoutChildren, fixedPositionObjectsOnly);
}
-GridSpan RenderGrid::resolveGridPositionsFromAutoPlacementPosition(const RenderBox*, TrackSizingDirection, size_t initialPosition) const
+void RenderGrid::offsetAndBreadthForPositionedChild(const RenderBox& child, GridTrackSizingDirection direction, LayoutUnit& offset, LayoutUnit& breadth)
{
- // FIXME: We don't support spanning with auto positions yet. Once we do, this is wrong. Also we should make
- // sure the grid can accomodate the new item as we only grow 1 position in a given direction.
- return GridSpan(initialPosition, initialPosition);
+ ASSERT(!isOrthogonalChild(child));
+ bool isRowAxis = direction == ForColumns;
+
+ unsigned autoRepeatCount = m_grid.autoRepeatTracks(direction);
+ GridSpan positions = GridPositionsResolver::resolveGridPositionsFromStyle(style(), child, direction, autoRepeatCount);
+ if (positions.isIndefinite()) {
+ offset = LayoutUnit();
+ breadth = isRowAxis ? clientLogicalWidth() : clientLogicalHeight();
+ return;
+ }
+
+ // For positioned items we cannot use GridSpan::translate() because we could end up with negative values, as the positioned items do not create implicit tracks per spec.
+ int smallestStart = std::abs(m_grid.smallestTrackStart(direction));
+ int startLine = positions.untranslatedStartLine() + smallestStart;
+ int endLine = positions.untranslatedEndLine() + smallestStart;
+
+ GridPosition startPosition = isRowAxis ? child.style().gridItemColumnStart() : child.style().gridItemRowStart();
+ GridPosition endPosition = isRowAxis ? child.style().gridItemColumnEnd() : child.style().gridItemRowEnd();
+ int lastLine = numTracks(direction, m_grid);
+
+ bool startIsAuto = startPosition.isAuto()
+ || (startPosition.isNamedGridArea() && !NamedLineCollection::isValidNamedLineOrArea(startPosition.namedGridLine(), style(), (direction == ForColumns) ? ColumnStartSide : RowStartSide))
+ || (startLine < 0)
+ || (startLine > lastLine);
+ bool endIsAuto = endPosition.isAuto()
+ || (endPosition.isNamedGridArea() && !NamedLineCollection::isValidNamedLineOrArea(endPosition.namedGridLine(), style(), (direction == ForColumns) ? ColumnEndSide : RowEndSide))
+ || (endLine < 0)
+ || (endLine > lastLine);
+
+ // We're normalizing the positions to avoid issues with RTL (as they're stored in the same order than LTR but adding an offset).
+ LayoutUnit start;
+ if (!startIsAuto) {
+ if (isRowAxis) {
+ if (style().isLeftToRightDirection())
+ start = m_columnPositions[startLine] - borderLogicalLeft();
+ else
+ start = logicalWidth() - translateRTLCoordinate(m_columnPositions[startLine]) - borderLogicalRight();
+ } else
+ start = m_rowPositions[startLine] - borderBefore();
+ }
+
+ LayoutUnit end = isRowAxis ? clientLogicalWidth() : clientLogicalHeight();
+ if (!endIsAuto) {
+ if (isRowAxis) {
+ if (style().isLeftToRightDirection())
+ end = m_columnPositions[endLine] - borderLogicalLeft();
+ else
+ end = logicalWidth() - translateRTLCoordinate(m_columnPositions[endLine]) - borderLogicalRight();
+ } else
+ end = m_rowPositions[endLine] - borderBefore();
+
+ // These vectors store line positions including gaps, but we shouldn't consider them for the edges of the grid.
+ if (endLine > 0 && endLine < lastLine) {
+ ASSERT(!m_grid.needsItemsPlacement());
+ end -= guttersSize(m_grid, direction, endLine - 1, 2);
+ end -= isRowAxis ? m_offsetBetweenColumns : m_offsetBetweenRows;
+ }
+ }
+
+ breadth = end - start;
+ offset = start;
+
+ if (isRowAxis && !style().isLeftToRightDirection() && !child.style().hasStaticInlinePosition(child.isHorizontalWritingMode())) {
+ // If the child doesn't have a static inline position (i.e. "left" and/or "right" aren't "auto",
+ // we need to calculate the offset from the left (even if we're in RTL).
+ if (endIsAuto)
+ offset = LayoutUnit();
+ else {
+ offset = translateRTLCoordinate(m_columnPositions[endLine]) - borderLogicalLeft();
+
+ if (endLine > 0 && endLine < lastLine) {
+ ASSERT(!m_grid.needsItemsPlacement());
+ offset += guttersSize(m_grid, direction, endLine - 1, 2);
+ offset += isRowAxis ? m_offsetBetweenColumns : m_offsetBetweenRows;
+ }
+ }
+ }
}
-PassOwnPtr<GridSpan> RenderGrid::resolveGridPositionsFromStyle(const RenderBox* gridItem, TrackSizingDirection direction) const
+LayoutUnit RenderGrid::gridAreaBreadthForChildIncludingAlignmentOffsets(const RenderBox& child, GridTrackSizingDirection direction) const
{
- const GridPosition& initialPosition = (direction == ForColumns) ? gridItem->style().gridItemColumnStart() : gridItem->style().gridItemRowStart();
- const GridPositionSide initialPositionSide = (direction == ForColumns) ? ColumnStartSide : RowStartSide;
- const GridPosition& finalPosition = (direction == ForColumns) ? gridItem->style().gridItemColumnEnd() : gridItem->style().gridItemRowEnd();
- const GridPositionSide finalPositionSide = (direction == ForColumns) ? ColumnEndSide : RowEndSide;
+ // We need the cached value when available because Content Distribution alignment properties
+ // may have some influence in the final grid area breadth.
+ const auto& tracks = m_trackSizingAlgorithm.tracks(direction);
+ const auto& span = m_grid.gridItemSpan(child, direction);
+ const auto& linePositions = (direction == ForColumns) ? m_columnPositions : m_rowPositions;
- // We should NEVER see both spans as they should have been handled during style resolve.
- ASSERT(!initialPosition.isSpan() || !finalPosition.isSpan());
+ LayoutUnit initialTrackPosition = linePositions[span.startLine()];
+ LayoutUnit finalTrackPosition = linePositions[span.endLine() - 1];
- if (initialPosition.shouldBeResolvedAgainstOppositePosition() && finalPosition.shouldBeResolvedAgainstOppositePosition()) {
- if (style().gridAutoFlow() == AutoFlowNone)
- return adoptPtr(new GridSpan(0, 0));
+ // Track Positions vector stores the 'start' grid line of each track, so we have to add last track's baseSize.
+ return finalTrackPosition - initialTrackPosition + tracks[span.endLine() - 1].baseSize();
+}
- // We can't get our grid positions without running the auto placement algorithm.
- return nullptr;
+void RenderGrid::populateGridPositionsForDirection(GridTrackSizingDirection direction)
+{
+ // Since we add alignment offsets and track gutters, grid lines are not always adjacent. Hence we will have to
+ // assume from now on that we just store positions of the initial grid lines of each track,
+ // except the last one, which is the only one considered as a final grid line of a track.
+
+ // The grid container's frame elements (border, padding and <content-position> offset) are sensible to the
+ // inline-axis flow direction. However, column lines positions are 'direction' unaware. This simplification
+ // allows us to use the same indexes to identify the columns independently on the inline-axis direction.
+ bool isRowAxis = direction == ForColumns;
+ auto& tracks = m_trackSizingAlgorithm.tracks(direction);
+ unsigned numberOfTracks = tracks.size();
+ unsigned numberOfLines = numberOfTracks + 1;
+ unsigned lastLine = numberOfLines - 1;
+
+ ContentAlignmentData offset = computeContentPositionAndDistributionOffset(direction, m_trackSizingAlgorithm.freeSpace(direction).value(), numberOfTracks);
+ auto& positions = isRowAxis ? m_columnPositions : m_rowPositions;
+ positions.resize(numberOfLines);
+ auto borderAndPadding = isRowAxis ? borderAndPaddingLogicalLeft() : borderAndPaddingBefore();
+ positions[0] = borderAndPadding + offset.positionOffset;
+ if (numberOfLines > 1) {
+ // If we have collapsed tracks we just ignore gaps here and add them later as we might not
+ // compute the gap between two consecutive tracks without examining the surrounding ones.
+ bool hasCollapsedTracks = m_grid.hasAutoRepeatEmptyTracks(direction);
+ LayoutUnit gap = !hasCollapsedTracks ? gridGapForDirection(direction) : LayoutUnit();
+ unsigned nextToLastLine = numberOfLines - 2;
+ for (unsigned i = 0; i < nextToLastLine; ++i)
+ positions[i + 1] = positions[i] + offset.distributionOffset + tracks[i].baseSize() + gap;
+ positions[lastLine] = positions[nextToLastLine] + tracks[nextToLastLine].baseSize();
+
+ // Adjust collapsed gaps. Collapsed tracks cause the surrounding gutters to collapse (they
+ // coincide exactly) except on the edges of the grid where they become 0.
+ if (hasCollapsedTracks) {
+ gap = gridGapForDirection(direction);
+ unsigned remainingEmptyTracks = m_grid.autoRepeatEmptyTracks(direction)->size();
+ LayoutUnit gapAccumulator;
+ for (unsigned i = 1; i < lastLine; ++i) {
+ if (m_grid.isEmptyAutoRepeatTrack(direction, i - 1))
+ --remainingEmptyTracks;
+ else {
+ // Add gap between consecutive non empty tracks. Add it also just once for an
+ // arbitrary number of empty tracks between two non empty ones.
+ bool allRemainingTracksAreEmpty = remainingEmptyTracks == (lastLine - i);
+ if (!allRemainingTracksAreEmpty || !m_grid.isEmptyAutoRepeatTrack(direction, i))
+ gapAccumulator += gap;
+ }
+ positions[i] += gapAccumulator;
+ }
+ positions[lastLine] += gapAccumulator;
+ }
}
+ auto& offsetBetweenTracks = isRowAxis ? m_offsetBetweenColumns : m_offsetBetweenRows;
+ offsetBetweenTracks = offset.distributionOffset;
+}
- if (initialPosition.shouldBeResolvedAgainstOppositePosition()) {
- // Infer the position from the final position ('auto / 1' or 'span 2 / 3' case).
- const size_t finalResolvedPosition = resolveGridPositionFromStyle(finalPosition, finalPositionSide);
- return resolveGridPositionAgainstOppositePosition(finalResolvedPosition, initialPosition, initialPositionSide);
+static LayoutUnit computeOverflowAlignmentOffset(OverflowAlignment overflow, LayoutUnit trackSize, LayoutUnit childSize)
+{
+ LayoutUnit offset = trackSize - childSize;
+ switch (overflow) {
+ case OverflowAlignmentSafe:
+ // If overflow is 'safe', we have to make sure we don't overflow the 'start'
+ // edge (potentially cause some data loss as the overflow is unreachable).
+ return std::max<LayoutUnit>(0, offset);
+ case OverflowAlignmentUnsafe:
+ case OverflowAlignmentDefault:
+ // If we overflow our alignment container and overflow is 'true' (default), we
+ // ignore the overflow and just return the value regardless (which may cause data
+ // loss as we overflow the 'start' edge).
+ return offset;
}
- if (finalPosition.shouldBeResolvedAgainstOppositePosition()) {
- // Infer our position from the initial position ('1 / auto' or '3 / span 2' case).
- const size_t initialResolvedPosition = resolveGridPositionFromStyle(initialPosition, initialPositionSide);
- return resolveGridPositionAgainstOppositePosition(initialResolvedPosition, finalPosition, finalPositionSide);
- }
+ ASSERT_NOT_REACHED();
+ return 0;
+}
- size_t resolvedInitialPosition = resolveGridPositionFromStyle(initialPosition, initialPositionSide);
- size_t resolvedFinalPosition = resolveGridPositionFromStyle(finalPosition, finalPositionSide);
+// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
+bool RenderGrid::needToStretchChildLogicalHeight(const RenderBox& child) const
+{
+ if (child.style().resolvedAlignSelf(style(), selfAlignmentNormalBehavior).position() != ItemPositionStretch)
+ return false;
- // If 'grid-row-end' specifies a line at or before that specified by 'grid-row-start', it computes to 'span 1'.
- if (resolvedFinalPosition < resolvedInitialPosition)
- resolvedFinalPosition = resolvedInitialPosition;
+ return isHorizontalWritingMode() && child.style().height().isAuto();
+}
- return adoptPtr(new GridSpan(resolvedInitialPosition, resolvedFinalPosition));
+// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
+LayoutUnit RenderGrid::marginLogicalHeightForChild(const RenderBox& child) const
+{
+ return isHorizontalWritingMode() ? child.verticalMarginExtent() : child.horizontalMarginExtent();
}
-inline static size_t adjustGridPositionForRowEndColumnEndSide(size_t resolvedPosition)
+LayoutUnit RenderGrid::computeMarginLogicalSizeForChild(GridTrackSizingDirection direction, const RenderBox& child) const
{
- return resolvedPosition ? resolvedPosition - 1 : 0;
+ if (!child.style().hasMargin())
+ return 0;
+
+ LayoutUnit marginStart;
+ LayoutUnit marginEnd;
+ if (direction == ForColumns)
+ child.computeInlineDirectionMargins(*this, child.containingBlockLogicalWidthForContentInRegion(nullptr), child.logicalWidth(), marginStart, marginEnd);
+ else
+ child.computeBlockDirectionMargins(*this, marginStart, marginEnd);
+
+ return marginStart + marginEnd;
}
-static size_t adjustGridPositionForSide(size_t resolvedPosition, RenderGrid::GridPositionSide side)
+LayoutUnit RenderGrid::availableAlignmentSpaceForChildBeforeStretching(LayoutUnit gridAreaBreadthForChild, const RenderBox& child) const
{
- // An item finishing on the N-th line belongs to the N-1-th cell.
- if (side == RenderGrid::ColumnEndSide || side == RenderGrid::RowEndSide)
- return adjustGridPositionForRowEndColumnEndSide(resolvedPosition);
+ // Because we want to avoid multiple layouts, stretching logic might be performed before
+ // children are laid out, so we can't use the child cached values. Hence, we need to
+ // compute margins in order to determine the available height before stretching.
+ return gridAreaBreadthForChild - (child.needsLayout() ? computeMarginLogicalSizeForChild(ForRows, child) : marginLogicalHeightForChild(child));
+}
- return resolvedPosition;
+StyleSelfAlignmentData RenderGrid::alignSelfForChild(const RenderBox& child) const
+{
+ return child.style().resolvedAlignSelf(style(), selfAlignmentNormalBehavior);
}
-size_t RenderGrid::resolveNamedGridLinePositionFromStyle(const GridPosition& position, GridPositionSide side) const
+StyleSelfAlignmentData RenderGrid::justifySelfForChild(const RenderBox& child) const
{
- ASSERT(!position.namedGridLine().isNull());
+ return child.style().resolvedJustifySelf(style(), selfAlignmentNormalBehavior);
+}
- const NamedGridLinesMap& gridLinesNames = (side == ColumnStartSide || side == ColumnEndSide) ? style().namedGridColumnLines() : style().namedGridRowLines();
- NamedGridLinesMap::const_iterator it = gridLinesNames.find(position.namedGridLine());
- if (it == gridLinesNames.end()) {
- if (position.isPositive())
- return 0;
- const size_t lastLine = explicitGridSizeForSide(side);
- return adjustGridPositionForSide(lastLine, side);
+// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
+void RenderGrid::applyStretchAlignmentToChildIfNeeded(RenderBox& child)
+{
+ ASSERT(child.overrideContainingBlockContentLogicalHeight());
+
+ // We clear height override values because we will decide now whether it's allowed or
+ // not, evaluating the conditions which might have changed since the old values were set.
+ child.clearOverrideLogicalContentHeight();
+
+ GridTrackSizingDirection childBlockDirection = flowAwareDirectionForChild(child, ForRows);
+ bool blockFlowIsColumnAxis = childBlockDirection == ForRows;
+ bool allowedToStretchChildBlockSize = blockFlowIsColumnAxis ? allowedToStretchChildAlongColumnAxis(child) : allowedToStretchChildAlongRowAxis(child);
+ if (allowedToStretchChildBlockSize) {
+ LayoutUnit stretchedLogicalHeight = availableAlignmentSpaceForChildBeforeStretching(overrideContainingBlockContentSizeForChild(child, childBlockDirection).value(), child);
+ LayoutUnit desiredLogicalHeight = child.constrainLogicalHeightByMinMax(stretchedLogicalHeight, LayoutUnit(-1));
+ child.setOverrideLogicalContentHeight(desiredLogicalHeight - child.borderAndPaddingLogicalHeight());
+ if (desiredLogicalHeight != child.logicalHeight()) {
+ // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905.
+ child.setLogicalHeight(LayoutUnit());
+ child.setNeedsLayout();
+ }
}
+}
- size_t namedGridLineIndex;
- if (position.isPositive())
- namedGridLineIndex = std::min<size_t>(position.integerPosition(), it->value.size()) - 1;
- else
- namedGridLineIndex = std::max<int>(it->value.size() - abs(position.integerPosition()), 0);
- return adjustGridPositionForSide(it->value[namedGridLineIndex], side);
+// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
+bool RenderGrid::hasAutoMarginsInColumnAxis(const RenderBox& child) const
+{
+ if (isHorizontalWritingMode())
+ return child.style().marginTop().isAuto() || child.style().marginBottom().isAuto();
+ return child.style().marginLeft().isAuto() || child.style().marginRight().isAuto();
}
-size_t RenderGrid::resolveGridPositionFromStyle(const GridPosition& position, GridPositionSide side) const
+// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
+bool RenderGrid::hasAutoMarginsInRowAxis(const RenderBox& child) const
{
- switch (position.type()) {
- case ExplicitPosition: {
- ASSERT(position.integerPosition());
+ if (isHorizontalWritingMode())
+ return child.style().marginLeft().isAuto() || child.style().marginRight().isAuto();
+ return child.style().marginTop().isAuto() || child.style().marginBottom().isAuto();
+}
- if (!position.namedGridLine().isNull())
- return resolveNamedGridLinePositionFromStyle(position, side);
+// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
+void RenderGrid::updateAutoMarginsInRowAxisIfNeeded(RenderBox& child)
+{
+ ASSERT(!child.isOutOfFlowPositioned());
- // Handle <integer> explicit position.
- if (position.isPositive())
- return adjustGridPositionForSide(position.integerPosition() - 1, side);
+ LayoutUnit availableAlignmentSpace = child.overrideContainingBlockContentLogicalWidth().value() - child.logicalWidth() - child.marginLogicalWidth();
+ if (availableAlignmentSpace <= 0)
+ return;
- size_t resolvedPosition = abs(position.integerPosition()) - 1;
- const size_t endOfTrack = explicitGridSizeForSide(side);
+ const RenderStyle& parentStyle = style();
+ Length marginStart = child.style().marginStartUsing(&parentStyle);
+ Length marginEnd = child.style().marginEndUsing(&parentStyle);
+ if (marginStart.isAuto() && marginEnd.isAuto()) {
+ child.setMarginStart(availableAlignmentSpace / 2, &parentStyle);
+ child.setMarginEnd(availableAlignmentSpace / 2, &parentStyle);
+ } else if (marginStart.isAuto()) {
+ child.setMarginStart(availableAlignmentSpace, &parentStyle);
+ } else if (marginEnd.isAuto()) {
+ child.setMarginEnd(availableAlignmentSpace, &parentStyle);
+ }
+}
- // Per http://lists.w3.org/Archives/Public/www-style/2013Mar/0589.html, we clamp negative value to the first line.
- if (endOfTrack < resolvedPosition)
- return 0;
+// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
+void RenderGrid::updateAutoMarginsInColumnAxisIfNeeded(RenderBox& child)
+{
+ ASSERT(!child.isOutOfFlowPositioned());
- return adjustGridPositionForSide(endOfTrack - resolvedPosition, side);
+ LayoutUnit availableAlignmentSpace = child.overrideContainingBlockContentLogicalHeight().value() - child.logicalHeight() - child.marginLogicalHeight();
+ if (availableAlignmentSpace <= 0)
+ return;
+
+ const RenderStyle& parentStyle = style();
+ Length marginBefore = child.style().marginBeforeUsing(&parentStyle);
+ Length marginAfter = child.style().marginAfterUsing(&parentStyle);
+ if (marginBefore.isAuto() && marginAfter.isAuto()) {
+ child.setMarginBefore(availableAlignmentSpace / 2, &parentStyle);
+ child.setMarginAfter(availableAlignmentSpace / 2, &parentStyle);
+ } else if (marginBefore.isAuto()) {
+ child.setMarginBefore(availableAlignmentSpace, &parentStyle);
+ } else if (marginAfter.isAuto()) {
+ child.setMarginAfter(availableAlignmentSpace, &parentStyle);
}
- case NamedGridAreaPosition:
- {
- NamedGridAreaMap::const_iterator it = style().namedGridArea().find(position.namedGridLine());
- // Unknown grid area should have been computed to 'auto' by now.
- ASSERT(it != style().namedGridArea().end());
- const GridCoordinate& gridAreaCoordinate = it->value;
- switch (side) {
- case ColumnStartSide:
- return gridAreaCoordinate.columns.initialPositionIndex;
- case ColumnEndSide:
- return gridAreaCoordinate.columns.finalPositionIndex;
- case RowStartSide:
- return gridAreaCoordinate.rows.initialPositionIndex;
- case RowEndSide:
- return gridAreaCoordinate.rows.finalPositionIndex;
+}
+
+// FIXME: This logic could be refactored somehow and defined in RenderBox.
+static int synthesizedBaselineFromBorderBox(const RenderBox& box, LineDirectionMode direction)
+{
+ return (direction == HorizontalLine ? box.size().height() : box.size().width()).toInt();
+}
+
+bool RenderGrid::isInlineBaselineAlignedChild(const RenderBox& child) const
+{
+ return alignSelfForChild(child).position() == ItemPositionBaseline && !isOrthogonalChild(child) && !hasAutoMarginsInColumnAxis(child);
+}
+
+// FIXME: This logic is shared by RenderFlexibleBox, so it might be refactored somehow.
+int RenderGrid::baselinePosition(FontBaseline, bool, LineDirectionMode direction, LinePositionMode mode) const
+{
+#if ENABLE(ASSERT)
+ ASSERT(mode == PositionOnContainingLine);
+#else
+ UNUSED_PARAM(mode);
+#endif
+ int baseline = firstLineBaseline().value_or(synthesizedBaselineFromBorderBox(*this, direction));
+
+ int marginSize = direction == HorizontalLine ? verticalMarginExtent() : horizontalMarginExtent();
+ return baseline + marginSize;
+}
+
+std::optional<int> RenderGrid::firstLineBaseline() const
+{
+ if (isWritingModeRoot() || !m_grid.hasGridItems())
+ return std::nullopt;
+
+ const RenderBox* baselineChild = nullptr;
+ // Finding the first grid item in grid order.
+ unsigned numColumns = m_grid.numTracks(ForColumns);
+ for (size_t column = 0; column < numColumns; column++) {
+ for (const auto* child : m_grid.cell(0, column)) {
+ // If an item participates in baseline alignment, we select such item.
+ if (isInlineBaselineAlignedChild(*child)) {
+ // FIXME: self-baseline and content-baseline alignment not implemented yet.
+ baselineChild = child;
+ break;
+ }
+ if (!baselineChild)
+ baselineChild = child;
}
- ASSERT_NOT_REACHED();
- return 0;
}
- case AutoPosition:
- case SpanPosition:
- // 'auto' and span depend on the opposite position for resolution (e.g. grid-row: auto / 1 or grid-column: span 3 / "myHeader").
- ASSERT_NOT_REACHED();
- return 0;
+
+ if (!baselineChild)
+ return std::nullopt;
+
+ auto baseline = isOrthogonalChild(*baselineChild) ? std::nullopt : baselineChild->firstLineBaseline();
+ // We take border-box's bottom if no valid baseline.
+ if (!baseline) {
+ // FIXME: We should pass |direction| into firstLineBaseline and stop bailing out if we're a writing
+ // mode root. This would also fix some cases where the grid is orthogonal to its container.
+ LineDirectionMode direction = isHorizontalWritingMode() ? HorizontalLine : VerticalLine;
+ return synthesizedBaselineFromBorderBox(*baselineChild, direction) + baselineChild->logicalTop().toInt();
}
- ASSERT_NOT_REACHED();
- return 0;
+ return baseline.value() + baselineChild->logicalTop().toInt();
}
-PassOwnPtr<GridSpan> RenderGrid::resolveGridPositionAgainstOppositePosition(size_t resolvedOppositePosition, const GridPosition& position, GridPositionSide side) const
+std::optional<int> RenderGrid::inlineBlockBaseline(LineDirectionMode direction) const
{
- if (position.isAuto())
- return GridSpan::create(resolvedOppositePosition, resolvedOppositePosition);
+ if (std::optional<int> baseline = firstLineBaseline())
+ return baseline;
- ASSERT(position.isSpan());
- ASSERT(position.spanPosition() > 0);
+ int marginAscent = direction == HorizontalLine ? marginTop() : marginRight();
+ return synthesizedBaselineFromBorderBox(*this, direction) + marginAscent;
+}
- if (!position.namedGridLine().isNull()) {
- // span 2 'c' -> we need to find the appropriate grid line before / after our opposite position.
- return resolveNamedGridLinePositionAgainstOppositePosition(resolvedOppositePosition, position, side);
+GridAxisPosition RenderGrid::columnAxisPositionForChild(const RenderBox& child) const
+{
+ bool hasSameWritingMode = child.style().writingMode() == style().writingMode();
+ bool childIsLTR = child.style().isLeftToRightDirection();
+
+ switch (child.style().resolvedAlignSelf(style(), selfAlignmentNormalBehavior).position()) {
+ case ItemPositionSelfStart:
+ // FIXME: Should we implement this logic in a generic utility function ?
+ // Aligns the alignment subject to be flush with the edge of the alignment container
+ // corresponding to the alignment subject's 'start' side in the column axis.
+ if (isOrthogonalChild(child)) {
+ // If orthogonal writing-modes, self-start will be based on the child's inline-axis
+ // direction (inline-start), because it's the one parallel to the column axis.
+ if (style().isFlippedBlocksWritingMode())
+ return childIsLTR ? GridAxisEnd : GridAxisStart;
+ return childIsLTR ? GridAxisStart : GridAxisEnd;
+ }
+ // 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.
+ return hasSameWritingMode ? GridAxisStart : GridAxisEnd;
+ case ItemPositionSelfEnd:
+ // FIXME: Should we implement this logic in a generic utility function ?
+ // Aligns the alignment subject to be flush with the edge of the alignment container
+ // corresponding to the alignment subject's 'end' side in the column axis.
+ if (isOrthogonalChild(child)) {
+ // If orthogonal writing-modes, self-end will be based on the child's inline-axis
+ // direction, (inline-end) because it's the one parallel to the column axis.
+ if (style().isFlippedBlocksWritingMode())
+ return childIsLTR ? GridAxisStart : GridAxisEnd;
+ return childIsLTR ? GridAxisEnd : GridAxisStart;
+ }
+ // 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.
+ return hasSameWritingMode ? GridAxisEnd : GridAxisStart;
+ case ItemPositionLeft:
+ // Aligns the alignment subject to be flush with the alignment container's 'line-left' edge.
+ // The alignment axis (column axis) is always orthogonal to the inline axis, hence this value behaves as 'start'.
+ return GridAxisStart;
+ case ItemPositionRight:
+ // Aligns the alignment subject to be flush with the alignment container's 'line-right' edge.
+ // The alignment axis (column axis) is always orthogonal to the inline axis, hence this value behaves as 'start'.
+ return GridAxisStart;
+ case ItemPositionCenter:
+ return GridAxisCenter;
+ case ItemPositionFlexStart: // Only used in flex layout, otherwise equivalent to 'start'.
+ // Aligns the alignment subject to be flush with the alignment container's 'start' edge (block-start) in the column axis.
+ case ItemPositionStart:
+ return GridAxisStart;
+ case ItemPositionFlexEnd: // Only used in flex layout, otherwise equivalent to 'end'.
+ // Aligns the alignment subject to be flush with the alignment container's 'end' edge (block-end) in the column axis.
+ case ItemPositionEnd:
+ return GridAxisEnd;
+ case ItemPositionStretch:
+ return GridAxisStart;
+ case ItemPositionBaseline:
+ case ItemPositionLastBaseline:
+ // FIXME: Implement the previous values. For now, we always 'start' align the child.
+ return GridAxisStart;
+ case ItemPositionAuto:
+ case ItemPositionNormal:
+ break;
}
- // 'span 1' is contained inside a single grid track regardless of the direction.
- // That's why the CSS span value is one more than the offset we apply.
- size_t positionOffset = position.spanPosition() - 1;
- if (side == ColumnStartSide || side == RowStartSide) {
- size_t initialResolvedPosition = std::max<int>(0, resolvedOppositePosition - positionOffset);
- return GridSpan::create(initialResolvedPosition, resolvedOppositePosition);
+ ASSERT_NOT_REACHED();
+ return GridAxisStart;
+}
+
+GridAxisPosition RenderGrid::rowAxisPositionForChild(const RenderBox& child) const
+{
+ bool hasSameDirection = child.style().direction() == style().direction();
+ bool gridIsLTR = style().isLeftToRightDirection();
+
+ switch (child.style().resolvedJustifySelf(style(), selfAlignmentNormalBehavior).position()) {
+ case ItemPositionSelfStart:
+ // FIXME: Should we implement this logic in a generic utility function ?
+ // Aligns the alignment subject to be flush with the edge of the alignment container
+ // corresponding to the alignment subject's 'start' side in the row axis.
+ if (isOrthogonalChild(child)) {
+ // If orthogonal writing-modes, self-start will be based on the child's block-axis
+ // direction, because it's the one parallel to the row axis.
+ if (child.style().isFlippedBlocksWritingMode())
+ return gridIsLTR ? GridAxisEnd : GridAxisStart;
+ return gridIsLTR ? GridAxisStart : GridAxisEnd;
+ }
+ // self-start is based on the child's inline-flow direction. That's why we need to check against the grid container's direction.
+ return hasSameDirection ? GridAxisStart : GridAxisEnd;
+ case ItemPositionSelfEnd:
+ // FIXME: Should we implement this logic in a generic utility function ?
+ // Aligns the alignment subject to be flush with the edge of the alignment container
+ // corresponding to the alignment subject's 'end' side in the row axis.
+ if (isOrthogonalChild(child)) {
+ // If orthogonal writing-modes, self-end will be based on the child's block-axis
+ // direction, because it's the one parallel to the row axis.
+ if (child.style().isFlippedBlocksWritingMode())
+ return gridIsLTR ? GridAxisStart : GridAxisEnd;
+ return gridIsLTR ? GridAxisEnd : GridAxisStart;
+ }
+ // self-end is based on the child's inline-flow direction. That's why we need to check against the grid container's direction.
+ return hasSameDirection ? GridAxisEnd : GridAxisStart;
+ case ItemPositionLeft:
+ // Aligns the alignment subject to be flush with the alignment container's 'line-left' edge.
+ // We want the physical 'left' side, so we have to take account, container's inline-flow direction.
+ return gridIsLTR ? GridAxisStart : GridAxisEnd;
+ case ItemPositionRight:
+ // Aligns the alignment subject to be flush with the alignment container's 'line-right' edge.
+ // We want the physical 'right' side, so we have to take account, container's inline-flow direction.
+ return gridIsLTR ? GridAxisEnd : GridAxisStart;
+ case ItemPositionCenter:
+ return GridAxisCenter;
+ case ItemPositionFlexStart: // Only used in flex layout, otherwise equivalent to 'start'.
+ // Aligns the alignment subject to be flush with the alignment container's 'start' edge (inline-start) in the row axis.
+ case ItemPositionStart:
+ return GridAxisStart;
+ case ItemPositionFlexEnd: // Only used in flex layout, otherwise equivalent to 'end'.
+ // Aligns the alignment subject to be flush with the alignment container's 'end' edge (inline-end) in the row axis.
+ case ItemPositionEnd:
+ return GridAxisEnd;
+ case ItemPositionStretch:
+ return GridAxisStart;
+ case ItemPositionBaseline:
+ case ItemPositionLastBaseline:
+ // FIXME: Implement the previous values. For now, we always 'start' align the child.
+ return GridAxisStart;
+ case ItemPositionAuto:
+ case ItemPositionNormal:
+ break;
}
- return GridSpan::create(resolvedOppositePosition, resolvedOppositePosition + positionOffset);
+ ASSERT_NOT_REACHED();
+ return GridAxisStart;
}
-PassOwnPtr<GridSpan> RenderGrid::resolveNamedGridLinePositionAgainstOppositePosition(size_t resolvedOppositePosition, const GridPosition& position, GridPositionSide side) const
+LayoutUnit RenderGrid::columnAxisOffsetForChild(const RenderBox& child) const
{
- ASSERT(position.isSpan());
- ASSERT(!position.namedGridLine().isNull());
- // Negative positions are not allowed per the specification and should have been handled during parsing.
- ASSERT(position.spanPosition() > 0);
+ const GridSpan& rowsSpan = m_grid.gridItemSpan(child, ForRows);
+ unsigned childStartLine = rowsSpan.startLine();
+ LayoutUnit startOfRow = m_rowPositions[childStartLine];
+ LayoutUnit startPosition = startOfRow + marginBeforeForChild(child);
+ if (hasAutoMarginsInColumnAxis(child))
+ return startPosition;
+ GridAxisPosition axisPosition = columnAxisPositionForChild(child);
+ switch (axisPosition) {
+ case GridAxisStart:
+ return startPosition;
+ case GridAxisEnd:
+ case GridAxisCenter: {
+ unsigned childEndLine = rowsSpan.endLine();
+ LayoutUnit endOfRow = m_rowPositions[childEndLine];
+ // m_rowPositions include distribution offset (because of content alignment) and gutters
+ // so we need to subtract them to get the actual end position for a given row
+ // (this does not have to be done for the last track as there are no more m_rowPositions after it).
+ if (childEndLine < m_rowPositions.size() - 1)
+ endOfRow -= gridGapForDirection(ForRows) + m_offsetBetweenRows;
+ LayoutUnit columnAxisChildSize = isOrthogonalChild(child) ? child.logicalWidth() + child.marginLogicalWidth() : child.logicalHeight() + child.marginLogicalHeight();
+ auto overflow = child.style().resolvedAlignSelf(style(), selfAlignmentNormalBehavior).overflow();
+ LayoutUnit offsetFromStartPosition = computeOverflowAlignmentOffset(overflow, endOfRow - startOfRow, columnAxisChildSize);
+ return startPosition + (axisPosition == GridAxisEnd ? offsetFromStartPosition : offsetFromStartPosition / 2);
+ }
+ }
- const NamedGridLinesMap& gridLinesNames = (side == ColumnStartSide || side == ColumnEndSide) ? style().namedGridColumnLines() : style().namedGridRowLines();
- NamedGridLinesMap::const_iterator it = gridLinesNames.find(position.namedGridLine());
+ ASSERT_NOT_REACHED();
+ return 0;
+}
- // If there is no named grid line of that name, we resolve the position to 'auto' (which is equivalent to 'span 1' in this case).
- // See http://lists.w3.org/Archives/Public/www-style/2013Jun/0394.html.
- if (it == gridLinesNames.end())
- return GridSpan::create(resolvedOppositePosition, resolvedOppositePosition);
- if (side == RowStartSide || side == ColumnStartSide)
- return resolveRowStartColumnStartNamedGridLinePositionAgainstOppositePosition(resolvedOppositePosition, position, it->value);
+LayoutUnit RenderGrid::rowAxisOffsetForChild(const RenderBox& child) const
+{
+ const GridSpan& columnsSpan = m_grid.gridItemSpan(child, ForColumns);
+ unsigned childStartLine = columnsSpan.startLine();
+ LayoutUnit startOfColumn = m_columnPositions[childStartLine];
+ LayoutUnit startPosition = startOfColumn + marginStartForChild(child);
+ if (hasAutoMarginsInRowAxis(child))
+ return startPosition;
+ GridAxisPosition axisPosition = rowAxisPositionForChild(child);
+ switch (axisPosition) {
+ case GridAxisStart:
+ return startPosition;
+ case GridAxisEnd:
+ case GridAxisCenter: {
+ unsigned childEndLine = columnsSpan.endLine();
+ LayoutUnit endOfColumn = m_columnPositions[childEndLine];
+ // m_columnPositions include distribution offset (because of content alignment) and gutters
+ // so we need to subtract them to get the actual end position for a given column
+ // (this does not have to be done for the last track as there are no more m_columnPositions after it).
+ if (childEndLine < m_columnPositions.size() - 1)
+ endOfColumn -= gridGapForDirection(ForColumns) + m_offsetBetweenColumns;
+ LayoutUnit rowAxisChildSize = isOrthogonalChild(child) ? child.logicalHeight() + child.marginLogicalHeight() : child.logicalWidth() + child.marginLogicalWidth();
+ auto overflow = child.style().resolvedJustifySelf(style(), selfAlignmentNormalBehavior).overflow();
+ LayoutUnit offsetFromStartPosition = computeOverflowAlignmentOffset(overflow, endOfColumn - startOfColumn, rowAxisChildSize);
+ return startPosition + (axisPosition == GridAxisEnd ? offsetFromStartPosition : offsetFromStartPosition / 2);
+ }
+ }
- return resolveRowEndColumnEndNamedGridLinePositionAgainstOppositePosition(resolvedOppositePosition, position, it->value);
+ ASSERT_NOT_REACHED();
+ return 0;
}
-PassOwnPtr<GridSpan> RenderGrid::resolveRowStartColumnStartNamedGridLinePositionAgainstOppositePosition(size_t resolvedOppositePosition, const GridPosition& position, const Vector<size_t>& gridLines) const
+ContentPosition static resolveContentDistributionFallback(ContentDistributionType distribution)
{
- // The grid line inequality needs to be strict (which doesn't match the after / end case) because |resolvedOppositePosition|
- // is already converted to an index in our grid representation (ie one was removed from the grid line to account for the side).
- // FIXME: This could be a binary search as |gridLines| is ordered.
- int firstLineBeforeOppositePositionIndex = gridLines.size() - 1;
- for (; firstLineBeforeOppositePositionIndex >= 0 && gridLines[firstLineBeforeOppositePositionIndex] > resolvedOppositePosition; --firstLineBeforeOppositePositionIndex) { }
+ switch (distribution) {
+ case ContentDistributionSpaceBetween:
+ return ContentPositionStart;
+ case ContentDistributionSpaceAround:
+ return ContentPositionCenter;
+ case ContentDistributionSpaceEvenly:
+ return ContentPositionCenter;
+ case ContentDistributionStretch:
+ return ContentPositionStart;
+ case ContentDistributionDefault:
+ return ContentPositionNormal;
+ }
- size_t gridLineIndex = std::max<int>(0, firstLineBeforeOppositePositionIndex - position.spanPosition() + 1);
- size_t resolvedGridLinePosition = gridLines[gridLineIndex];
- if (resolvedGridLinePosition > resolvedOppositePosition)
- resolvedGridLinePosition = resolvedOppositePosition;
- return GridSpan::create(resolvedGridLinePosition, resolvedOppositePosition);
+ ASSERT_NOT_REACHED();
+ return ContentPositionNormal;
}
-PassOwnPtr<GridSpan> RenderGrid::resolveRowEndColumnEndNamedGridLinePositionAgainstOppositePosition(size_t resolvedOppositePosition, const GridPosition& position, const Vector<size_t>& gridLines) const
+static ContentAlignmentData contentDistributionOffset(const LayoutUnit& availableFreeSpace, ContentPosition& fallbackPosition, ContentDistributionType distribution, unsigned numberOfGridTracks)
{
- // FIXME: This could be a binary search as |gridLines| is ordered.
- size_t firstLineAfterOppositePositionIndex = 0;
- for (; firstLineAfterOppositePositionIndex < gridLines.size() && gridLines[firstLineAfterOppositePositionIndex] <= resolvedOppositePosition; ++firstLineAfterOppositePositionIndex) { }
+ if (distribution != ContentDistributionDefault && fallbackPosition == ContentPositionNormal)
+ fallbackPosition = resolveContentDistributionFallback(distribution);
+
+ if (availableFreeSpace <= 0)
+ return ContentAlignmentData::defaultOffsets();
+
+ LayoutUnit distributionOffset;
+ switch (distribution) {
+ case ContentDistributionSpaceBetween:
+ if (numberOfGridTracks < 2)
+ return ContentAlignmentData::defaultOffsets();
+ return {0, availableFreeSpace / (numberOfGridTracks - 1)};
+ case ContentDistributionSpaceAround:
+ if (numberOfGridTracks < 1)
+ return ContentAlignmentData::defaultOffsets();
+ distributionOffset = availableFreeSpace / numberOfGridTracks;
+ return {distributionOffset / 2, distributionOffset};
+ case ContentDistributionSpaceEvenly:
+ distributionOffset = availableFreeSpace / (numberOfGridTracks + 1);
+ return {distributionOffset, distributionOffset};
+ case ContentDistributionStretch:
+ case ContentDistributionDefault:
+ return ContentAlignmentData::defaultOffsets();
+ }
- size_t gridLineIndex = std::min(gridLines.size() - 1, firstLineAfterOppositePositionIndex + position.spanPosition() - 1);
- size_t resolvedGridLinePosition = adjustGridPositionForRowEndColumnEndSide(gridLines[gridLineIndex]);
- if (resolvedGridLinePosition < resolvedOppositePosition)
- resolvedGridLinePosition = resolvedOppositePosition;
- return GridSpan::create(resolvedOppositePosition, resolvedGridLinePosition);
+ ASSERT_NOT_REACHED();
+ return ContentAlignmentData::defaultOffsets();
}
-LayoutUnit RenderGrid::gridAreaBreadthForChild(const RenderBox* child, TrackSizingDirection direction, const Vector<GridTrack>& tracks) const
+ContentAlignmentData RenderGrid::computeContentPositionAndDistributionOffset(GridTrackSizingDirection direction, const LayoutUnit& availableFreeSpace, unsigned numberOfGridTracks) const
{
- const GridCoordinate& coordinate = cachedGridCoordinate(child);
- const GridSpan& span = (direction == ForColumns) ? coordinate.columns : coordinate.rows;
- LayoutUnit gridAreaBreadth = 0;
- for (size_t trackIndex = span.initialPositionIndex; trackIndex <= span.finalPositionIndex; ++trackIndex)
- gridAreaBreadth += tracks[trackIndex].m_usedBreadth;
- return gridAreaBreadth;
+ bool isRowAxis = direction == ForColumns;
+ auto position = isRowAxis ? style().resolvedJustifyContentPosition(contentAlignmentNormalBehaviorGrid()) : style().resolvedAlignContentPosition(contentAlignmentNormalBehaviorGrid());
+ auto distribution = isRowAxis ? style().resolvedJustifyContentDistribution(contentAlignmentNormalBehaviorGrid()) : style().resolvedAlignContentDistribution(contentAlignmentNormalBehaviorGrid());
+ // If <content-distribution> value can't be applied, 'position' will become the associated
+ // <content-position> fallback value.
+ auto contentAlignment = contentDistributionOffset(availableFreeSpace, position, distribution, numberOfGridTracks);
+ if (contentAlignment.isValid())
+ return contentAlignment;
+
+ auto overflow = (isRowAxis ? style().justifyContent() : style().alignContent()).overflow();
+ if (availableFreeSpace <= 0 && overflow == OverflowAlignmentSafe)
+ return {0, 0};
+
+ switch (position) {
+ case ContentPositionLeft:
+ // The align-content's axis is always orthogonal to the inline-axis.
+ return {0, 0};
+ case ContentPositionRight:
+ if (isRowAxis)
+ return {availableFreeSpace, 0};
+ // The align-content's axis is always orthogonal to the inline-axis.
+ return {0, 0};
+ case ContentPositionCenter:
+ return {availableFreeSpace / 2, 0};
+ case ContentPositionFlexEnd: // Only used in flex layout, for other layout, it's equivalent to 'end'.
+ case ContentPositionEnd:
+ if (isRowAxis)
+ return {style().isLeftToRightDirection() ? availableFreeSpace : LayoutUnit(), LayoutUnit()};
+ return {availableFreeSpace, 0};
+ case ContentPositionFlexStart: // Only used in flex layout, for other layout, it's equivalent to 'start'.
+ case ContentPositionStart:
+ if (isRowAxis)
+ return {style().isLeftToRightDirection() ? LayoutUnit() : availableFreeSpace, LayoutUnit()};
+ return {0, 0};
+ case ContentPositionBaseline:
+ case ContentPositionLastBaseline:
+ // FIXME: Implement the previous values. For now, we always 'start' align.
+ // http://webkit.org/b/145566
+ if (isRowAxis)
+ return {style().isLeftToRightDirection() ? LayoutUnit() : availableFreeSpace, LayoutUnit()};
+ return {0, 0};
+ case ContentPositionNormal:
+ break;
+ }
+
+ ASSERT_NOT_REACHED();
+ return {0, 0};
}
-LayoutPoint RenderGrid::findChildLogicalPosition(RenderBox* child, const GridSizingData& sizingData)
+LayoutUnit RenderGrid::translateRTLCoordinate(LayoutUnit coordinate) const
{
- const GridCoordinate& coordinate = cachedGridCoordinate(child);
+ ASSERT(!style().isLeftToRightDirection());
- // The grid items should be inside the grid container's border box, that's why they need to be shifted.
- LayoutPoint offset(borderAndPaddingStart() + marginStartForChild(*child), borderAndPaddingBefore() + marginBeforeForChild(*child));
- // FIXME: |columnTrack| and |rowTrack| should be smaller than our column / row count.
- for (size_t i = 0; i < coordinate.columns.initialPositionIndex && i < sizingData.columnTracks.size(); ++i)
- offset.setX(offset.x() + sizingData.columnTracks[i].m_usedBreadth);
- for (size_t i = 0; i < coordinate.rows.initialPositionIndex && i < sizingData.rowTracks.size(); ++i)
- offset.setY(offset.y() + sizingData.rowTracks[i].m_usedBreadth);
+ LayoutUnit alignmentOffset = m_columnPositions[0];
+ LayoutUnit rightGridEdgePosition = m_columnPositions[m_columnPositions.size() - 1];
+ return rightGridEdgePosition + alignmentOffset - coordinate;
+}
- return offset;
+LayoutPoint RenderGrid::findChildLogicalPosition(const RenderBox& child) const
+{
+ LayoutUnit columnAxisOffset = columnAxisOffsetForChild(child);
+ LayoutUnit rowAxisOffset = rowAxisOffsetForChild(child);
+
+ // We stored m_columnPositions's data ignoring the direction, hence we might need now
+ // to translate positions from RTL to LTR, as it's more convenient for painting.
+ if (!style().isLeftToRightDirection())
+ rowAxisOffset = translateRTLCoordinate(rowAxisOffset) - (isOrthogonalChild(child) ? child.logicalHeight() : child.logicalWidth());
+
+ // "In the positioning phase [...] calculations are performed according to the writing mode
+ // of the containing block of the box establishing the orthogonal flow." However, the
+ // resulting LayoutPoint will be used in 'setLogicalPosition' in order to set the child's
+ // logical position, which will only take into account the child's writing-mode.
+ LayoutPoint childLocation(rowAxisOffset, columnAxisOffset);
+ return isOrthogonalChild(child) ? childLocation.transposedPoint() : childLocation;
+}
+
+unsigned RenderGrid::numTracks(GridTrackSizingDirection direction, const Grid& grid) const
+{
+ // Due to limitations in our internal representation, we cannot know the number of columns from
+ // m_grid *if* there is no row (because m_grid would be empty). That's why in that case we need
+ // to get it from the style. Note that we know for sure that there are't any implicit tracks,
+ // because not having rows implies that there are no "normal" children (out-of-flow children are
+ // not stored in m_grid).
+ ASSERT(!grid.needsItemsPlacement());
+ if (direction == ForRows)
+ return grid.numTracks(ForRows);
+
+ // FIXME: This still requires knowledge about m_grid internals.
+ return grid.numTracks(ForRows) ? grid.numTracks(ForColumns) : GridPositionsResolver::explicitGridColumnCount(style(), grid.autoRepeatTracks(ForColumns));
}
void RenderGrid::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& forChild, bool usePrintRect)
{
- for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next())
- paintChild(*child, paintInfo, paintOffset, forChild, usePrintRect);
+ ASSERT(!m_grid.needsItemsPlacement());
+ for (RenderBox* child = m_grid.orderIterator().first(); child; child = m_grid.orderIterator().next())
+ paintChild(*child, paintInfo, paintOffset, forChild, usePrintRect, PaintAsInlineBlock);
}
const char* RenderGrid::renderName() const