diff options
| author | Allan Sandfeld Jensen <allan.jensen@digia.com> | 2013-09-13 12:51:20 +0200 |
|---|---|---|
| committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-19 20:50:05 +0200 |
| commit | d441d6f39bb846989d95bcf5caf387b42414718d (patch) | |
| tree | e367e64a75991c554930278175d403c072de6bb8 /Source/WebCore/rendering/RenderGrid.cpp | |
| parent | 0060b2994c07842f4c59de64b5e3e430525c4b90 (diff) | |
| download | qtwebkit-d441d6f39bb846989d95bcf5caf387b42414718d.tar.gz | |
Import Qt5x2 branch of QtWebkit for Qt 5.2
Importing a new snapshot of webkit.
Change-Id: I2d01ad12cdc8af8cb015387641120a9d7ea5f10c
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@digia.com>
Diffstat (limited to 'Source/WebCore/rendering/RenderGrid.cpp')
| -rw-r--r-- | Source/WebCore/rendering/RenderGrid.cpp | 706 |
1 files changed, 643 insertions, 63 deletions
diff --git a/Source/WebCore/rendering/RenderGrid.cpp b/Source/WebCore/rendering/RenderGrid.cpp index e7a351c86..a65941552 100644 --- a/Source/WebCore/rendering/RenderGrid.cpp +++ b/Source/WebCore/rendering/RenderGrid.cpp @@ -33,18 +33,101 @@ namespace WebCore { -class RenderGrid::GridTrack { +static const int infinity = intMaxForLayoutUnit; + +class GridTrack { public: GridTrack() : m_usedBreadth(0) + , m_maxBreadth(0) + { + } + + 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; } LayoutUnit m_usedBreadth; + LayoutUnit m_maxBreadth; +}; + +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()); + } + + RenderBox* nextGridItem() + { + if (!m_grid.size()) + return 0; + + 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++]; + + m_childIndex = 0; + } + return 0; + } + + 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(); + } + } + 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; }; -RenderGrid::RenderGrid(Node* node) - : RenderBlock(node) +RenderGrid::RenderGrid(Element* element) + : RenderBlock(element) { // All of our children must be block level. setChildrenInline(false); @@ -66,20 +149,18 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit) LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - if (inRenderFlowThread()) { - // Regions changing widths can force us to relayout our children. - if (logicalWidthChangedInRegions()) - relayoutChildren = true; - } - updateRegionsAndExclusionsLogicalSize(); + // Regions changing widths can force us to relayout our children. + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (logicalWidthChangedInRegions(flowThread)) + relayoutChildren = true; + if (updateRegionsAndShapesBeforeChildLayout(flowThread)) + relayoutChildren = true; LayoutSize previousSize = size(); setLogicalHeight(0); updateLogicalWidth(); - m_overflow.clear(); - layoutGridItems(); LayoutUnit oldClientAfterEdge = clientLogicalBottom(); @@ -90,7 +171,7 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit) layoutPositionedObjects(relayoutChildren || isRoot()); - computeRegionRangeForBlock(); + updateRegionsAndShapesAfterChildLayout(flowThread); computeOverflow(oldClientAfterEdge); statePusher.pop(); @@ -99,14 +180,35 @@ void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit) // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if // we overflow or not. - if (hasOverflowClip()) - layer()->updateScrollInfoAfterLayout(); + updateScrollInfoAfterLayout(); repainter.repaintAfterLayout(); setNeedsLayout(false); } +void RenderGrid::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const +{ + const_cast<RenderGrid*>(this)->placeItemsOnGrid(); + + // FIXME: This is an inefficient way to fill our sizes as it will try every grid areas, when we would + // only want to account for fixed grid tracks and grid items. Also this will be incorrect if we have spanning + // grid items. + for (size_t i = 0; i < gridColumnCount(); ++i) { + const GridTrackSize& trackSize = gridTrackSize(ForColumns, i); + LayoutUnit minTrackBreadth = computePreferredTrackWidth(trackSize.minTrackBreadth(), i); + LayoutUnit maxTrackBreadth = computePreferredTrackWidth(trackSize.maxTrackBreadth(), i); + maxTrackBreadth = std::max(maxTrackBreadth, minTrackBreadth); + + minLogicalWidth += minTrackBreadth; + maxLogicalWidth += maxTrackBreadth; + + // FIXME: This should add in the scrollbarWidth (e.g. see RenderFlexibleBox). + } + + const_cast<RenderGrid*>(this)->clearGrid(); +} + void RenderGrid::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); @@ -114,64 +216,471 @@ void RenderGrid::computePreferredLogicalWidths() m_minPreferredLogicalWidth = 0; m_maxPreferredLogicalWidth = 0; - // FIXME: We don't take our own logical width into account. + // 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. - const Vector<GridTrackSize>& trackStyles = style()->gridColumns(); + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); - for (size_t i = 0; i < trackStyles.size(); ++i) { - Length trackLength = trackStyles[i].length(); - if (!trackLength.isFixed()) { - notImplemented(); - continue; + LayoutUnit borderAndPaddingInInlineDirection = borderAndPaddingLogicalWidth(); + m_minPreferredLogicalWidth += borderAndPaddingInInlineDirection; + m_maxPreferredLogicalWidth += borderAndPaddingInInlineDirection; + + setPreferredLogicalWidthsDirty(false); +} + +LayoutUnit RenderGrid::computePreferredTrackWidth(const Length& length, size_t trackIndex) const +{ + if (length.isFixed()) { + // Grid areas don't have borders, margins or paddings so we don't need to account for them. + return length.intValue(); + } + + if (length.isMinContent()) { + LayoutUnit minContentSize = 0; + GridIterator iterator(m_grid, ForColumns, trackIndex); + while (RenderBox* gridItem = iterator.nextGridItem()) { + // FIXME: We should include the child's fixed margins like RenderFlexibleBox. + minContentSize = std::max(minContentSize, gridItem->minPreferredLogicalWidth()); } + return minContentSize; + } - m_minPreferredLogicalWidth += trackLength.intValue(); - m_maxPreferredLogicalWidth += trackLength.intValue(); + if (length.isMaxContent()) { + LayoutUnit maxContentSize = 0; + GridIterator iterator(m_grid, ForColumns, trackIndex); + while (RenderBox* gridItem = iterator.nextGridItem()) { + // FIXME: We should include the child's fixed margins like RenderFlexibleBox. + maxContentSize = std::max(maxContentSize, gridItem->maxPreferredLogicalWidth()); + } + return maxContentSize; } - // FIXME: We should account for min / max logical width. + // FIXME: css3-sizing mentions that we should resolve "definite sizes" + // (including <percentage> and calc()) but we don't do it elsewhere. + return 0; +} - // FIXME: Include borders and paddings in inline direction. +void RenderGrid::computedUsedBreadthOfGridTracks(TrackSizingDirection direction, Vector<GridTrack>& columnTracks, Vector<GridTrack>& rowTracks) +{ + LayoutUnit availableLogicalSpace = (direction == ForColumns) ? availableLogicalWidth() : availableLogicalHeight(IncludeMarginBorderPadding); + Vector<GridTrack>& tracks = (direction == ForColumns) ? columnTracks : rowTracks; + for (size_t i = 0; i < tracks.size(); ++i) { + GridTrack& track = tracks[i]; + const GridTrackSize& trackSize = gridTrackSize(direction, i); + const Length& minTrackBreadth = trackSize.minTrackBreadth(); + const Length& maxTrackBreadth = trackSize.maxTrackBreadth(); + + track.m_usedBreadth = computeUsedBreadthOfMinLength(direction, minTrackBreadth); + track.m_maxBreadth = computeUsedBreadthOfMaxLength(direction, maxTrackBreadth); + + track.m_maxBreadth = std::max(track.m_maxBreadth, track.m_usedBreadth); + } - setPreferredLogicalWidthsDirty(false); + // FIXME: We shouldn't call resolveContentBasedTrackSizingFunctions if we have no min-content / max-content tracks. + resolveContentBasedTrackSizingFunctions(direction, columnTracks, rowTracks, availableLogicalSpace); + + if (availableLogicalSpace <= 0) + return; + + const size_t tracksSize = tracks.size(); + Vector<GridTrack*> tracksForDistribution(tracksSize); + for (size_t i = 0; i < tracksSize; ++i) + tracksForDistribution[i] = tracks.data() + i; + + distributeSpaceToTracks(tracksForDistribution, 0, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth, availableLogicalSpace); +} + +LayoutUnit RenderGrid::computeUsedBreadthOfMinLength(TrackSizingDirection direction, const Length& trackLength) const +{ + if (trackLength.isFixed() || trackLength.isPercent() || trackLength.isViewportPercentage()) + return computeUsedBreadthOfSpecifiedLength(direction, trackLength); + + ASSERT(trackLength.isMinContent() || trackLength.isMaxContent() || trackLength.isAuto()); + return 0; +} + +LayoutUnit RenderGrid::computeUsedBreadthOfMaxLength(TrackSizingDirection direction, const Length& trackLength) const +{ + if (trackLength.isFixed() || trackLength.isPercent() || trackLength.isViewportPercentage()) { + LayoutUnit computedBreadth = computeUsedBreadthOfSpecifiedLength(direction, trackLength); + // FIXME: We should ASSERT that computedBreadth cannot return infinity but it's currently + // possible. See https://bugs.webkit.org/show_bug.cgi?id=107053 + return computedBreadth; + } + + ASSERT(trackLength.isMinContent() || trackLength.isMaxContent() || trackLength.isAuto()); + return infinity; } -void RenderGrid::computedUsedBreadthOfGridTracks(TrackSizingDirection direction, Vector<GridTrack>& tracks) +LayoutUnit RenderGrid::computeUsedBreadthOfSpecifiedLength(TrackSizingDirection direction, const Length& trackLength) 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()), view()); +} + +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(); + + return trackStyles[i]; +} + +static size_t estimatedGridSizeForPosition(const GridPosition& position) +{ + if (position.isAuto()) + return 1; + + return std::max(position.integerPosition(), 1); +} + +size_t RenderGrid::maximumIndexInDirection(TrackSizingDirection direction) const { const Vector<GridTrackSize>& trackStyles = (direction == ForColumns) ? style()->gridColumns() : style()->gridRows(); - for (size_t i = 0; i < trackStyles.size(); ++i) { - GridTrack track; - if (trackStyles[i].length().isFixed()) - track.m_usedBreadth = trackStyles[i].length().getFloatValue(); - else - notImplemented(); - tracks.append(track); + size_t maximumIndex = std::max<size_t>(1, trackStyles.size()); + + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + // This function bypasses the cache (cachedGridCoordinate()) as it is used to build it. + // Also we can't call resolveGridPositionsFromStyle here as it assumes that the grid is build and we are in + // the middle of building it. However we should be able to share more code with the previous logic (FIXME). + const GridPosition& initialPosition = (direction == ForColumns) ? child->style()->gridItemStart() : child->style()->gridItemBefore(); + const GridPosition& finalPosition = (direction == ForColumns) ? child->style()->gridItemEnd() : child->style()->gridItemAfter(); + + size_t estimatedSizeForInitialPosition = estimatedGridSizeForPosition(initialPosition); + size_t estimatedSizeForFinalPosition = estimatedGridSizeForPosition(finalPosition); + ASSERT(estimatedSizeForInitialPosition); + ASSERT(estimatedSizeForFinalPosition); + + maximumIndex = std::max(maximumIndex, estimatedSizeForInitialPosition); + maximumIndex = std::max(maximumIndex, estimatedSizeForFinalPosition); + } + + return maximumIndex; +} + +LayoutUnit RenderGrid::logicalContentHeightForChild(RenderBox* child, Vector<GridTrack>& columnTracks) +{ + // FIXME: We shouldn't force a layout every time this function is called but + // 1) Return computeLogicalHeight's value if it's available. Unfortunately computeLogicalHeight + // doesn't return if the logical height is available so would need to be changed. + // 2) Relayout if the column track's used breadth changed OR the logical height is unavailable. + if (!child->needsLayout()) + child->setNeedsLayout(true, MarkOnlyThis); + + 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->layout(); + return child->logicalHeight(); +} + +LayoutUnit RenderGrid::minContentForChild(RenderBox* child, TrackSizingDirection direction, Vector<GridTrack>& columnTracks) +{ + 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(); } + + return logicalContentHeightForChild(child, columnTracks); +} + +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(); + + 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(); + } + + return logicalContentHeightForChild(child, columnTracks); +} + +void RenderGrid::resolveContentBasedTrackSizingFunctions(TrackSizingDirection direction, Vector<GridTrack>& columnTracks, Vector<GridTrack>& rowTracks, LayoutUnit& availableLogicalSpace) +{ + // FIXME: Split the grid tracks once we support fractions (step 1 of the algorithm). + + Vector<GridTrack>& tracks = (direction == ForColumns) ? columnTracks : rowTracks; + + // FIXME: Per step 2 of the specification, we should order the grid items by increasing span. + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + resolveContentBasedTrackSizingFunctionsForItems(direction, columnTracks, rowTracks, child, &GridTrackSize::hasMinOrMaxContentMinTrackBreadth, &RenderGrid::minContentForChild, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth); + resolveContentBasedTrackSizingFunctionsForItems(direction, columnTracks, rowTracks, child, &GridTrackSize::hasMaxContentMinTrackBreadth, &RenderGrid::maxContentForChild, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth); + resolveContentBasedTrackSizingFunctionsForItems(direction, columnTracks, rowTracks, child, &GridTrackSize::hasMinOrMaxContentMaxTrackBreadth, &RenderGrid::minContentForChild, &GridTrack::maxBreadthIfNotInfinite, &GridTrack::growMaxBreadth); + resolveContentBasedTrackSizingFunctionsForItems(direction, columnTracks, rowTracks, child, &GridTrackSize::hasMaxContentMaxTrackBreadth, &RenderGrid::maxContentForChild, &GridTrack::maxBreadthIfNotInfinite, &GridTrack::growMaxBreadth); + } + + for (size_t i = 0; i < tracks.size(); ++i) { + GridTrack& track = tracks[i]; + if (track.m_maxBreadth == infinity) + track.m_maxBreadth = track.m_usedBreadth; + + availableLogicalSpace -= track.m_usedBreadth; + } +} + +void RenderGrid::resolveContentBasedTrackSizingFunctionsForItems(TrackSizingDirection direction, Vector<GridTrack>& columnTracks, Vector<GridTrack>& rowTracks, 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; + + Vector<GridTrack*> tracks; + for (size_t trackIndex = initialTrackIndex; trackIndex <= finalTrackIndex; ++trackIndex) { + const GridTrackSize& trackSize = gridTrackSize(direction, trackIndex); + if (!(trackSize.*filterFunction)()) + continue; + + GridTrack& track = (direction == ForColumns) ? columnTracks[trackIndex] : rowTracks[trackIndex]; + tracks.append(&track); + } + + LayoutUnit additionalBreadthSpace = (this->*sizingFunction)(gridItem, direction, columnTracks); + for (size_t trackIndexForSpace = initialTrackIndex; trackIndexForSpace <= finalTrackIndex; ++trackIndexForSpace) { + GridTrack& track = (direction == ForColumns) ? columnTracks[trackIndexForSpace] : rowTracks[trackIndexForSpace]; + additionalBreadthSpace -= (track.*trackGetter)(); + } + + // FIXME: We should pass different values for |tracksForGrowthAboveMaxBreadth|. + distributeSpaceToTracks(tracks, &tracks, trackGetter, trackGrowthFunction, additionalBreadthSpace); +} + +static bool sortByGridTrackGrowthPotential(const GridTrack* track1, const GridTrack* track2) +{ + return (track1->m_maxBreadth - track1->m_usedBreadth) < (track2->m_maxBreadth - track2->m_usedBreadth); +} + +void RenderGrid::distributeSpaceToTracks(Vector<GridTrack*>& tracks, Vector<GridTrack*>* tracksForGrowthAboveMaxBreadth, AccumulatorGetter trackGetter, AccumulatorGrowFunction trackGrowthFunction, LayoutUnit& availableLogicalSpace) +{ + std::sort(tracks.begin(), tracks.end(), sortByGridTrackGrowthPotential); + + size_t tracksSize = tracks.size(); + Vector<LayoutUnit> updatedTrackBreadths(tracksSize); + + 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::min(availableLogicalSpaceShare, track.m_maxBreadth - trackBreadth); + updatedTrackBreadths[i] = trackBreadth + growthShare; + availableLogicalSpace -= growthShare; + } + + if (availableLogicalSpace > 0 && tracksForGrowthAboveMaxBreadth) { + tracksSize = tracksForGrowthAboveMaxBreadth->size(); + for (size_t i = 0; i < tracksSize; ++i) { + LayoutUnit growthShare = availableLogicalSpace / (tracksSize - i); + updatedTrackBreadths[i] += growthShare; + availableLogicalSpace -= growthShare; + } + } + + for (size_t i = 0; i < tracksSize; ++i) { + LayoutUnit growth = updatedTrackBreadths[i] - (tracks[i]->*trackGetter)(); + if (growth >= 0) + (tracks[i]->*trackGrowthFunction)(growth); + } +} + +#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 Length& minTrackBreadth = trackSize.minTrackBreadth(); + if (computeUsedBreadthOfMinLength(direction, minTrackBreadth) > tracks[i].m_usedBreadth) + return false; + } + return true; +} +#endif + +void RenderGrid::growGrid(TrackSizingDirection direction) +{ + 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()); + } +} + +void RenderGrid::insertItemIntoGrid(RenderBox* child, const GridCoordinate& coordinate) +{ + m_grid[coordinate.rows.initialPositionIndex][coordinate.columns.initialPositionIndex].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() +{ + ASSERT(!gridWasPopulated()); + ASSERT(m_gridItemCoordinate.isEmpty()); + + m_grid.grow(maximumIndexInDirection(ForRows)); + size_t maximumColumnIndex = maximumIndexInDirection(ForColumns); + for (size_t i = 0; i < m_grid.size(); ++i) + m_grid[i].grow(maximumColumnIndex); + + Vector<RenderBox*> autoMajorAxisAutoGridItems; + Vector<RenderBox*> specifiedMajorAxisAutoGridItems; + GridAutoFlow autoFlow = style()->gridAutoFlow(); + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + // 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; + } + 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; + } + + placeSpecifiedMajorAxisItemsOnGrid(specifiedMajorAxisAutoGridItems); + placeAutoMajorAxisItemsOnGrid(autoMajorAxisAutoGridItems); +} + +void RenderGrid::placeSpecifiedMajorAxisItemsOnGrid(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); + } +} + +void RenderGrid::placeAutoMajorAxisItemsOnGrid(Vector<RenderBox*> autoGridItems) +{ + for (size_t i = 0; i < autoGridItems.size(); ++i) + placeAutoMajorAxisItemOnGrid(autoGridItems[i]); +} + +void RenderGrid::placeAutoMajorAxisItemOnGrid(RenderBox* gridItem) +{ + 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; + } + } + } + + // 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); +} + +RenderGrid::TrackSizingDirection RenderGrid::autoPlacementMajorAxisDirection() const +{ + GridAutoFlow flow = style()->gridAutoFlow(); + ASSERT(flow != AutoFlowNone); + return (flow == AutoFlowColumn) ? ForColumns : ForRows; +} + +RenderGrid::TrackSizingDirection RenderGrid::autoPlacementMinorAxisDirection() const +{ + GridAutoFlow flow = style()->gridAutoFlow(); + ASSERT(flow != AutoFlowNone); + return (flow == AutoFlowColumn) ? ForRows : ForColumns; +} + +void RenderGrid::clearGrid() +{ + m_grid.clear(); + m_gridItemCoordinate.clear(); } void RenderGrid::layoutGridItems() { - Vector<GridTrack> columnTracks, rowTracks; - computedUsedBreadthOfGridTracks(ForColumns, columnTracks); - // FIXME: The logical width of Grid Columns from the prior step is used in - // the formatting of Grid items in content-sized Grid Rows to determine - // their required height. We will probably need to pass columns through. - computedUsedBreadthOfGridTracks(ForRows, rowTracks); + placeItemsOnGrid(); + + Vector<GridTrack> columnTracks(gridColumnCount()); + Vector<GridTrack> rowTracks(gridRowCount()); + computedUsedBreadthOfGridTracks(ForColumns, columnTracks, rowTracks); + ASSERT(tracksAreWiderThanMinTrackBreadth(ForColumns, columnTracks)); + computedUsedBreadthOfGridTracks(ForRows, columnTracks, rowTracks); + ASSERT(tracksAreWiderThanMinTrackBreadth(ForRows, rowTracks)); for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { LayoutPoint childPosition = findChildLogicalPosition(child, columnTracks, rowTracks); - size_t columnTrack = resolveGridPosition(child->style()->gridItemColumn()); - size_t rowTrack = resolveGridPosition(child->style()->gridItemRow()); + // 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(); - // FIXME: Properly support implicit rows and columns (bug 103573). - if (columnTrack < columnTracks.size() && rowTrack < rowTracks.size()) { - // Because the grid area cannot be styled, we don't need to adjust - // the grid breadth to account for 'box-sizing'. - child->setOverrideContainingBlockContentLogicalWidth(columnTracks[columnTrack].m_usedBreadth); - child->setOverrideContainingBlockContentLogicalHeight(rowTracks[rowTrack].m_usedBreadth); - } + // FIXME: For children in a content sized track, we clear the overrideContainingBlockContentLogicalHeight + // in minContentForChild / maxContentForChild which means that we will always relayout the child. + LayoutUnit overrideContainingBlockContentLogicalWidth = gridAreaBreadthForChild(child, ForColumns, columnTracks); + LayoutUnit overrideContainingBlockContentLogicalHeight = gridAreaBreadthForChild(child, ForRows, rowTracks); + if (oldOverrideContainingBlockContentLogicalWidth != overrideContainingBlockContentLogicalWidth || oldOverrideContainingBlockContentLogicalHeight != overrideContainingBlockContentLogicalHeight) + child->setNeedsLayout(true, MarkOnlyThis); + + child->setOverrideContainingBlockContentLogicalWidth(overrideContainingBlockContentLogicalWidth); + child->setOverrideContainingBlockContentLogicalHeight(overrideContainingBlockContentLogicalHeight); + + 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 @@ -180,43 +689,114 @@ void RenderGrid::layoutGridItems() // FIXME: Handle border & padding on the grid element. child->setLogicalLocation(childPosition); + + // If the child moved, we have to repaint it as well as any floating/positioned + // descendants. An exception is if we need a layout. In this case, we know we're going to + // repaint ourselves (and the child) anyway. + if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) + child->repaintDuringLayoutIfMoved(oldChildRect); } - // FIXME: Handle border & padding on the grid element. for (size_t i = 0; i < rowTracks.size(); ++i) setLogicalHeight(logicalHeight() + rowTracks[i].m_usedBreadth); + + // FIXME: We should handle min / max logical height. + + setLogicalHeight(logicalHeight() + borderAndPaddingLogicalHeight()); + clearGrid(); +} + +RenderGrid::GridCoordinate RenderGrid::cachedGridCoordinate(const RenderBox* gridItem) const +{ + ASSERT(m_gridItemCoordinate.contains(gridItem)); + return m_gridItemCoordinate.get(gridItem); +} + +RenderGrid::GridSpan RenderGrid::resolveGridPositionsFromAutoPlacementPosition(const RenderBox*, TrackSizingDirection, size_t initialPosition) 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); } -size_t RenderGrid::resolveGridPosition(const GridPosition& position) const +PassOwnPtr<RenderGrid::GridSpan> RenderGrid::resolveGridPositionsFromStyle(const RenderBox* gridItem, TrackSizingDirection direction) const { + ASSERT(gridWasPopulated()); + + const GridPosition& initialPosition = (direction == ForColumns) ? gridItem->style()->gridItemStart() : gridItem->style()->gridItemBefore(); + const GridPositionSide initialPositionSide = (direction == ForColumns) ? StartSide : BeforeSide; + const GridPosition& finalPosition = (direction == ForColumns) ? gridItem->style()->gridItemEnd() : gridItem->style()->gridItemAfter(); + const GridPositionSide finalPositionSide = (direction == ForColumns) ? EndSide : AfterSide; + + if (initialPosition.isAuto() && finalPosition.isAuto()) { + 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.isAuto()) { + // Infer the position from the final position ('auto / 1' case). + const size_t finalResolvedPosition = resolveGridPositionFromStyle(finalPosition, finalPositionSide); + return adoptPtr(new GridSpan(finalResolvedPosition, finalResolvedPosition)); + } + + if (finalPosition.isAuto()) { + // Infer our position from the initial position ('1 / auto' case). + const size_t initialResolvedPosition = resolveGridPositionFromStyle(initialPosition, initialPositionSide); + return adoptPtr(new GridSpan(initialResolvedPosition, initialResolvedPosition)); + } + + return adoptPtr(new GridSpan(resolveGridPositionFromStyle(initialPosition, initialPositionSide), resolveGridPositionFromStyle(finalPosition, finalPositionSide))); +} + +size_t RenderGrid::resolveGridPositionFromStyle(const GridPosition& position, GridPositionSide side) const +{ + ASSERT(gridWasPopulated()); + // FIXME: Handle other values for grid-{row,column} like ranges or line names. switch (position.type()) { - case IntegerPosition: + case IntegerPosition: { // FIXME: What does a non-positive integer mean for a column/row? - if (!position.isPositive()) - return 0; + size_t resolvedPosition = position.isPositive() ? position.integerPosition() - 1 : 0; + + if (side == StartSide || side == BeforeSide) + return resolvedPosition; - return position.integerPosition() - 1; + const size_t endOfTrack = (side == EndSide) ? gridColumnCount() - 1 : gridRowCount() - 1; + ASSERT(endOfTrack >= resolvedPosition); + return endOfTrack - resolvedPosition; + } case AutoPosition: - // FIXME: We should follow 'grid-auto-flow' for resolution. - // Until then, we use the 'grid-auto-flow: none' behavior (which is the default) - // and resolve 'auto' as the first row / column. + // 'auto' depends on the opposite position for resolution (e.g. grid-row: auto / 1). + ASSERT_NOT_REACHED(); return 0; } ASSERT_NOT_REACHED(); return 0; } +LayoutUnit RenderGrid::gridAreaBreadthForChild(const RenderBox* child, TrackSizingDirection direction, const Vector<GridTrack>& tracks) 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; +} + LayoutPoint RenderGrid::findChildLogicalPosition(RenderBox* child, const Vector<GridTrack>& columnTracks, const Vector<GridTrack>& rowTracks) { - size_t columnTrack = resolveGridPosition(child->style()->gridItemColumn()); - size_t rowTrack = resolveGridPosition(child->style()->gridItemRow()); + const GridCoordinate& coordinate = cachedGridCoordinate(child); - LayoutPoint offset; + // The grid items should be inside the grid container's border box, that's why they need to be shifted. + LayoutPoint offset(borderAndPaddingStart(), borderAndPaddingBefore()); // FIXME: |columnTrack| and |rowTrack| should be smaller than our column / row count. - for (size_t i = 0; i < columnTrack && i < columnTracks.size(); ++i) + for (size_t i = 0; i < coordinate.columns.initialPositionIndex && i < columnTracks.size(); ++i) offset.setX(offset.x() + columnTracks[i].m_usedBreadth); - for (size_t i = 0; i < rowTrack && i < rowTracks.size(); ++i) + for (size_t i = 0; i < coordinate.rows.initialPositionIndex && i < rowTracks.size(); ++i) offset.setY(offset.y() + rowTracks[i].m_usedBreadth); // FIXME: Handle margins on the grid item. |
