/* * Copyright (C) 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "RenderGrid.h" #include "LayoutRepainter.h" #include "NotImplemented.h" #include "RenderLayer.h" #include "RenderView.h" namespace WebCore { 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 > >& 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& children = m_grid[m_rowIndex][m_columnIndex]; if (m_childIndex < children.size()) return children[m_childIndex++]; m_childIndex = 0; } return 0; } PassOwnPtr 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& children = m_grid[m_rowIndex][m_columnIndex]; if (children.isEmpty()) { OwnPtr 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 > >& m_grid; TrackSizingDirection m_direction; size_t m_rowIndex; size_t m_columnIndex; size_t m_childIndex; }; RenderGrid::RenderGrid(Element* element) : RenderBlock(element) { // All of our children must be block level. setChildrenInline(false); } RenderGrid::~RenderGrid() { } void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit) { ASSERT(needsLayout()); if (!relayoutChildren && simplifiedLayout()) return; // FIXME: Much of this method is boiler plate that matches RenderBox::layoutBlock and Render*FlexibleBox::layoutBlock. // It would be nice to refactor some of the duplicate code. LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); // 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(); layoutGridItems(); LayoutUnit oldClientAfterEdge = clientLogicalBottom(); updateLogicalHeight(); if (size() != previousSize) relayoutChildren = true; layoutPositionedObjects(relayoutChildren || isRoot()); updateRegionsAndShapesAfterChildLayout(flowThread); computeOverflow(oldClientAfterEdge); statePusher.pop(); updateLayerTransform(); // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if // we overflow or not. updateScrollInfoAfterLayout(); repainter.repaintAfterLayout(); setNeedsLayout(false); } void RenderGrid::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const { const_cast(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(this)->clearGrid(); } void RenderGrid::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); m_minPreferredLogicalWidth = 0; m_maxPreferredLogicalWidth = 0; // 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. computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); 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; } 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: css3-sizing mentions that we should resolve "definite sizes" // (including and calc()) but we don't do it elsewhere. return 0; } void RenderGrid::computedUsedBreadthOfGridTracks(TrackSizingDirection direction, Vector& columnTracks, Vector& rowTracks) { LayoutUnit availableLogicalSpace = (direction == ForColumns) ? availableLogicalWidth() : availableLogicalHeight(IncludeMarginBorderPadding); Vector& 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); } // 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 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; } 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& 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& trackStyles = (direction == ForColumns) ? style()->gridColumns() : style()->gridRows(); size_t maximumIndex = std::max(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& 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& 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& 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& columnTracks, Vector& rowTracks, LayoutUnit& availableLogicalSpace) { // FIXME: Split the grid tracks once we support fractions (step 1 of the algorithm). Vector& 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& columnTracks, Vector& 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 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& tracks, Vector* tracksForGrowthAboveMaxBreadth, AccumulatorGetter trackGetter, AccumulatorGrowFunction trackGrowthFunction, LayoutUnit& availableLogicalSpace) { std::sort(tracks.begin(), tracks.end(), sortByGridTrackGrowthPotential); size_t tracksSize = tracks.size(); Vector 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& 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 autoMajorAxisAutoGridItems; Vector 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 / // positions to not match the author's intent. The specification is unclear on what should be done in this case. OwnPtr rowPositions = resolveGridPositionsFromStyle(child, ForRows); OwnPtr 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 autoGridItems) { for (size_t i = 0; i < autoGridItems.size(); ++i) { OwnPtr majorAxisPositions = resolveGridPositionsFromStyle(autoGridItems[i], autoPlacementMajorAxisDirection()); GridIterator iterator(m_grid, autoPlacementMajorAxisDirection(), majorAxisPositions->initialPositionIndex); if (OwnPtr emptyGridArea = iterator.nextEmptyGridArea()) { insertItemIntoGrid(autoGridItems[i], emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex); continue; } growGrid(autoPlacementMinorAxisDirection()); OwnPtr emptyGridArea = iterator.nextEmptyGridArea(); ASSERT(emptyGridArea); insertItemIntoGrid(autoGridItems[i], emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex); } } void RenderGrid::placeAutoMajorAxisItemsOnGrid(Vector autoGridItems) { for (size_t i = 0; i < autoGridItems.size(); ++i) placeAutoMajorAxisItemOnGrid(autoGridItems[i]); } void RenderGrid::placeAutoMajorAxisItemOnGrid(RenderBox* gridItem) { OwnPtr 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 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 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() { placeItemsOnGrid(); Vector columnTracks(gridColumnCount()); Vector 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); // 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: 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 // now, just size as if we were a regular child. child->layoutIfNeeded(); // 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); } 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); } PassOwnPtr 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: { // FIXME: What does a non-positive integer mean for a column/row? size_t resolvedPosition = position.isPositive() ? position.integerPosition() - 1 : 0; if (side == StartSide || side == BeforeSide) return resolvedPosition; const size_t endOfTrack = (side == EndSide) ? gridColumnCount() - 1 : gridRowCount() - 1; ASSERT(endOfTrack >= resolvedPosition); return endOfTrack - resolvedPosition; } case AutoPosition: // '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& 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& columnTracks, const Vector& rowTracks) { const GridCoordinate& coordinate = cachedGridCoordinate(child); // 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 < coordinate.columns.initialPositionIndex && i < columnTracks.size(); ++i) offset.setX(offset.x() + columnTracks[i].m_usedBreadth); 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. return offset; } const char* RenderGrid::renderName() const { if (isFloating()) return "RenderGrid (floating)"; if (isOutOfFlowPositioned()) return "RenderGrid (positioned)"; if (isAnonymous()) return "RenderGrid (generated)"; if (isRelPositioned()) return "RenderGrid (relative positioned)"; return "RenderGrid"; } } // namespace WebCore