diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/rendering/RenderGrid.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/core/rendering/RenderGrid.cpp | 769 |
1 files changed, 438 insertions, 331 deletions
diff --git a/chromium/third_party/WebKit/Source/core/rendering/RenderGrid.cpp b/chromium/third_party/WebKit/Source/core/rendering/RenderGrid.cpp index 38c296ac866..0d167415707 100644 --- a/chromium/third_party/WebKit/Source/core/rendering/RenderGrid.cpp +++ b/chromium/third_party/WebKit/Source/core/rendering/RenderGrid.cpp @@ -26,10 +26,12 @@ #include "config.h" #include "core/rendering/RenderGrid.h" +#include "core/rendering/FastTextAutosizer.h" #include "core/rendering/LayoutRepainter.h" #include "core/rendering/RenderLayer.h" #include "core/rendering/RenderView.h" #include "core/rendering/style/GridCoordinate.h" +#include "platform/LengthFunctions.h" namespace WebCore { @@ -75,7 +77,7 @@ struct GridTrackForNormalization { } // Required by std::sort. - GridTrackForNormalization operator=(const GridTrackForNormalization& o) + GridTrackForNormalization& operator=(const GridTrackForNormalization& o) { m_track = o.m_track; m_flex = o.m_flex; @@ -120,16 +122,37 @@ public: return 0; } - PassOwnPtr<GridCoordinate> nextEmptyGridArea() + bool checkEmptyCells(size_t rowSpan, size_t columnSpan) const + { + // Ignore cells outside current grid as we will grow it later if needed. + size_t maxRows = std::min(m_rowIndex + rowSpan, m_grid.size()); + size_t maxColumns = std::min(m_columnIndex + columnSpan, m_grid[0].size()); + + // This adds a O(N^2) behavior that shouldn't be a big deal as we expect spanning areas to be small. + for (size_t row = m_rowIndex; row < maxRows; ++row) { + for (size_t column = m_columnIndex; column < maxColumns; ++column) { + const GridCell& children = m_grid[row][column]; + if (!children.isEmpty()) + return false; + } + } + + return true; + } + + PassOwnPtr<GridCoordinate> nextEmptyGridArea(size_t fixedTrackSpan, size_t varyingTrackSpan) { ASSERT(!m_grid.isEmpty()); + ASSERT(fixedTrackSpan >= 1 && varyingTrackSpan >= 1); + + size_t rowSpan = (m_direction == ForColumns) ? varyingTrackSpan : fixedTrackSpan; + size_t columnSpan = (m_direction == ForColumns) ? fixedTrackSpan : varyingTrackSpan; 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 GridCell& 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))); + if (checkEmptyCells(rowSpan, columnSpan)) { + OwnPtr<GridCoordinate> result = adoptPtr(new GridCoordinate(GridSpan(m_rowIndex, m_rowIndex + rowSpan - 1), GridSpan(m_columnIndex, m_columnIndex + columnSpan - 1))); // Advance the iterator to avoid an infinite loop where we would return the same grid area over and over. ++varyingTrackIndex; return result.release(); @@ -168,7 +191,6 @@ RenderGrid::RenderGrid(Element* element) : RenderBlock(element) , m_gridIsDirty(true) , m_orderIterator(this) - , m_gridItemOverflowGridArea(false) { // All of our children must be block level. setChildrenInline(false); @@ -190,20 +212,44 @@ void RenderGrid::addChild(RenderObject* newChild, RenderObject* beforeChild) return; } + if (style()->gridAutoFlow() != AutoFlowNone) { + // The grid needs to be recomputed as it might contain auto-placed items that will change their position. + dirtyGrid(); + return; + } + RenderBox* newChildBox = toRenderBox(newChild); - OwnPtr<GridSpan> rowPositions = resolveGridPositionsFromStyle(newChildBox, ForRows); - OwnPtr<GridSpan> columnPositions = resolveGridPositionsFromStyle(newChildBox, ForColumns); + OwnPtr<GridSpan> rowPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *newChildBox, ForRows); + OwnPtr<GridSpan> columnPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *newChildBox, ForColumns); if (!rowPositions || !columnPositions) { // The new child requires the auto-placement algorithm to run so we need to recompute the grid fully. dirtyGrid(); + return; } else { - if (gridRowCount() <= rowPositions->finalPositionIndex || gridColumnCount() <= columnPositions->finalPositionIndex) { - // FIXME: We could just insert the new child provided we had a primitive to arbitrarily grow the grid. - dirtyGrid(); - } else { - insertItemIntoGrid(newChildBox, GridCoordinate(*rowPositions, *columnPositions)); - } + insertItemIntoGrid(newChildBox, GridCoordinate(*rowPositions, *columnPositions)); + addChildToIndexesMap(newChildBox); + } +} + +void RenderGrid::addChildToIndexesMap(RenderBox* child) +{ + ASSERT(!m_gridItemsIndexesMap.contains(child)); + RenderBox* sibling = child->nextSiblingBox(); + bool lastSibling = !sibling; + + if (lastSibling) + sibling = child->previousSiblingBox(); + + size_t index = 0; + if (sibling) + index = lastSibling ? m_gridItemsIndexesMap.get(sibling) + 1 : m_gridItemsIndexesMap.get(sibling); + + if (sibling && !lastSibling) { + for (; sibling; sibling = sibling->nextSiblingBox()) + m_gridItemsIndexesMap.set(sibling, m_gridItemsIndexesMap.get(sibling) + 1); } + + m_gridItemsIndexesMap.set(child, index); } void RenderGrid::removeChild(RenderObject* child) @@ -214,8 +260,24 @@ void RenderGrid::removeChild(RenderObject* child) return; ASSERT(child->isBox()); - // FIXME: We could avoid dirtying the grid in some cases (e.g. if it's an explicitly positioned element). - dirtyGrid(); + + if (style()->gridAutoFlow() != AutoFlowNone) { + // The grid needs to be recomputed as it might contain auto-placed items that will change their position. + dirtyGrid(); + return; + } + + const RenderBox* childBox = toRenderBox(child); + GridCoordinate coordinate = m_gridItemCoordinate.take(childBox); + + for (GridSpan::iterator row = coordinate.rows.begin(); row != coordinate.rows.end(); ++row) { + for (GridSpan::iterator column = coordinate.columns.begin(); column != coordinate.columns.end(); ++column) { + GridCell& cell = m_grid[row.toInt()][column.toInt()]; + cell.remove(cell.find(childBox)); + } + } + + m_gridItemsIndexesMap.remove(childBox); } void RenderGrid::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) @@ -237,8 +299,8 @@ void RenderGrid::styleDidChange(StyleDifference diff, const RenderStyle* oldStyl bool RenderGrid::explicitGridDidResize(const RenderStyle* oldStyle) const { - return oldStyle->gridDefinitionColumns().size() != style()->gridDefinitionColumns().size() - || oldStyle->gridDefinitionRows().size() != style()->gridDefinitionRows().size(); + return oldStyle->gridTemplateColumns().size() != style()->gridTemplateColumns().size() + || oldStyle->gridTemplateRows().size() != style()->gridTemplateRows().size(); } bool RenderGrid::namedGridLinesDefinitionDidChange(const RenderStyle* oldStyle) const @@ -247,7 +309,7 @@ bool RenderGrid::namedGridLinesDefinitionDidChange(const RenderStyle* oldStyle) || oldStyle->namedGridColumnLines() != style()->namedGridColumnLines(); } -void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit) +void RenderGrid::layoutBlock(bool relayoutChildren) { ASSERT(needsLayout()); @@ -256,21 +318,16 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit) // 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()); - - // Regions changing widths can force us to relayout our children. - RenderFlowThread* flowThread = flowThreadContainingBlock(); - if (logicalWidthChangedInRegions(flowThread)) - relayoutChildren = true; - if (updateRegionsAndShapesLogicalSize(flowThread)) - relayoutChildren = true; + LayoutRepainter repainter(*this, checkForPaintInvalidationDuringLayout()); + LayoutState state(*this, locationOffset()); LayoutSize previousSize = size(); setLogicalHeight(0); updateLogicalWidth(); + FastTextAutosizer::LayoutScope fastTextAutosizerLayoutScope(this); + layoutGridItems(); LayoutUnit oldClientAfterEdge = clientLogicalBottom(); @@ -279,14 +336,13 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit) if (size() != previousSize) relayoutChildren = true; - layoutPositionedObjects(relayoutChildren || isRoot()); + layoutPositionedObjects(relayoutChildren || isDocumentElement()); - computeRegionRangeForBlock(flowThread); + computeRegionRangeForBlock(flowThreadContainingBlock()); computeOverflow(oldClientAfterEdge); - statePusher.pop(); - updateLayerTransform(); + updateLayerTransformAfterLayout(); // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if // we overflow or not. @@ -304,7 +360,7 @@ void RenderGrid::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, Layo GridSizingData sizingData(gridColumnCount(), gridRowCount()); LayoutUnit availableLogicalSpace = 0; - const_cast<RenderGrid*>(this)->computedUsedBreadthOfGridTracks(ForColumns, sizingData, availableLogicalSpace); + const_cast<RenderGrid*>(this)->computeUsedBreadthOfGridTracks(ForColumns, sizingData, availableLogicalSpace); for (size_t i = 0; i < sizingData.columnTracks.size(); ++i) { LayoutUnit minTrackBreadth = sizingData.columnTracks[i].m_usedBreadth; @@ -337,16 +393,24 @@ void RenderGrid::computePreferredLogicalWidths() clearPreferredLogicalWidthsDirty(); } -void RenderGrid::computedUsedBreadthOfGridTracks(GridTrackSizingDirection direction, GridSizingData& sizingData) +void RenderGrid::computeUsedBreadthOfGridTracks(GridTrackSizingDirection direction, GridSizingData& sizingData) { LayoutUnit availableLogicalSpace = (direction == ForColumns) ? availableLogicalWidth() : availableLogicalHeight(IncludeMarginBorderPadding); - computedUsedBreadthOfGridTracks(direction, sizingData, availableLogicalSpace); + computeUsedBreadthOfGridTracks(direction, sizingData, availableLogicalSpace); } -void RenderGrid::computedUsedBreadthOfGridTracks(GridTrackSizingDirection direction, GridSizingData& sizingData, LayoutUnit& availableLogicalSpace) +bool RenderGrid::gridElementIsShrinkToFit() +{ + return isFloatingOrOutOfFlowPositioned(); +} + +void RenderGrid::computeUsedBreadthOfGridTracks(GridTrackSizingDirection direction, GridSizingData& sizingData, LayoutUnit& availableLogicalSpace) { Vector<GridTrack>& tracks = (direction == ForColumns) ? sizingData.columnTracks : sizingData.rowTracks; + Vector<size_t> flexibleSizedTracksIndex; sizingData.contentSizedTracksIndex.shrink(0); + + // 1. Initialize per Grid track variables. for (size_t i = 0; i < tracks.size(); ++i) { GridTrack& track = tracks[i]; const GridTrackSize& trackSize = gridTrackSize(direction, i); @@ -360,8 +424,11 @@ void RenderGrid::computedUsedBreadthOfGridTracks(GridTrackSizingDirection direct if (trackSize.isContentSized()) sizingData.contentSizedTracksIndex.append(i); + if (trackSize.maxTrackBreadth().isFlex()) + flexibleSizedTracksIndex.append(i); } + // 2. Resolve content-based TrackSizingFunctions. if (!sizingData.contentSizedTracksIndex.isEmpty()) resolveContentBasedTrackSizingFunctions(direction, sizingData, availableLogicalSpace); @@ -370,26 +437,60 @@ void RenderGrid::computedUsedBreadthOfGridTracks(GridTrackSizingDirection direct availableLogicalSpace -= tracks[i].m_usedBreadth; } - if (availableLogicalSpace <= 0) + const bool hasUndefinedRemainingSpace = (direction == ForRows) ? style()->logicalHeight().isAuto() : gridElementIsShrinkToFit(); + + if (!hasUndefinedRemainingSpace && availableLogicalSpace <= 0) return; + // 3. Grow all Grid tracks in GridTracks from their UsedBreadth up to their MaxBreadth value until + // availableLogicalSpace (RemainingSpace in the specs) is exhausted. const size_t tracksSize = tracks.size(); - Vector<GridTrack*> tracksForDistribution(tracksSize); - for (size_t i = 0; i < tracksSize; ++i) - tracksForDistribution[i] = tracks.data() + i; + if (!hasUndefinedRemainingSpace) { + Vector<GridTrack*> tracksForDistribution(tracksSize); + for (size_t i = 0; i < tracksSize; ++i) + tracksForDistribution[i] = tracks.data() + i; - distributeSpaceToTracks(tracksForDistribution, 0, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth, sizingData, availableLogicalSpace); + distributeSpaceToTracks(tracksForDistribution, 0, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth, sizingData, availableLogicalSpace); + } else { + for (size_t i = 0; i < tracksSize; ++i) + tracks[i].m_usedBreadth = tracks[i].m_maxBreadth; + } + + if (flexibleSizedTracksIndex.isEmpty()) + return; // 4. Grow all Grid tracks having a fraction as the MaxTrackSizingFunction. + double normalizedFractionBreadth = 0; + if (!hasUndefinedRemainingSpace) { + normalizedFractionBreadth = computeNormalizedFractionBreadth(tracks, GridSpan(0, tracks.size() - 1), direction, availableLogicalSpace); + } else { + for (size_t i = 0; i < flexibleSizedTracksIndex.size(); ++i) { + const size_t trackIndex = flexibleSizedTracksIndex[i]; + const GridTrackSize& trackSize = gridTrackSize(direction, trackIndex); + normalizedFractionBreadth = std::max(normalizedFractionBreadth, tracks[trackIndex].m_usedBreadth / trackSize.maxTrackBreadth().flex()); + } - // 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; + for (size_t i = 0; i < flexibleSizedTracksIndex.size(); ++i) { + GridIterator iterator(m_grid, direction, flexibleSizedTracksIndex[i]); + while (RenderBox* gridItem = iterator.nextGridItem()) { + const GridCoordinate coordinate = cachedGridCoordinate(gridItem); + const GridSpan span = (direction == ForColumns) ? coordinate.columns : coordinate.rows; + + // Do not include already processed items. + if (i > 0 && span.resolvedInitialPosition.toInt() <= flexibleSizedTracksIndex[i - 1]) + continue; + + double itemNormalizedFlexBreadth = computeNormalizedFractionBreadth(tracks, span, direction, maxContentForChild(gridItem, direction, sizingData.columnTracks)); + normalizedFractionBreadth = std::max(normalizedFractionBreadth, itemNormalizedFlexBreadth); + } + } + } - tracks[i].m_usedBreadth = std::max<LayoutUnit>(tracks[i].m_usedBreadth, normalizedFractionBreadth * trackSize.maxTrackBreadth().flex()); + for (size_t i = 0; i < flexibleSizedTracksIndex.size(); ++i) { + const size_t trackIndex = flexibleSizedTracksIndex[i]; + const GridTrackSize& trackSize = gridTrackSize(direction, trackIndex); + + tracks[trackIndex].m_usedBreadth = std::max<LayoutUnit>(tracks[trackIndex].m_usedBreadth, normalizedFractionBreadth * trackSize.maxTrackBreadth().flex()); } } @@ -428,7 +529,7 @@ LayoutUnit RenderGrid::computeUsedBreadthOfSpecifiedLength(GridTrackSizingDirect { ASSERT(trackLength.isSpecified()); // FIXME: The -1 here should be replaced by whatever the intrinsic height of the grid is. - return valueForLength(trackLength, direction == ForColumns ? logicalWidth() : computeContentLogicalHeight(style()->logicalHeight(), -1), view()); + return valueForLength(trackLength, direction == ForColumns ? logicalWidth() : computeContentLogicalHeight(style()->logicalHeight(), -1)); } static bool sortByGridNormalizedFlexValue(const GridTrackForNormalization& track1, const GridTrackForNormalization& track2) @@ -436,22 +537,21 @@ static bool sortByGridNormalizedFlexValue(const GridTrackForNormalization& track return track1.m_normalizedFlexValue < track2.m_normalizedFlexValue; } -double RenderGrid::computeNormalizedFractionBreadth(Vector<GridTrack>& tracks, GridTrackSizingDirection direction, LayoutUnit availableLogicalSpace) const +double RenderGrid::computeNormalizedFractionBreadth(Vector<GridTrack>& tracks, const GridSpan& tracksSpan, GridTrackSizingDirection direction, LayoutUnit availableLogicalSpace) const { // |availableLogicalSpace| already accounts for the used breadths so no need to remove it here. Vector<GridTrackForNormalization> tracksForNormalization; - for (size_t i = 0; i < tracks.size(); ++i) { - const GridTrackSize& trackSize = gridTrackSize(direction, i); + for (GridSpan::iterator resolvedPosition = tracksSpan.begin(); resolvedPosition != tracksSpan.end(); ++resolvedPosition) { + const GridTrackSize& trackSize = gridTrackSize(direction, resolvedPosition.toInt()); if (!trackSize.maxTrackBreadth().isFlex()) continue; - tracksForNormalization.append(GridTrackForNormalization(tracks[i], trackSize.maxTrackBreadth().flex())); + tracksForNormalization.append(GridTrackForNormalization(tracks[resolvedPosition.toInt()], trackSize.maxTrackBreadth().flex())); } - // FIXME: Ideally we shouldn't come here without any <flex> grid track. - if (tracksForNormalization.isEmpty()) - return LayoutUnit(); + // The function is not called if we don't have <flex> grid tracks + ASSERT(!tracksForNormalization.isEmpty()); std::sort(tracksForNormalization.begin(), tracksForNormalization.end(), sortByGridNormalizedFlexValue); @@ -483,40 +583,37 @@ double RenderGrid::computeNormalizedFractionBreadth(Vector<GridTrack>& tracks, G const GridTrackSize& RenderGrid::gridTrackSize(GridTrackSizingDirection direction, size_t i) const { - const Vector<GridTrackSize>& trackStyles = (direction == ForColumns) ? style()->gridDefinitionColumns() : style()->gridDefinitionRows(); + const Vector<GridTrackSize>& trackStyles = (direction == ForColumns) ? style()->gridTemplateColumns() : style()->gridTemplateRows(); if (i >= trackStyles.size()) return (direction == ForColumns) ? style()->gridAutoColumns() : style()->gridAutoRows(); - return trackStyles[i]; -} - -size_t RenderGrid::explicitGridColumnCount() const -{ - return style()->gridDefinitionColumns().size(); -} - -size_t RenderGrid::explicitGridRowCount() const -{ - return style()->gridDefinitionRows().size(); -} + const GridTrackSize& trackSize = trackStyles[i]; + // If the logical width/height of the grid container is indefinite, percentage values are treated as <auto>. + if (trackSize.isPercentage()) { + Length logicalSize = direction == ForColumns ? style()->logicalWidth() : style()->logicalHeight(); + if (logicalSize.isIntrinsicOrAuto()) { + DEFINE_STATIC_LOCAL(GridTrackSize, autoTrackSize, (Length(Auto))); + return autoTrackSize; + } + } -size_t RenderGrid::explicitGridSizeForSide(GridPositionSide side) const -{ - return (side == ColumnStartSide || side == ColumnEndSide) ? explicitGridColumnCount() : explicitGridRowCount(); + return trackSize; } -LayoutUnit RenderGrid::logicalContentHeightForChild(RenderBox* child, Vector<GridTrack>& columnTracks) +LayoutUnit RenderGrid::logicalHeightForChild(RenderBox* child, Vector<GridTrack>& columnTracks) { - SubtreeLayoutScope layoutScope(child); - if (child->style()->logicalHeight().isPercent()) + SubtreeLayoutScope layoutScope(*child); + LayoutUnit oldOverrideContainingBlockContentLogicalWidth = child->hasOverrideContainingBlockLogicalWidth() ? child->overrideContainingBlockContentLogicalWidth() : LayoutUnit(); + LayoutUnit overrideContainingBlockContentLogicalWidth = gridAreaBreadthForChild(child, ForColumns, columnTracks); + if (child->style()->logicalHeight().isPercent() || oldOverrideContainingBlockContentLogicalWidth != overrideContainingBlockContentLogicalWidth) layoutScope.setNeedsLayout(child); - child->setOverrideContainingBlockContentLogicalWidth(gridAreaBreadthForChild(child, ForColumns, columnTracks)); + child->setOverrideContainingBlockContentLogicalWidth(overrideContainingBlockContentLogicalWidth); // 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(); + return child->logicalHeight() + child->marginLogicalHeight(); } LayoutUnit RenderGrid::minContentForChild(RenderBox* child, GridTrackSizingDirection direction, Vector<GridTrack>& columnTracks) @@ -532,7 +629,7 @@ LayoutUnit RenderGrid::minContentForChild(RenderBox* child, GridTrackSizingDirec return child->minPreferredLogicalWidth() + marginIntrinsicLogicalWidthForChild(child); } - return logicalContentHeightForChild(child, columnTracks); + return logicalHeightForChild(child, columnTracks); } LayoutUnit RenderGrid::maxContentForChild(RenderBox* child, GridTrackSizingDirection direction, Vector<GridTrack>& columnTracks) @@ -548,7 +645,7 @@ LayoutUnit RenderGrid::maxContentForChild(RenderBox* child, GridTrackSizingDirec return child->maxPreferredLogicalWidth() + marginIntrinsicLogicalWidthForChild(child); } - return logicalContentHeightForChild(child, columnTracks); + return logicalHeightForChild(child, columnTracks); } void RenderGrid::resolveContentBasedTrackSizingFunctions(GridTrackSizingDirection direction, GridSizingData& sizingData, LayoutUnit& availableLogicalSpace) @@ -575,16 +672,16 @@ void RenderGrid::resolveContentBasedTrackSizingFunctions(GridTrackSizingDirectio void RenderGrid::resolveContentBasedTrackSizingFunctionsForItems(GridTrackSizingDirection 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; + const GridResolvedPosition initialTrackPosition = (direction == ForColumns) ? coordinate.columns.resolvedInitialPosition : coordinate.rows.resolvedInitialPosition; + const GridResolvedPosition finalTrackPosition = (direction == ForColumns) ? coordinate.columns.resolvedFinalPosition : coordinate.rows.resolvedFinalPosition; sizingData.filteredTracks.shrink(0); - for (size_t trackIndex = initialTrackIndex; trackIndex <= finalTrackIndex; ++trackIndex) { - const GridTrackSize& trackSize = gridTrackSize(direction, trackIndex); + for (GridResolvedPosition trackPosition = initialTrackPosition; trackPosition <= finalTrackPosition; ++trackPosition) { + const GridTrackSize& trackSize = gridTrackSize(direction, trackPosition.toInt()); if (!(trackSize.*filterFunction)()) continue; - GridTrack& track = (direction == ForColumns) ? sizingData.columnTracks[trackIndex] : sizingData.rowTracks[trackIndex]; + GridTrack& track = (direction == ForColumns) ? sizingData.columnTracks[trackPosition.toInt()] : sizingData.rowTracks[trackPosition.toInt()]; sizingData.filteredTracks.append(&track); } @@ -592,8 +689,8 @@ void RenderGrid::resolveContentBasedTrackSizingFunctionsForItems(GridTrackSizing return; 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]; + for (GridResolvedPosition trackIndexForSpace = initialTrackPosition; trackIndexForSpace <= finalTrackPosition; ++trackIndexForSpace) { + GridTrack& track = (direction == ForColumns) ? sizingData.columnTracks[trackIndexForSpace.toInt()] : sizingData.rowTracks[trackIndexForSpace.toInt()]; additionalBreadthSpace -= (track.*trackGetter)(); } @@ -655,36 +752,33 @@ bool RenderGrid::tracksAreWiderThanMinTrackBreadth(GridTrackSizingDirection dire } #endif -void RenderGrid::growGrid(GridTrackSizingDirection direction) +void RenderGrid::ensureGridSize(size_t maximumRowIndex, size_t maximumColumnIndex) { - 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()); + const size_t oldRowSize = gridRowCount(); + if (maximumRowIndex >= oldRowSize) { + m_grid.grow(maximumRowIndex + 1); + for (size_t row = oldRowSize; row < gridRowCount(); ++row) + m_grid[row].grow(gridColumnCount()); + } + + if (maximumColumnIndex >= gridColumnCount()) { + for (size_t row = 0; row < gridRowCount(); ++row) + m_grid[row].grow(maximumColumnIndex + 1); } } void RenderGrid::insertItemIntoGrid(RenderBox* child, const GridCoordinate& coordinate) { - 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); + ensureGridSize(coordinate.rows.resolvedFinalPosition.toInt(), coordinate.columns.resolvedFinalPosition.toInt()); + + for (GridSpan::iterator row = coordinate.rows.begin(); row != coordinate.rows.end(); ++row) { + for (GridSpan::iterator column = coordinate.columns.begin(); column != coordinate.columns.end(); ++column) + m_grid[row.toInt()][column.toInt()].append(child); } m_gridItemCoordinate.set(child, coordinate); } -void RenderGrid::insertItemIntoGrid(RenderBox* child, size_t rowTrack, size_t columnTrack) -{ - const GridSpan& rowSpan = resolveGridPositionsFromAutoPlacementPosition(child, ForRows, rowTrack); - const GridSpan& columnSpan = resolveGridPositionsFromAutoPlacementPosition(child, ForColumns, columnTrack); - insertItemIntoGrid(child, GridCoordinate(rowSpan, columnSpan)); -} - void RenderGrid::placeItemsOnGrid() { if (!gridIsDirty()) @@ -704,8 +798,8 @@ void RenderGrid::placeItemsOnGrid() 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); + OwnPtr<GridSpan> rowPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *child, ForRows); + OwnPtr<GridSpan> columnPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *child, ForColumns); if (!rowPositions || !columnPositions) { GridSpan* majorAxisPositions = (autoPlacementMajorAxisDirection() == ForColumns) ? columnPositions.get() : rowPositions.get(); if (!majorAxisPositions) @@ -717,8 +811,8 @@ void RenderGrid::placeItemsOnGrid() insertItemIntoGrid(child, GridCoordinate(*rowPositions, *columnPositions)); } - ASSERT(gridRowCount() >= style()->gridDefinitionRows().size()); - ASSERT(gridColumnCount() >= style()->gridDefinitionColumns().size()); + ASSERT(gridRowCount() >= style()->gridTemplateRows().size()); + ASSERT(gridColumnCount() >= style()->gridTemplateColumns().size()); if (autoFlow == AutoFlowNone) { // If we did collect some grid items, they won't be placed thus never laid out. @@ -737,22 +831,35 @@ void RenderGrid::populateExplicitGridAndOrderIterator() { OrderIteratorPopulator populator(m_orderIterator); - size_t maximumRowIndex = std::max<size_t>(1, explicitGridRowCount()); - size_t maximumColumnIndex = std::max<size_t>(1, explicitGridColumnCount()); + size_t maximumRowIndex = std::max<size_t>(1, GridResolvedPosition::explicitGridRowCount(*style())); + size_t maximumColumnIndex = std::max<size_t>(1, GridResolvedPosition::explicitGridColumnCount(*style())); + ASSERT(m_gridItemsIndexesMap.isEmpty()); + size_t childIndex = 0; for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { populator.collectChild(child); + m_gridItemsIndexesMap.set(child, childIndex++); // 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); + OwnPtr<GridSpan> rowPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *child, ForRows); + OwnPtr<GridSpan> columnPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *child, ForColumns); + + // |positions| is 0 if we need to run the auto-placement algorithm. + if (rowPositions) { + maximumRowIndex = std::max<size_t>(maximumRowIndex, rowPositions->resolvedFinalPosition.next().toInt()); + } else { + // Grow the grid for items with a definite row span, getting the largest such span. + GridSpan positions = GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(*style(), *child, ForRows, GridResolvedPosition(0)); + maximumRowIndex = std::max<size_t>(maximumRowIndex, positions.resolvedFinalPosition.next().toInt()); + } + + if (columnPositions) { + maximumColumnIndex = std::max<size_t>(maximumColumnIndex, columnPositions->resolvedFinalPosition.next().toInt()); + } else { + // Grow the grid for items with a definite column span, getting the largest such span. + GridSpan positions = GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(*style(), *child, ForColumns, GridResolvedPosition(0)); + maximumColumnIndex = std::max<size_t>(maximumColumnIndex, positions.resolvedFinalPosition.next().toInt()); + } } m_grid.grow(maximumRowIndex); @@ -760,20 +867,25 @@ void RenderGrid::populateExplicitGridAndOrderIterator() m_grid[i].grow(maximumColumnIndex); } +PassOwnPtr<GridCoordinate> RenderGrid::createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(const RenderBox* gridItem, GridTrackSizingDirection specifiedDirection, const GridSpan& specifiedPositions) const +{ + GridTrackSizingDirection crossDirection = specifiedDirection == ForColumns ? ForRows : ForColumns; + const size_t endOfCrossDirection = crossDirection == ForColumns ? gridColumnCount() : gridRowCount(); + GridSpan crossDirectionPositions = GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(*style(), *gridItem, crossDirection, GridResolvedPosition(endOfCrossDirection)); + return adoptPtr(new GridCoordinate(specifiedDirection == ForColumns ? crossDirectionPositions : specifiedPositions, specifiedDirection == ForColumns ? specifiedPositions : crossDirectionPositions)); +} + void RenderGrid::placeSpecifiedMajorAxisItemsOnGrid(const Vector<RenderBox*>& autoGridItems) { 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); + OwnPtr<GridSpan> majorAxisPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *autoGridItems[i], autoPlacementMajorAxisDirection()); + GridSpan minorAxisPositions = GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(*style(), *autoGridItems[i], autoPlacementMinorAxisDirection(), GridResolvedPosition(0)); + + GridIterator iterator(m_grid, autoPlacementMajorAxisDirection(), majorAxisPositions->resolvedInitialPosition.toInt()); + OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea(majorAxisPositions->integerSpan(), minorAxisPositions.integerSpan()); + if (!emptyGridArea) + emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(autoGridItems[i], autoPlacementMajorAxisDirection(), *majorAxisPositions); + insertItemIntoGrid(autoGridItems[i], *emptyGridArea); } } @@ -785,32 +897,41 @@ void RenderGrid::placeAutoMajorAxisItemsOnGrid(const Vector<RenderBox*>& autoGri void RenderGrid::placeAutoMajorAxisItemOnGrid(RenderBox* gridItem) { - OwnPtr<GridSpan> minorAxisPositions = resolveGridPositionsFromStyle(gridItem, autoPlacementMinorAxisDirection()); - ASSERT(!resolveGridPositionsFromStyle(gridItem, autoPlacementMajorAxisDirection())); - size_t minorAxisIndex = 0; + OwnPtr<GridSpan> minorAxisPositions = GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *gridItem, autoPlacementMinorAxisDirection()); + ASSERT(!GridResolvedPosition::resolveGridPositionsFromStyle(*style(), *gridItem, autoPlacementMajorAxisDirection())); + GridSpan majorAxisPositions = GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(*style(), *gridItem, autoPlacementMajorAxisDirection(), GridResolvedPosition(0)); + OwnPtr<GridCoordinate> emptyGridArea; 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; - } + GridIterator iterator(m_grid, autoPlacementMinorAxisDirection(), minorAxisPositions->resolvedInitialPosition.toInt()); + emptyGridArea = iterator.nextEmptyGridArea(minorAxisPositions->integerSpan(), majorAxisPositions.integerSpan()); + if (!emptyGridArea) + emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(gridItem, autoPlacementMinorAxisDirection(), *minorAxisPositions); } else { + GridSpan minorAxisPositions = GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(*style(), *gridItem, autoPlacementMinorAxisDirection(), GridResolvedPosition(0)); + 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; + emptyGridArea = iterator.nextEmptyGridArea(majorAxisPositions.integerSpan(), minorAxisPositions.integerSpan()); + + 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()). + GridResolvedPosition minorAxisFinalPositionIndex = autoPlacementMinorAxisDirection() == ForColumns ? emptyGridArea->columns.resolvedFinalPosition : emptyGridArea->rows.resolvedFinalPosition; + const size_t endOfMinorAxis = autoPlacementMinorAxisDirection() == ForColumns ? gridColumnCount() : gridRowCount(); + if (minorAxisFinalPositionIndex.toInt() < 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; } } + + if (!emptyGridArea) + emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(gridItem, autoPlacementMinorAxisDirection(), minorAxisPositions); } - // 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); + insertItemIntoGrid(gridItem, *emptyGridArea); } GridTrackSizingDirection RenderGrid::autoPlacementMajorAxisDirection() const @@ -832,6 +953,8 @@ void RenderGrid::dirtyGrid() m_grid.resize(0); m_gridItemCoordinate.clear(); m_gridIsDirty = true; + m_gridItemsOverflowingGridArea.resize(0); + m_gridItemsIndexesMap.clear(); } void RenderGrid::layoutGridItems() @@ -839,12 +962,13 @@ void RenderGrid::layoutGridItems() placeItemsOnGrid(); GridSizingData sizingData(gridColumnCount(), gridRowCount()); - computedUsedBreadthOfGridTracks(ForColumns, sizingData); + computeUsedBreadthOfGridTracks(ForColumns, sizingData); ASSERT(tracksAreWiderThanMinTrackBreadth(ForColumns, sizingData.columnTracks)); - computedUsedBreadthOfGridTracks(ForRows, sizingData); + computeUsedBreadthOfGridTracks(ForRows, sizingData); ASSERT(tracksAreWiderThanMinTrackBreadth(ForRows, sizingData.rowTracks)); populateGridPositions(sizingData); + m_gridItemsOverflowingGridArea.resize(0); for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { // Because the grid area cannot be styled, we don't need to adjust @@ -855,7 +979,7 @@ void RenderGrid::layoutGridItems() LayoutUnit overrideContainingBlockContentLogicalWidth = gridAreaBreadthForChild(child, ForColumns, sizingData.columnTracks); LayoutUnit overrideContainingBlockContentLogicalHeight = gridAreaBreadthForChild(child, ForRows, sizingData.rowTracks); - SubtreeLayoutScope layoutScope(child); + SubtreeLayoutScope layoutScope(*child); if (oldOverrideContainingBlockContentLogicalWidth != overrideContainingBlockContentLogicalWidth || (oldOverrideContainingBlockContentLogicalHeight != overrideContainingBlockContentLogicalHeight && child->hasRelativeLogicalHeight())) layoutScope.setNeedsLayout(child); @@ -869,16 +993,23 @@ void RenderGrid::layoutGridItems() // now, just size as if we were a regular child. child->layoutIfNeeded(); - child->setLogicalLocation(findChildLogicalPosition(child, sizingData)); +#ifndef NDEBUG + const GridCoordinate& coordinate = cachedGridCoordinate(child); + ASSERT(coordinate.columns.resolvedInitialPosition.toInt() < sizingData.columnTracks.size()); + ASSERT(coordinate.rows.resolvedInitialPosition.toInt() < sizingData.rowTracks.size()); +#endif + child->setLogicalLocation(findChildLogicalPosition(child)); - // For correctness, we disable some painting optimizations if we have a child overflowing its grid area. - m_gridItemOverflowGridArea = child->logicalHeight() > overrideContainingBlockContentLogicalHeight - || child->logicalWidth() > overrideContainingBlockContentLogicalWidth; + // Keep track of children overflowing their grid area as we might need to paint them even if the grid-area is + // not visible + if (child->logicalHeight() > overrideContainingBlockContentLogicalHeight + || child->logicalWidth() > overrideContainingBlockContentLogicalWidth) + m_gridItemsOverflowingGridArea.append(child); // If the child moved, we have to repaint it as well as any floating/positioned // descendants. An exception is if we need a layout. In this case, we know we're going to // repaint ourselves (and the child) anyway. - if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) + if (!selfNeedsLayout() && child->checkForPaintInvalidationDuringLayout()) child->repaintDuringLayoutIfMoved(oldChildRect); } @@ -896,243 +1027,219 @@ GridCoordinate RenderGrid::cachedGridCoordinate(const RenderBox* gridItem) const return m_gridItemCoordinate.get(gridItem); } -GridSpan RenderGrid::resolveGridPositionsFromAutoPlacementPosition(const RenderBox*, GridTrackSizingDirection, size_t initialPosition) const +LayoutUnit RenderGrid::gridAreaBreadthForChild(const RenderBox* child, GridTrackSizingDirection direction, const Vector<GridTrack>& tracks) const { - // 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); + const GridCoordinate& coordinate = cachedGridCoordinate(child); + const GridSpan& span = (direction == ForColumns) ? coordinate.columns : coordinate.rows; + LayoutUnit gridAreaBreadth = 0; + for (GridSpan::iterator trackPosition = span.begin(); trackPosition != span.end(); ++trackPosition) + gridAreaBreadth += tracks[trackPosition.toInt()].m_usedBreadth; + return gridAreaBreadth; } -PassOwnPtr<GridSpan> RenderGrid::resolveGridPositionsFromStyle(const RenderBox* gridItem, GridTrackSizingDirection direction) const +void RenderGrid::populateGridPositions(const GridSizingData& sizingData) { - const GridPosition& initialPosition = (direction == ForColumns) ? gridItem->style()->gridColumnStart() : gridItem->style()->gridRowStart(); - const GridPositionSide initialPositionSide = (direction == ForColumns) ? ColumnStartSide : RowStartSide; - const GridPosition& finalPosition = (direction == ForColumns) ? gridItem->style()->gridColumnEnd() : gridItem->style()->gridRowEnd(); - const GridPositionSide finalPositionSide = (direction == ForColumns) ? ColumnEndSide : RowEndSide; - - // We should NEVER see both spans as they should have been handled during style resolve. - ASSERT(!initialPosition.isSpan() || !finalPosition.isSpan()); - - if (initialPosition.shouldBeResolvedAgainstOppositePosition() && finalPosition.shouldBeResolvedAgainstOppositePosition()) { - if (style()->gridAutoFlow() == AutoFlowNone) - return adoptPtr(new GridSpan(0, 0)); - - // We can't get our grid positions without running the auto placement algorithm. - return nullptr; - } - - 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); - } - - 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); - } - - size_t resolvedInitialPosition = resolveGridPositionFromStyle(initialPosition, initialPositionSide); - size_t resolvedFinalPosition = resolveGridPositionFromStyle(finalPosition, finalPositionSide); - - // If 'grid-after' specifies a line at or before that specified by 'grid-before', it computes to 'span 1'. - if (resolvedFinalPosition < resolvedInitialPosition) - resolvedFinalPosition = resolvedInitialPosition; + m_columnPositions.resize(sizingData.columnTracks.size() + 1); + m_columnPositions[0] = borderAndPaddingStart(); + for (size_t i = 0; i < m_columnPositions.size() - 1; ++i) + m_columnPositions[i + 1] = m_columnPositions[i] + sizingData.columnTracks[i].m_usedBreadth; - return adoptPtr(new GridSpan(resolvedInitialPosition, resolvedFinalPosition)); + m_rowPositions.resize(sizingData.rowTracks.size() + 1); + m_rowPositions[0] = borderAndPaddingBefore(); + for (size_t i = 0; i < m_rowPositions.size() - 1; ++i) + m_rowPositions[i + 1] = m_rowPositions[i] + sizingData.rowTracks[i].m_usedBreadth; } -size_t RenderGrid::resolveNamedGridLinePositionFromStyle(const GridPosition& position, GridPositionSide side) const +LayoutUnit RenderGrid::startOfColumnForChild(const RenderBox* child) const { - ASSERT(!position.namedGridLine().isNull()); - - 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 GridPosition::adjustGridPositionForSide(lastLine, side); - } - - 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 GridPosition::adjustGridPositionForSide(it->value[namedGridLineIndex], side); + const GridCoordinate& coordinate = cachedGridCoordinate(child); + LayoutUnit startOfColumn = m_columnPositions[coordinate.columns.resolvedInitialPosition.toInt()]; + // The grid items should be inside the grid container's border box, that's why they need to be shifted. + // FIXME: This should account for the grid item's <overflow-position>. + return startOfColumn + marginStartForChild(child); } -size_t RenderGrid::resolveGridPositionFromStyle(const GridPosition& position, GridPositionSide side) const +LayoutUnit RenderGrid::endOfColumnForChild(const RenderBox* child) const { - switch (position.type()) { - case ExplicitPosition: { - ASSERT(position.integerPosition()); - - if (!position.namedGridLine().isNull()) - return resolveNamedGridLinePositionFromStyle(position, side); - - // Handle <integer> explicit position. - if (position.isPositive()) - return GridPosition::adjustGridPositionForSide(position.integerPosition() - 1, side); - - size_t resolvedPosition = abs(position.integerPosition()) - 1; - const size_t endOfTrack = explicitGridSizeForSide(side); - - // 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; + const GridCoordinate& coordinate = cachedGridCoordinate(child); + LayoutUnit startOfColumn = m_columnPositions[coordinate.columns.resolvedInitialPosition.toInt()]; + // The grid items should be inside the grid container's border box, that's why they need to be shifted. + LayoutUnit columnPosition = startOfColumn + marginStartForChild(child); - return GridPosition::adjustGridPositionForSide(endOfTrack - resolvedPosition, side); - } - case NamedGridAreaPosition: - { - NamedGridAreaMap::const_iterator it = style()->namedGridArea().find(position.namedGridLine()); - // Unknown grid area should have been computed to 'auto' by now. - ASSERT_WITH_SECURITY_IMPLICATION(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; - } - 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; - } - ASSERT_NOT_REACHED(); - return 0; + LayoutUnit endOfColumn = m_columnPositions[coordinate.columns.resolvedFinalPosition.next().toInt()]; + // FIXME: This should account for the grid item's <overflow-position>. + return columnPosition + std::max<LayoutUnit>(0, endOfColumn - m_columnPositions[coordinate.columns.resolvedInitialPosition.toInt()] - child->logicalWidth()); } -PassOwnPtr<GridSpan> RenderGrid::resolveGridPositionAgainstOppositePosition(size_t resolvedOppositePosition, const GridPosition& position, GridPositionSide side) const +LayoutUnit RenderGrid::columnPositionAlignedWithGridContainerStart(const RenderBox* child) const { - if (position.isAuto()) - return GridSpan::create(resolvedOppositePosition, resolvedOppositePosition); + if (style()->isLeftToRightDirection()) + return startOfColumnForChild(child); - ASSERT(position.isSpan()); - ASSERT(position.spanPosition() > 0); - - 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); - } - - return GridSpan::createWithSpanAgainstOpposite(resolvedOppositePosition, position, side); + return endOfColumnForChild(child); } -PassOwnPtr<GridSpan> RenderGrid::resolveNamedGridLinePositionAgainstOppositePosition(size_t resolvedOppositePosition, const GridPosition& position, GridPositionSide side) const +LayoutUnit RenderGrid::columnPositionAlignedWithGridContainerEnd(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); + if (!style()->isLeftToRightDirection()) + return startOfColumnForChild(child); - const NamedGridLinesMap& gridLinesNames = (side == ColumnStartSide || side == ColumnEndSide) ? style()->namedGridColumnLines() : style()->namedGridRowLines(); - NamedGridLinesMap::const_iterator it = gridLinesNames.find(position.namedGridLine()); - - // 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); - - return GridSpan::createWithNamedSpanAgainstOpposite(resolvedOppositePosition, position, side, it->value); + return endOfColumnForChild(child); } -LayoutUnit RenderGrid::gridAreaBreadthForChild(const RenderBox* child, GridTrackSizingDirection direction, const Vector<GridTrack>& tracks) const +LayoutUnit RenderGrid::centeredColumnPositionForChild(const RenderBox* child) 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; + LayoutUnit startOfColumn = m_columnPositions[coordinate.columns.resolvedInitialPosition.toInt()]; + LayoutUnit endOfColumn = m_columnPositions[coordinate.columns.resolvedFinalPosition.next().toInt()]; + LayoutUnit columnPosition = startOfColumn + marginStartForChild(child); + return columnPosition + std::max<LayoutUnit>(0, endOfColumn - startOfColumn - child->logicalWidth()) / 2; } -void RenderGrid::populateGridPositions(const GridSizingData& sizingData) +LayoutUnit RenderGrid::columnPositionForChild(const RenderBox* child) const { - m_columnPositions.resize(sizingData.columnTracks.size() + 1); - m_columnPositions[0] = borderAndPaddingStart(); - for (size_t i = 0; i < m_columnPositions.size() - 1; ++i) - m_columnPositions[i + 1] = m_columnPositions[i] + sizingData.columnTracks[i].m_usedBreadth; + ItemPosition childJustifySelf = child->style()->justifySelf(); + switch (childJustifySelf) { + case ItemPositionSelfStart: + // self-start is based on the child's direction. That's why we need to check against the grid container's direction. + if (child->style()->direction() != style()->direction()) + return columnPositionAlignedWithGridContainerEnd(child); + + return columnPositionAlignedWithGridContainerStart(child); + case ItemPositionSelfEnd: + // self-end is based on the child's direction. That's why we need to check against the grid container's direction. + if (child->style()->direction() != style()->direction()) + return columnPositionAlignedWithGridContainerStart(child); + + return columnPositionAlignedWithGridContainerEnd(child); + + case ItemPositionFlexStart: + case ItemPositionFlexEnd: + // Only used in flex layout, for other layout, it's equivalent to 'start'. + return columnPositionAlignedWithGridContainerStart(child); + + case ItemPositionLeft: + // If the property's axis is not parallel with the inline axis, this is equivalent to ‘start’. + if (!isHorizontalWritingMode()) + return columnPositionAlignedWithGridContainerStart(child); + + if (style()->isLeftToRightDirection()) + return columnPositionAlignedWithGridContainerStart(child); + + return columnPositionAlignedWithGridContainerEnd(child); + case ItemPositionRight: + // If the property's axis is not parallel with the inline axis, this is equivalent to ‘start’. + if (!isHorizontalWritingMode()) + return columnPositionAlignedWithGridContainerStart(child); + + if (style()->isLeftToRightDirection()) + return columnPositionAlignedWithGridContainerEnd(child); + + return columnPositionAlignedWithGridContainerStart(child); + + case ItemPositionCenter: + return centeredColumnPositionForChild(child); + case ItemPositionStart: + return columnPositionAlignedWithGridContainerStart(child); + case ItemPositionEnd: + return columnPositionAlignedWithGridContainerEnd(child); + + case ItemPositionAuto: + case ItemPositionStretch: + case ItemPositionBaseline: + // FIXME: Implement the previous values. For now, we always start align the child. + return startOfColumnForChild(child); + } - m_rowPositions.resize(sizingData.rowTracks.size() + 1); - m_rowPositions[0] = borderAndPaddingBefore(); - for (size_t i = 0; i < m_rowPositions.size() - 1; ++i) - m_rowPositions[i + 1] = m_rowPositions[i] + sizingData.rowTracks[i].m_usedBreadth; + ASSERT_NOT_REACHED(); + return 0; } -LayoutPoint RenderGrid::findChildLogicalPosition(RenderBox* child, const GridSizingData& sizingData) +LayoutUnit RenderGrid::rowPositionForChild(const RenderBox* child) const { const GridCoordinate& coordinate = cachedGridCoordinate(child); - ASSERT(coordinate.columns.initialPositionIndex < sizingData.columnTracks.size()); - ASSERT(coordinate.rows.initialPositionIndex < sizingData.rowTracks.size()); // The grid items should be inside the grid container's border box, that's why they need to be shifted. - return LayoutPoint(m_columnPositions[coordinate.columns.initialPositionIndex] + marginStartForChild(child), m_rowPositions[coordinate.rows.initialPositionIndex] + marginBeforeForChild(child)); + LayoutUnit startOfRow = m_rowPositions[coordinate.rows.resolvedInitialPosition.toInt()]; + LayoutUnit rowPosition = startOfRow + marginBeforeForChild(child); + + // FIXME: This function should account for 'align-self'. + + return rowPosition; +} + +LayoutPoint RenderGrid::findChildLogicalPosition(const RenderBox* child) const +{ + return LayoutPoint(columnPositionForChild(child), rowPositionForChild(child)); } static GridSpan dirtiedGridAreas(const Vector<LayoutUnit>& coordinates, LayoutUnit start, LayoutUnit end) { // This function does a binary search over the coordinates. - // FIXME: This doesn't work with grid items overflowing their grid areas and should be tested & fixed. + // This doesn't work with grid items overflowing their grid areas, but that is managed with m_gridItemsOverflowingGridArea. size_t startGridAreaIndex = std::upper_bound(coordinates.begin(), coordinates.end() - 1, start) - coordinates.begin(); if (startGridAreaIndex > 0) --startGridAreaIndex; size_t endGridAreaIndex = std::upper_bound(coordinates.begin() + startGridAreaIndex, coordinates.end() - 1, end) - coordinates.begin(); + if (endGridAreaIndex > 0) + --endGridAreaIndex; + return GridSpan(startGridAreaIndex, endGridAreaIndex); } -void RenderGrid::paintChildrenSlowCase(PaintInfo& paintInfo, const LayoutPoint& paintOffset) -{ - for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) - paintChild(child, paintInfo, paintOffset); -} +class GridItemsSorter { +public: + bool operator()(const std::pair<RenderBox*, size_t> firstChild, const std::pair<RenderBox*, size_t> secondChild) const + { + if (firstChild.first->style()->order() != secondChild.first->style()->order()) + return firstChild.first->style()->order() < secondChild.first->style()->order(); + + return firstChild.second < secondChild.second; + } +}; void RenderGrid::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { ASSERT_WITH_SECURITY_IMPLICATION(!gridIsDirty()); - if (m_gridItemOverflowGridArea) { - paintChildrenSlowCase(paintInfo, paintOffset); - return; - } - LayoutRect localRepaintRect = paintInfo.rect; localRepaintRect.moveBy(-paintOffset); GridSpan dirtiedColumns = dirtiedGridAreas(m_columnPositions, localRepaintRect.x(), localRepaintRect.maxX()); GridSpan dirtiedRows = dirtiedGridAreas(m_rowPositions, localRepaintRect.y(), localRepaintRect.maxY()); - OrderIterator paintIterator(this); - { - OrderIteratorPopulator populator(paintIterator); - - for (size_t row = dirtiedRows.initialPositionIndex; row < dirtiedRows.finalPositionIndex; ++row) { - for (size_t column = dirtiedColumns.initialPositionIndex; column < dirtiedColumns.finalPositionIndex; ++column) { - const Vector<RenderBox*, 1>& children = m_grid[row][column]; - // FIXME: If we start adding spanning children in all grid areas they span, this - // would make us paint them several times, which is wrong! - for (size_t j = 0; j < children.size(); ++j) - populator.storeChild(children[j]); - } + Vector<std::pair<RenderBox*, size_t> > gridItemsToBePainted; + + for (GridSpan::iterator row = dirtiedRows.begin(); row != dirtiedRows.end(); ++row) { + for (GridSpan::iterator column = dirtiedColumns.begin(); column != dirtiedColumns.end(); ++column) { + const Vector<RenderBox*, 1>& children = m_grid[row.toInt()][column.toInt()]; + for (size_t j = 0; j < children.size(); ++j) + gridItemsToBePainted.append(std::make_pair(children[j], m_gridItemsIndexesMap.get(children[j]))); } } - for (RenderBox* child = paintIterator.first(); child; child = paintIterator.next()) - paintChild(child, paintInfo, paintOffset); + for (Vector<RenderBox*>::const_iterator it = m_gridItemsOverflowingGridArea.begin(); it != m_gridItemsOverflowingGridArea.end(); ++it) { + if ((*it)->frameRect().intersects(localRepaintRect)) + gridItemsToBePainted.append(std::make_pair(*it, m_gridItemsIndexesMap.get(*it))); + } + + // Sort grid items following order-modified document order. + // See http://www.w3.org/TR/css-flexbox/#order-modified-document-order + std::stable_sort(gridItemsToBePainted.begin(), gridItemsToBePainted.end(), GridItemsSorter()); + + RenderBox* previous = 0; + for (Vector<std::pair<RenderBox*, size_t> >::const_iterator it = gridItemsToBePainted.begin(); it != gridItemsToBePainted.end(); ++it) { + // We might have duplicates because of spanning children are included in all cells they span. + // Skip them here to avoid painting items several times. + RenderBox* current = (*it).first; + if (current == previous) + continue; + + paintChild(current, paintInfo, paintOffset); + previous = current; + } } const char* RenderGrid::renderName() const |