1 /*
2 * Copyright (C) 2011 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "core/rendering/RenderGrid.h"
28
29 #include "core/rendering/LayoutRepainter.h"
30 #include "core/rendering/RenderLayer.h"
31 #include "core/rendering/RenderView.h"
32 #include "core/rendering/style/GridCoordinate.h"
33
34 namespace WebCore {
35
36 static const int infinity = -1;
37
38 class GridTrack {
39 public:
GridTrack()40 GridTrack()
41 : m_usedBreadth(0)
42 , m_maxBreadth(0)
43 {
44 }
45
growUsedBreadth(LayoutUnit growth)46 void growUsedBreadth(LayoutUnit growth)
47 {
48 ASSERT(growth >= 0);
49 m_usedBreadth += growth;
50 }
usedBreadth() const51 LayoutUnit usedBreadth() const { return m_usedBreadth; }
52
growMaxBreadth(LayoutUnit growth)53 void growMaxBreadth(LayoutUnit growth)
54 {
55 if (m_maxBreadth == infinity)
56 m_maxBreadth = m_usedBreadth + growth;
57 else
58 m_maxBreadth += growth;
59 }
maxBreadthIfNotInfinite() const60 LayoutUnit maxBreadthIfNotInfinite() const
61 {
62 return (m_maxBreadth == infinity) ? m_usedBreadth : m_maxBreadth;
63 }
64
65 LayoutUnit m_usedBreadth;
66 LayoutUnit m_maxBreadth;
67 };
68
69 struct GridTrackForNormalization {
GridTrackForNormalizationWebCore::GridTrackForNormalization70 GridTrackForNormalization(const GridTrack& track, double flex)
71 : m_track(&track)
72 , m_flex(flex)
73 , m_normalizedFlexValue(track.m_usedBreadth / flex)
74 {
75 }
76
77 // Required by std::sort.
operator =WebCore::GridTrackForNormalization78 GridTrackForNormalization operator=(const GridTrackForNormalization& o)
79 {
80 m_track = o.m_track;
81 m_flex = o.m_flex;
82 m_normalizedFlexValue = o.m_normalizedFlexValue;
83 return *this;
84 }
85
86 const GridTrack* m_track;
87 double m_flex;
88 LayoutUnit m_normalizedFlexValue;
89 };
90
91 class RenderGrid::GridIterator {
92 WTF_MAKE_NONCOPYABLE(GridIterator);
93 public:
94 // |direction| is the direction that is fixed to |fixedTrackIndex| so e.g
95 // GridIterator(m_grid, ForColumns, 1) will walk over the rows of the 2nd column.
GridIterator(const GridRepresentation & grid,GridTrackSizingDirection direction,size_t fixedTrackIndex)96 GridIterator(const GridRepresentation& grid, GridTrackSizingDirection direction, size_t fixedTrackIndex)
97 : m_grid(grid)
98 , m_direction(direction)
99 , m_rowIndex((direction == ForColumns) ? 0 : fixedTrackIndex)
100 , m_columnIndex((direction == ForColumns) ? fixedTrackIndex : 0)
101 , m_childIndex(0)
102 {
103 ASSERT(m_rowIndex < m_grid.size());
104 ASSERT(m_columnIndex < m_grid[0].size());
105 }
106
nextGridItem()107 RenderBox* nextGridItem()
108 {
109 ASSERT(!m_grid.isEmpty());
110
111 size_t& varyingTrackIndex = (m_direction == ForColumns) ? m_rowIndex : m_columnIndex;
112 const size_t endOfVaryingTrackIndex = (m_direction == ForColumns) ? m_grid.size() : m_grid[0].size();
113 for (; varyingTrackIndex < endOfVaryingTrackIndex; ++varyingTrackIndex) {
114 const GridCell& children = m_grid[m_rowIndex][m_columnIndex];
115 if (m_childIndex < children.size())
116 return children[m_childIndex++];
117
118 m_childIndex = 0;
119 }
120 return 0;
121 }
122
nextEmptyGridArea()123 PassOwnPtr<GridCoordinate> nextEmptyGridArea()
124 {
125 ASSERT(!m_grid.isEmpty());
126
127 size_t& varyingTrackIndex = (m_direction == ForColumns) ? m_rowIndex : m_columnIndex;
128 const size_t endOfVaryingTrackIndex = (m_direction == ForColumns) ? m_grid.size() : m_grid[0].size();
129 for (; varyingTrackIndex < endOfVaryingTrackIndex; ++varyingTrackIndex) {
130 const GridCell& children = m_grid[m_rowIndex][m_columnIndex];
131 if (children.isEmpty()) {
132 OwnPtr<GridCoordinate> result = adoptPtr(new GridCoordinate(GridSpan(m_rowIndex, m_rowIndex), GridSpan(m_columnIndex, m_columnIndex)));
133 // Advance the iterator to avoid an infinite loop where we would return the same grid area over and over.
134 ++varyingTrackIndex;
135 return result.release();
136 }
137 }
138 return nullptr;
139 }
140
141 private:
142 const GridRepresentation& m_grid;
143 GridTrackSizingDirection m_direction;
144 size_t m_rowIndex;
145 size_t m_columnIndex;
146 size_t m_childIndex;
147 };
148
149 struct RenderGrid::GridSizingData {
150 WTF_MAKE_NONCOPYABLE(GridSizingData);
151 public:
GridSizingDataWebCore::RenderGrid::GridSizingData152 GridSizingData(size_t gridColumnCount, size_t gridRowCount)
153 : columnTracks(gridColumnCount)
154 , rowTracks(gridRowCount)
155 {
156 }
157
158 Vector<GridTrack> columnTracks;
159 Vector<GridTrack> rowTracks;
160 Vector<size_t> contentSizedTracksIndex;
161
162 // Performance optimization: hold onto these Vectors until the end of Layout to avoid repeated malloc / free.
163 Vector<LayoutUnit> distributeTrackVector;
164 Vector<GridTrack*> filteredTracks;
165 };
166
RenderGrid(Element * element)167 RenderGrid::RenderGrid(Element* element)
168 : RenderBlock(element)
169 , m_gridIsDirty(true)
170 , m_orderIterator(this)
171 , m_gridItemOverflowGridArea(false)
172 {
173 // All of our children must be block level.
174 setChildrenInline(false);
175 }
176
~RenderGrid()177 RenderGrid::~RenderGrid()
178 {
179 }
180
addChild(RenderObject * newChild,RenderObject * beforeChild)181 void RenderGrid::addChild(RenderObject* newChild, RenderObject* beforeChild)
182 {
183 RenderBlock::addChild(newChild, beforeChild);
184
185 if (gridIsDirty())
186 return;
187
188 if (!newChild->isBox()) {
189 dirtyGrid();
190 return;
191 }
192
193 RenderBox* newChildBox = toRenderBox(newChild);
194 OwnPtr<GridSpan> rowPositions = resolveGridPositionsFromStyle(newChildBox, ForRows);
195 OwnPtr<GridSpan> columnPositions = resolveGridPositionsFromStyle(newChildBox, ForColumns);
196 if (!rowPositions || !columnPositions) {
197 // The new child requires the auto-placement algorithm to run so we need to recompute the grid fully.
198 dirtyGrid();
199 } else {
200 if (gridRowCount() <= rowPositions->finalPositionIndex || gridColumnCount() <= columnPositions->finalPositionIndex) {
201 // FIXME: We could just insert the new child provided we had a primitive to arbitrarily grow the grid.
202 dirtyGrid();
203 } else {
204 insertItemIntoGrid(newChildBox, GridCoordinate(*rowPositions, *columnPositions));
205 }
206 }
207 }
208
removeChild(RenderObject * child)209 void RenderGrid::removeChild(RenderObject* child)
210 {
211 RenderBlock::removeChild(child);
212
213 if (gridIsDirty())
214 return;
215
216 ASSERT(child->isBox());
217 // FIXME: We could avoid dirtying the grid in some cases (e.g. if it's an explicitly positioned element).
218 dirtyGrid();
219 }
220
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)221 void RenderGrid::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
222 {
223 RenderBlock::styleDidChange(diff, oldStyle);
224 if (!oldStyle)
225 return;
226
227 // FIXME: The following checks could be narrowed down if we kept track of which type of grid items we have:
228 // - explicit grid size changes impact negative explicitely positioned and auto-placed grid items.
229 // - named grid lines only impact grid items with named grid lines.
230 // - auto-flow changes only impacts auto-placed children.
231
232 if (explicitGridDidResize(oldStyle)
233 || namedGridLinesDefinitionDidChange(oldStyle)
234 || oldStyle->gridAutoFlow() != style()->gridAutoFlow())
235 dirtyGrid();
236 }
237
explicitGridDidResize(const RenderStyle * oldStyle) const238 bool RenderGrid::explicitGridDidResize(const RenderStyle* oldStyle) const
239 {
240 return oldStyle->gridDefinitionColumns().size() != style()->gridDefinitionColumns().size()
241 || oldStyle->gridDefinitionRows().size() != style()->gridDefinitionRows().size();
242 }
243
namedGridLinesDefinitionDidChange(const RenderStyle * oldStyle) const244 bool RenderGrid::namedGridLinesDefinitionDidChange(const RenderStyle* oldStyle) const
245 {
246 return oldStyle->namedGridRowLines() != style()->namedGridRowLines()
247 || oldStyle->namedGridColumnLines() != style()->namedGridColumnLines();
248 }
249
layoutBlock(bool relayoutChildren,LayoutUnit)250 void RenderGrid::layoutBlock(bool relayoutChildren, LayoutUnit)
251 {
252 ASSERT(needsLayout());
253
254 if (!relayoutChildren && simplifiedLayout())
255 return;
256
257 // FIXME: Much of this method is boiler plate that matches RenderBox::layoutBlock and Render*FlexibleBox::layoutBlock.
258 // It would be nice to refactor some of the duplicate code.
259 LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
260 LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode());
261
262 // Regions changing widths can force us to relayout our children.
263 RenderFlowThread* flowThread = flowThreadContainingBlock();
264 if (logicalWidthChangedInRegions(flowThread))
265 relayoutChildren = true;
266 if (updateRegionsAndShapesLogicalSize(flowThread))
267 relayoutChildren = true;
268
269 LayoutSize previousSize = size();
270
271 setLogicalHeight(0);
272 updateLogicalWidth();
273
274 layoutGridItems();
275
276 LayoutUnit oldClientAfterEdge = clientLogicalBottom();
277 updateLogicalHeight();
278
279 if (size() != previousSize)
280 relayoutChildren = true;
281
282 layoutPositionedObjects(relayoutChildren || isRoot());
283
284 computeRegionRangeForBlock(flowThread);
285
286 computeOverflow(oldClientAfterEdge);
287 statePusher.pop();
288
289 updateLayerTransform();
290
291 // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
292 // we overflow or not.
293 if (hasOverflowClip())
294 layer()->scrollableArea()->updateAfterLayout();
295
296 repainter.repaintAfterLayout();
297
298 clearNeedsLayout();
299 }
300
computeIntrinsicLogicalWidths(LayoutUnit & minLogicalWidth,LayoutUnit & maxLogicalWidth) const301 void RenderGrid::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
302 {
303 const_cast<RenderGrid*>(this)->placeItemsOnGrid();
304
305 GridSizingData sizingData(gridColumnCount(), gridRowCount());
306 LayoutUnit availableLogicalSpace = 0;
307 const_cast<RenderGrid*>(this)->computedUsedBreadthOfGridTracks(ForColumns, sizingData, availableLogicalSpace);
308
309 for (size_t i = 0; i < sizingData.columnTracks.size(); ++i) {
310 LayoutUnit minTrackBreadth = sizingData.columnTracks[i].m_usedBreadth;
311 LayoutUnit maxTrackBreadth = sizingData.columnTracks[i].m_maxBreadth;
312 maxTrackBreadth = std::max(maxTrackBreadth, minTrackBreadth);
313
314 minLogicalWidth += minTrackBreadth;
315 maxLogicalWidth += maxTrackBreadth;
316
317 // FIXME: This should add in the scrollbarWidth (e.g. see RenderFlexibleBox).
318 }
319 }
320
computePreferredLogicalWidths()321 void RenderGrid::computePreferredLogicalWidths()
322 {
323 ASSERT(preferredLogicalWidthsDirty());
324
325 m_minPreferredLogicalWidth = 0;
326 m_maxPreferredLogicalWidth = 0;
327
328 // FIXME: We don't take our own logical width into account. Once we do, we need to make sure
329 // we apply (and test the interaction with) min-width / max-width.
330
331 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
332
333 LayoutUnit borderAndPaddingInInlineDirection = borderAndPaddingLogicalWidth();
334 m_minPreferredLogicalWidth += borderAndPaddingInInlineDirection;
335 m_maxPreferredLogicalWidth += borderAndPaddingInInlineDirection;
336
337 clearPreferredLogicalWidthsDirty();
338 }
339
computedUsedBreadthOfGridTracks(GridTrackSizingDirection direction,GridSizingData & sizingData)340 void RenderGrid::computedUsedBreadthOfGridTracks(GridTrackSizingDirection direction, GridSizingData& sizingData)
341 {
342 LayoutUnit availableLogicalSpace = (direction == ForColumns) ? availableLogicalWidth() : availableLogicalHeight(IncludeMarginBorderPadding);
343 computedUsedBreadthOfGridTracks(direction, sizingData, availableLogicalSpace);
344 }
345
computedUsedBreadthOfGridTracks(GridTrackSizingDirection direction,GridSizingData & sizingData,LayoutUnit & availableLogicalSpace)346 void RenderGrid::computedUsedBreadthOfGridTracks(GridTrackSizingDirection direction, GridSizingData& sizingData, LayoutUnit& availableLogicalSpace)
347 {
348 Vector<GridTrack>& tracks = (direction == ForColumns) ? sizingData.columnTracks : sizingData.rowTracks;
349 sizingData.contentSizedTracksIndex.shrink(0);
350 for (size_t i = 0; i < tracks.size(); ++i) {
351 GridTrack& track = tracks[i];
352 const GridTrackSize& trackSize = gridTrackSize(direction, i);
353 const GridLength& minTrackBreadth = trackSize.minTrackBreadth();
354 const GridLength& maxTrackBreadth = trackSize.maxTrackBreadth();
355
356 track.m_usedBreadth = computeUsedBreadthOfMinLength(direction, minTrackBreadth);
357 track.m_maxBreadth = computeUsedBreadthOfMaxLength(direction, maxTrackBreadth, track.m_usedBreadth);
358
359 track.m_maxBreadth = std::max(track.m_maxBreadth, track.m_usedBreadth);
360
361 if (trackSize.isContentSized())
362 sizingData.contentSizedTracksIndex.append(i);
363 }
364
365 if (!sizingData.contentSizedTracksIndex.isEmpty())
366 resolveContentBasedTrackSizingFunctions(direction, sizingData, availableLogicalSpace);
367
368 for (size_t i = 0; i < tracks.size(); ++i) {
369 ASSERT(tracks[i].m_maxBreadth != infinity);
370 availableLogicalSpace -= tracks[i].m_usedBreadth;
371 }
372
373 if (availableLogicalSpace <= 0)
374 return;
375
376 const size_t tracksSize = tracks.size();
377 Vector<GridTrack*> tracksForDistribution(tracksSize);
378 for (size_t i = 0; i < tracksSize; ++i)
379 tracksForDistribution[i] = tracks.data() + i;
380
381 distributeSpaceToTracks(tracksForDistribution, 0, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth, sizingData, availableLogicalSpace);
382
383 // 4. Grow all Grid tracks having a fraction as the MaxTrackSizingFunction.
384
385 // FIXME: Handle the case where RemainingSpace is not defined.
386 double normalizedFractionBreadth = computeNormalizedFractionBreadth(tracks, direction, availableLogicalSpace);
387 for (size_t i = 0; i < tracksSize; ++i) {
388 const GridTrackSize& trackSize = gridTrackSize(direction, i);
389 if (!trackSize.maxTrackBreadth().isFlex())
390 continue;
391
392 tracks[i].m_usedBreadth = std::max<LayoutUnit>(tracks[i].m_usedBreadth, normalizedFractionBreadth * trackSize.maxTrackBreadth().flex());
393 }
394 }
395
computeUsedBreadthOfMinLength(GridTrackSizingDirection direction,const GridLength & gridLength) const396 LayoutUnit RenderGrid::computeUsedBreadthOfMinLength(GridTrackSizingDirection direction, const GridLength& gridLength) const
397 {
398 if (gridLength.isFlex())
399 return 0;
400
401 const Length& trackLength = gridLength.length();
402 ASSERT(!trackLength.isAuto());
403 if (trackLength.isSpecified())
404 return computeUsedBreadthOfSpecifiedLength(direction, trackLength);
405
406 ASSERT(trackLength.isMinContent() || trackLength.isMaxContent());
407 return 0;
408 }
409
computeUsedBreadthOfMaxLength(GridTrackSizingDirection direction,const GridLength & gridLength,LayoutUnit usedBreadth) const410 LayoutUnit RenderGrid::computeUsedBreadthOfMaxLength(GridTrackSizingDirection direction, const GridLength& gridLength, LayoutUnit usedBreadth) const
411 {
412 if (gridLength.isFlex())
413 return usedBreadth;
414
415 const Length& trackLength = gridLength.length();
416 ASSERT(!trackLength.isAuto());
417 if (trackLength.isSpecified()) {
418 LayoutUnit computedBreadth = computeUsedBreadthOfSpecifiedLength(direction, trackLength);
419 ASSERT(computedBreadth != infinity);
420 return computedBreadth;
421 }
422
423 ASSERT(trackLength.isMinContent() || trackLength.isMaxContent());
424 return infinity;
425 }
426
computeUsedBreadthOfSpecifiedLength(GridTrackSizingDirection direction,const Length & trackLength) const427 LayoutUnit RenderGrid::computeUsedBreadthOfSpecifiedLength(GridTrackSizingDirection direction, const Length& trackLength) const
428 {
429 ASSERT(trackLength.isSpecified());
430 // FIXME: The -1 here should be replaced by whatever the intrinsic height of the grid is.
431 return valueForLength(trackLength, direction == ForColumns ? logicalWidth() : computeContentLogicalHeight(style()->logicalHeight(), -1), view());
432 }
433
sortByGridNormalizedFlexValue(const GridTrackForNormalization & track1,const GridTrackForNormalization & track2)434 static bool sortByGridNormalizedFlexValue(const GridTrackForNormalization& track1, const GridTrackForNormalization& track2)
435 {
436 return track1.m_normalizedFlexValue < track2.m_normalizedFlexValue;
437 }
438
computeNormalizedFractionBreadth(Vector<GridTrack> & tracks,GridTrackSizingDirection direction,LayoutUnit availableLogicalSpace) const439 double RenderGrid::computeNormalizedFractionBreadth(Vector<GridTrack>& tracks, GridTrackSizingDirection direction, LayoutUnit availableLogicalSpace) const
440 {
441 // |availableLogicalSpace| already accounts for the used breadths so no need to remove it here.
442
443 Vector<GridTrackForNormalization> tracksForNormalization;
444 for (size_t i = 0; i < tracks.size(); ++i) {
445 const GridTrackSize& trackSize = gridTrackSize(direction, i);
446 if (!trackSize.maxTrackBreadth().isFlex())
447 continue;
448
449 tracksForNormalization.append(GridTrackForNormalization(tracks[i], trackSize.maxTrackBreadth().flex()));
450 }
451
452 // FIXME: Ideally we shouldn't come here without any <flex> grid track.
453 if (tracksForNormalization.isEmpty())
454 return LayoutUnit();
455
456 std::sort(tracksForNormalization.begin(), tracksForNormalization.end(), sortByGridNormalizedFlexValue);
457
458 // These values work together: as we walk over our grid tracks, we increase fractionValueBasedOnGridItemsRatio
459 // to match a grid track's usedBreadth to <flex> ratio until the total fractions sized grid tracks wouldn't
460 // fit into availableLogicalSpaceIgnoringFractionTracks.
461 double accumulatedFractions = 0;
462 LayoutUnit fractionValueBasedOnGridItemsRatio = 0;
463 LayoutUnit availableLogicalSpaceIgnoringFractionTracks = availableLogicalSpace;
464
465 for (size_t i = 0; i < tracksForNormalization.size(); ++i) {
466 const GridTrackForNormalization& track = tracksForNormalization[i];
467 if (track.m_normalizedFlexValue > fractionValueBasedOnGridItemsRatio) {
468 // If the normalized flex value (we ordered |tracksForNormalization| by increasing normalized flex value)
469 // will make us overflow our container, then stop. We have the previous step's ratio is the best fit.
470 if (track.m_normalizedFlexValue * accumulatedFractions > availableLogicalSpaceIgnoringFractionTracks)
471 break;
472
473 fractionValueBasedOnGridItemsRatio = track.m_normalizedFlexValue;
474 }
475
476 accumulatedFractions += track.m_flex;
477 // This item was processed so we re-add its used breadth to the available space to accurately count the remaining space.
478 availableLogicalSpaceIgnoringFractionTracks += track.m_track->m_usedBreadth;
479 }
480
481 return availableLogicalSpaceIgnoringFractionTracks / accumulatedFractions;
482 }
483
gridTrackSize(GridTrackSizingDirection direction,size_t i) const484 const GridTrackSize& RenderGrid::gridTrackSize(GridTrackSizingDirection direction, size_t i) const
485 {
486 const Vector<GridTrackSize>& trackStyles = (direction == ForColumns) ? style()->gridDefinitionColumns() : style()->gridDefinitionRows();
487 if (i >= trackStyles.size())
488 return (direction == ForColumns) ? style()->gridAutoColumns() : style()->gridAutoRows();
489
490 return trackStyles[i];
491 }
492
explicitGridColumnCount() const493 size_t RenderGrid::explicitGridColumnCount() const
494 {
495 return style()->gridDefinitionColumns().size();
496 }
497
explicitGridRowCount() const498 size_t RenderGrid::explicitGridRowCount() const
499 {
500 return style()->gridDefinitionRows().size();
501 }
502
explicitGridSizeForSide(GridPositionSide side) const503 size_t RenderGrid::explicitGridSizeForSide(GridPositionSide side) const
504 {
505 return (side == ColumnStartSide || side == ColumnEndSide) ? explicitGridColumnCount() : explicitGridRowCount();
506 }
507
logicalContentHeightForChild(RenderBox * child,Vector<GridTrack> & columnTracks)508 LayoutUnit RenderGrid::logicalContentHeightForChild(RenderBox* child, Vector<GridTrack>& columnTracks)
509 {
510 SubtreeLayoutScope layoutScope(child);
511 if (child->style()->logicalHeight().isPercent())
512 layoutScope.setNeedsLayout(child);
513
514 child->setOverrideContainingBlockContentLogicalWidth(gridAreaBreadthForChild(child, ForColumns, columnTracks));
515 // If |child| has a percentage logical height, we shouldn't let it override its intrinsic height, which is
516 // what we are interested in here. Thus we need to set the override logical height to -1 (no possible resolution).
517 child->setOverrideContainingBlockContentLogicalHeight(-1);
518 child->layoutIfNeeded();
519 return child->logicalHeight();
520 }
521
minContentForChild(RenderBox * child,GridTrackSizingDirection direction,Vector<GridTrack> & columnTracks)522 LayoutUnit RenderGrid::minContentForChild(RenderBox* child, GridTrackSizingDirection direction, Vector<GridTrack>& columnTracks)
523 {
524 bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode();
525 // FIXME: Properly support orthogonal writing mode.
526 if (hasOrthogonalWritingMode)
527 return 0;
528
529 if (direction == ForColumns) {
530 // FIXME: It's unclear if we should return the intrinsic width or the preferred width.
531 // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html
532 return child->minPreferredLogicalWidth() + marginIntrinsicLogicalWidthForChild(child);
533 }
534
535 return logicalContentHeightForChild(child, columnTracks);
536 }
537
maxContentForChild(RenderBox * child,GridTrackSizingDirection direction,Vector<GridTrack> & columnTracks)538 LayoutUnit RenderGrid::maxContentForChild(RenderBox* child, GridTrackSizingDirection direction, Vector<GridTrack>& columnTracks)
539 {
540 bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode();
541 // FIXME: Properly support orthogonal writing mode.
542 if (hasOrthogonalWritingMode)
543 return LayoutUnit();
544
545 if (direction == ForColumns) {
546 // FIXME: It's unclear if we should return the intrinsic width or the preferred width.
547 // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html
548 return child->maxPreferredLogicalWidth() + marginIntrinsicLogicalWidthForChild(child);
549 }
550
551 return logicalContentHeightForChild(child, columnTracks);
552 }
553
resolveContentBasedTrackSizingFunctions(GridTrackSizingDirection direction,GridSizingData & sizingData,LayoutUnit & availableLogicalSpace)554 void RenderGrid::resolveContentBasedTrackSizingFunctions(GridTrackSizingDirection direction, GridSizingData& sizingData, LayoutUnit& availableLogicalSpace)
555 {
556 // FIXME: Split the grid tracks into groups that doesn't overlap a <flex> grid track (crbug.com/235258).
557
558 // FIXME: Per step 2 of the specification, we should order the grid items by increasing span.
559
560 for (size_t i = 0; i < sizingData.contentSizedTracksIndex.size(); ++i) {
561 GridIterator iterator(m_grid, direction, sizingData.contentSizedTracksIndex[i]);
562 while (RenderBox* gridItem = iterator.nextGridItem()) {
563 resolveContentBasedTrackSizingFunctionsForItems(direction, sizingData, gridItem, &GridTrackSize::hasMinOrMaxContentMinTrackBreadth, &RenderGrid::minContentForChild, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth);
564 resolveContentBasedTrackSizingFunctionsForItems(direction, sizingData, gridItem, &GridTrackSize::hasMaxContentMinTrackBreadth, &RenderGrid::maxContentForChild, &GridTrack::usedBreadth, &GridTrack::growUsedBreadth);
565 resolveContentBasedTrackSizingFunctionsForItems(direction, sizingData, gridItem, &GridTrackSize::hasMinOrMaxContentMaxTrackBreadth, &RenderGrid::minContentForChild, &GridTrack::maxBreadthIfNotInfinite, &GridTrack::growMaxBreadth);
566 resolveContentBasedTrackSizingFunctionsForItems(direction, sizingData, gridItem, &GridTrackSize::hasMaxContentMaxTrackBreadth, &RenderGrid::maxContentForChild, &GridTrack::maxBreadthIfNotInfinite, &GridTrack::growMaxBreadth);
567 }
568
569 GridTrack& track = (direction == ForColumns) ? sizingData.columnTracks[i] : sizingData.rowTracks[i];
570 if (track.m_maxBreadth == infinity)
571 track.m_maxBreadth = track.m_usedBreadth;
572 }
573 }
574
resolveContentBasedTrackSizingFunctionsForItems(GridTrackSizingDirection direction,GridSizingData & sizingData,RenderBox * gridItem,FilterFunction filterFunction,SizingFunction sizingFunction,AccumulatorGetter trackGetter,AccumulatorGrowFunction trackGrowthFunction)575 void RenderGrid::resolveContentBasedTrackSizingFunctionsForItems(GridTrackSizingDirection direction, GridSizingData& sizingData, RenderBox* gridItem, FilterFunction filterFunction, SizingFunction sizingFunction, AccumulatorGetter trackGetter, AccumulatorGrowFunction trackGrowthFunction)
576 {
577 const GridCoordinate coordinate = cachedGridCoordinate(gridItem);
578 const size_t initialTrackIndex = (direction == ForColumns) ? coordinate.columns.initialPositionIndex : coordinate.rows.initialPositionIndex;
579 const size_t finalTrackIndex = (direction == ForColumns) ? coordinate.columns.finalPositionIndex : coordinate.rows.finalPositionIndex;
580
581 sizingData.filteredTracks.shrink(0);
582 for (size_t trackIndex = initialTrackIndex; trackIndex <= finalTrackIndex; ++trackIndex) {
583 const GridTrackSize& trackSize = gridTrackSize(direction, trackIndex);
584 if (!(trackSize.*filterFunction)())
585 continue;
586
587 GridTrack& track = (direction == ForColumns) ? sizingData.columnTracks[trackIndex] : sizingData.rowTracks[trackIndex];
588 sizingData.filteredTracks.append(&track);
589 }
590
591 if (sizingData.filteredTracks.isEmpty())
592 return;
593
594 LayoutUnit additionalBreadthSpace = (this->*sizingFunction)(gridItem, direction, sizingData.columnTracks);
595 for (size_t trackIndexForSpace = initialTrackIndex; trackIndexForSpace <= finalTrackIndex; ++trackIndexForSpace) {
596 GridTrack& track = (direction == ForColumns) ? sizingData.columnTracks[trackIndexForSpace] : sizingData.rowTracks[trackIndexForSpace];
597 additionalBreadthSpace -= (track.*trackGetter)();
598 }
599
600 // FIXME: We should pass different values for |tracksForGrowthAboveMaxBreadth|.
601 distributeSpaceToTracks(sizingData.filteredTracks, &sizingData.filteredTracks, trackGetter, trackGrowthFunction, sizingData, additionalBreadthSpace);
602 }
603
sortByGridTrackGrowthPotential(const GridTrack * track1,const GridTrack * track2)604 static bool sortByGridTrackGrowthPotential(const GridTrack* track1, const GridTrack* track2)
605 {
606 return (track1->m_maxBreadth - track1->m_usedBreadth) < (track2->m_maxBreadth - track2->m_usedBreadth);
607 }
608
distributeSpaceToTracks(Vector<GridTrack * > & tracks,Vector<GridTrack * > * tracksForGrowthAboveMaxBreadth,AccumulatorGetter trackGetter,AccumulatorGrowFunction trackGrowthFunction,GridSizingData & sizingData,LayoutUnit & availableLogicalSpace)609 void RenderGrid::distributeSpaceToTracks(Vector<GridTrack*>& tracks, Vector<GridTrack*>* tracksForGrowthAboveMaxBreadth, AccumulatorGetter trackGetter, AccumulatorGrowFunction trackGrowthFunction, GridSizingData& sizingData, LayoutUnit& availableLogicalSpace)
610 {
611 std::sort(tracks.begin(), tracks.end(), sortByGridTrackGrowthPotential);
612
613 size_t tracksSize = tracks.size();
614 sizingData.distributeTrackVector.resize(tracksSize);
615
616 for (size_t i = 0; i < tracksSize; ++i) {
617 GridTrack& track = *tracks[i];
618 LayoutUnit availableLogicalSpaceShare = availableLogicalSpace / (tracksSize - i);
619 LayoutUnit trackBreadth = (tracks[i]->*trackGetter)();
620 LayoutUnit growthShare = std::min(availableLogicalSpaceShare, track.m_maxBreadth - trackBreadth);
621 sizingData.distributeTrackVector[i] = trackBreadth;
622 // We should never shrink any grid track or else we can't guarantee we abide by our min-sizing function.
623 if (growthShare > 0) {
624 sizingData.distributeTrackVector[i] += growthShare;
625 availableLogicalSpace -= growthShare;
626 }
627 }
628
629 if (availableLogicalSpace > 0 && tracksForGrowthAboveMaxBreadth) {
630 tracksSize = tracksForGrowthAboveMaxBreadth->size();
631 for (size_t i = 0; i < tracksSize; ++i) {
632 LayoutUnit growthShare = availableLogicalSpace / (tracksSize - i);
633 sizingData.distributeTrackVector[i] += growthShare;
634 availableLogicalSpace -= growthShare;
635 }
636 }
637
638 for (size_t i = 0; i < tracksSize; ++i) {
639 LayoutUnit growth = sizingData.distributeTrackVector[i] - (tracks[i]->*trackGetter)();
640 if (growth >= 0)
641 (tracks[i]->*trackGrowthFunction)(growth);
642 }
643 }
644
645 #ifndef NDEBUG
tracksAreWiderThanMinTrackBreadth(GridTrackSizingDirection direction,const Vector<GridTrack> & tracks)646 bool RenderGrid::tracksAreWiderThanMinTrackBreadth(GridTrackSizingDirection direction, const Vector<GridTrack>& tracks)
647 {
648 for (size_t i = 0; i < tracks.size(); ++i) {
649 const GridTrackSize& trackSize = gridTrackSize(direction, i);
650 const GridLength& minTrackBreadth = trackSize.minTrackBreadth();
651 if (computeUsedBreadthOfMinLength(direction, minTrackBreadth) > tracks[i].m_usedBreadth)
652 return false;
653 }
654 return true;
655 }
656 #endif
657
growGrid(GridTrackSizingDirection direction)658 void RenderGrid::growGrid(GridTrackSizingDirection direction)
659 {
660 if (direction == ForColumns) {
661 const size_t oldColumnSize = m_grid[0].size();
662 for (size_t row = 0; row < m_grid.size(); ++row)
663 m_grid[row].grow(oldColumnSize + 1);
664 } else {
665 const size_t oldRowSize = m_grid.size();
666 m_grid.grow(oldRowSize + 1);
667 m_grid[oldRowSize].grow(m_grid[0].size());
668 }
669 }
670
insertItemIntoGrid(RenderBox * child,const GridCoordinate & coordinate)671 void RenderGrid::insertItemIntoGrid(RenderBox* child, const GridCoordinate& coordinate)
672 {
673 for (size_t row = coordinate.rows.initialPositionIndex; row <= coordinate.rows.finalPositionIndex; ++row) {
674 for (size_t column = coordinate.columns.initialPositionIndex; column <= coordinate.columns.finalPositionIndex; ++column)
675 m_grid[row][column].append(child);
676 }
677
678 m_gridItemCoordinate.set(child, coordinate);
679 }
680
insertItemIntoGrid(RenderBox * child,size_t rowTrack,size_t columnTrack)681 void RenderGrid::insertItemIntoGrid(RenderBox* child, size_t rowTrack, size_t columnTrack)
682 {
683 const GridSpan& rowSpan = resolveGridPositionsFromAutoPlacementPosition(child, ForRows, rowTrack);
684 const GridSpan& columnSpan = resolveGridPositionsFromAutoPlacementPosition(child, ForColumns, columnTrack);
685 insertItemIntoGrid(child, GridCoordinate(rowSpan, columnSpan));
686 }
687
placeItemsOnGrid()688 void RenderGrid::placeItemsOnGrid()
689 {
690 if (!gridIsDirty())
691 return;
692
693 ASSERT(m_gridItemCoordinate.isEmpty());
694
695 populateExplicitGridAndOrderIterator();
696
697 // We clear the dirty bit here as the grid sizes have been updated, this means
698 // that we can safely call gridRowCount() / gridColumnCount().
699 m_gridIsDirty = false;
700
701 Vector<RenderBox*> autoMajorAxisAutoGridItems;
702 Vector<RenderBox*> specifiedMajorAxisAutoGridItems;
703 GridAutoFlow autoFlow = style()->gridAutoFlow();
704 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
705 // FIXME: We never re-resolve positions if the grid is grown during auto-placement which may lead auto / <integer>
706 // positions to not match the author's intent. The specification is unclear on what should be done in this case.
707 OwnPtr<GridSpan> rowPositions = resolveGridPositionsFromStyle(child, ForRows);
708 OwnPtr<GridSpan> columnPositions = resolveGridPositionsFromStyle(child, ForColumns);
709 if (!rowPositions || !columnPositions) {
710 GridSpan* majorAxisPositions = (autoPlacementMajorAxisDirection() == ForColumns) ? columnPositions.get() : rowPositions.get();
711 if (!majorAxisPositions)
712 autoMajorAxisAutoGridItems.append(child);
713 else
714 specifiedMajorAxisAutoGridItems.append(child);
715 continue;
716 }
717 insertItemIntoGrid(child, GridCoordinate(*rowPositions, *columnPositions));
718 }
719
720 ASSERT(gridRowCount() >= style()->gridDefinitionRows().size());
721 ASSERT(gridColumnCount() >= style()->gridDefinitionColumns().size());
722
723 if (autoFlow == AutoFlowNone) {
724 // If we did collect some grid items, they won't be placed thus never laid out.
725 ASSERT(!autoMajorAxisAutoGridItems.size());
726 ASSERT(!specifiedMajorAxisAutoGridItems.size());
727 return;
728 }
729
730 placeSpecifiedMajorAxisItemsOnGrid(specifiedMajorAxisAutoGridItems);
731 placeAutoMajorAxisItemsOnGrid(autoMajorAxisAutoGridItems);
732
733 m_grid.shrinkToFit();
734 }
735
populateExplicitGridAndOrderIterator()736 void RenderGrid::populateExplicitGridAndOrderIterator()
737 {
738 OrderIteratorPopulator populator(m_orderIterator);
739
740 size_t maximumRowIndex = std::max<size_t>(1, explicitGridRowCount());
741 size_t maximumColumnIndex = std::max<size_t>(1, explicitGridColumnCount());
742
743 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
744 populator.collectChild(child);
745
746 // This function bypasses the cache (cachedGridCoordinate()) as it is used to build it.
747 OwnPtr<GridSpan> rowPositions = resolveGridPositionsFromStyle(child, ForRows);
748 OwnPtr<GridSpan> columnPositions = resolveGridPositionsFromStyle(child, ForColumns);
749
750 // |positions| is 0 if we need to run the auto-placement algorithm. Our estimation ignores
751 // this case as the auto-placement algorithm will grow the grid as needed.
752 if (rowPositions)
753 maximumRowIndex = std::max(maximumRowIndex, rowPositions->finalPositionIndex + 1);
754 if (columnPositions)
755 maximumColumnIndex = std::max(maximumColumnIndex, columnPositions->finalPositionIndex + 1);
756 }
757
758 m_grid.grow(maximumRowIndex);
759 for (size_t i = 0; i < m_grid.size(); ++i)
760 m_grid[i].grow(maximumColumnIndex);
761 }
762
placeSpecifiedMajorAxisItemsOnGrid(const Vector<RenderBox * > & autoGridItems)763 void RenderGrid::placeSpecifiedMajorAxisItemsOnGrid(const Vector<RenderBox*>& autoGridItems)
764 {
765 for (size_t i = 0; i < autoGridItems.size(); ++i) {
766 OwnPtr<GridSpan> majorAxisPositions = resolveGridPositionsFromStyle(autoGridItems[i], autoPlacementMajorAxisDirection());
767 GridIterator iterator(m_grid, autoPlacementMajorAxisDirection(), majorAxisPositions->initialPositionIndex);
768 if (OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea()) {
769 insertItemIntoGrid(autoGridItems[i], emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex);
770 continue;
771 }
772
773 growGrid(autoPlacementMinorAxisDirection());
774 OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea();
775 ASSERT(emptyGridArea);
776 insertItemIntoGrid(autoGridItems[i], emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex);
777 }
778 }
779
placeAutoMajorAxisItemsOnGrid(const Vector<RenderBox * > & autoGridItems)780 void RenderGrid::placeAutoMajorAxisItemsOnGrid(const Vector<RenderBox*>& autoGridItems)
781 {
782 for (size_t i = 0; i < autoGridItems.size(); ++i)
783 placeAutoMajorAxisItemOnGrid(autoGridItems[i]);
784 }
785
placeAutoMajorAxisItemOnGrid(RenderBox * gridItem)786 void RenderGrid::placeAutoMajorAxisItemOnGrid(RenderBox* gridItem)
787 {
788 OwnPtr<GridSpan> minorAxisPositions = resolveGridPositionsFromStyle(gridItem, autoPlacementMinorAxisDirection());
789 ASSERT(!resolveGridPositionsFromStyle(gridItem, autoPlacementMajorAxisDirection()));
790 size_t minorAxisIndex = 0;
791 if (minorAxisPositions) {
792 minorAxisIndex = minorAxisPositions->initialPositionIndex;
793 GridIterator iterator(m_grid, autoPlacementMinorAxisDirection(), minorAxisIndex);
794 if (OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea()) {
795 insertItemIntoGrid(gridItem, emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex);
796 return;
797 }
798 } else {
799 const size_t endOfMajorAxis = (autoPlacementMajorAxisDirection() == ForColumns) ? gridColumnCount() : gridRowCount();
800 for (size_t majorAxisIndex = 0; majorAxisIndex < endOfMajorAxis; ++majorAxisIndex) {
801 GridIterator iterator(m_grid, autoPlacementMajorAxisDirection(), majorAxisIndex);
802 if (OwnPtr<GridCoordinate> emptyGridArea = iterator.nextEmptyGridArea()) {
803 insertItemIntoGrid(gridItem, emptyGridArea->rows.initialPositionIndex, emptyGridArea->columns.initialPositionIndex);
804 return;
805 }
806 }
807 }
808
809 // We didn't find an empty grid area so we need to create an extra major axis line and insert our gridItem in it.
810 const size_t columnIndex = (autoPlacementMajorAxisDirection() == ForColumns) ? m_grid[0].size() : minorAxisIndex;
811 const size_t rowIndex = (autoPlacementMajorAxisDirection() == ForColumns) ? minorAxisIndex : m_grid.size();
812 growGrid(autoPlacementMajorAxisDirection());
813 insertItemIntoGrid(gridItem, rowIndex, columnIndex);
814 }
815
autoPlacementMajorAxisDirection() const816 GridTrackSizingDirection RenderGrid::autoPlacementMajorAxisDirection() const
817 {
818 GridAutoFlow flow = style()->gridAutoFlow();
819 ASSERT(flow != AutoFlowNone);
820 return (flow == AutoFlowColumn) ? ForColumns : ForRows;
821 }
822
autoPlacementMinorAxisDirection() const823 GridTrackSizingDirection RenderGrid::autoPlacementMinorAxisDirection() const
824 {
825 GridAutoFlow flow = style()->gridAutoFlow();
826 ASSERT(flow != AutoFlowNone);
827 return (flow == AutoFlowColumn) ? ForRows : ForColumns;
828 }
829
dirtyGrid()830 void RenderGrid::dirtyGrid()
831 {
832 m_grid.resize(0);
833 m_gridItemCoordinate.clear();
834 m_gridIsDirty = true;
835 }
836
layoutGridItems()837 void RenderGrid::layoutGridItems()
838 {
839 placeItemsOnGrid();
840
841 GridSizingData sizingData(gridColumnCount(), gridRowCount());
842 computedUsedBreadthOfGridTracks(ForColumns, sizingData);
843 ASSERT(tracksAreWiderThanMinTrackBreadth(ForColumns, sizingData.columnTracks));
844 computedUsedBreadthOfGridTracks(ForRows, sizingData);
845 ASSERT(tracksAreWiderThanMinTrackBreadth(ForRows, sizingData.rowTracks));
846
847 populateGridPositions(sizingData);
848
849 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
850 // Because the grid area cannot be styled, we don't need to adjust
851 // the grid breadth to account for 'box-sizing'.
852 LayoutUnit oldOverrideContainingBlockContentLogicalWidth = child->hasOverrideContainingBlockLogicalWidth() ? child->overrideContainingBlockContentLogicalWidth() : LayoutUnit();
853 LayoutUnit oldOverrideContainingBlockContentLogicalHeight = child->hasOverrideContainingBlockLogicalHeight() ? child->overrideContainingBlockContentLogicalHeight() : LayoutUnit();
854
855 LayoutUnit overrideContainingBlockContentLogicalWidth = gridAreaBreadthForChild(child, ForColumns, sizingData.columnTracks);
856 LayoutUnit overrideContainingBlockContentLogicalHeight = gridAreaBreadthForChild(child, ForRows, sizingData.rowTracks);
857
858 SubtreeLayoutScope layoutScope(child);
859 if (oldOverrideContainingBlockContentLogicalWidth != overrideContainingBlockContentLogicalWidth || (oldOverrideContainingBlockContentLogicalHeight != overrideContainingBlockContentLogicalHeight && child->hasRelativeLogicalHeight()))
860 layoutScope.setNeedsLayout(child);
861
862 child->setOverrideContainingBlockContentLogicalWidth(overrideContainingBlockContentLogicalWidth);
863 child->setOverrideContainingBlockContentLogicalHeight(overrideContainingBlockContentLogicalHeight);
864
865 LayoutRect oldChildRect = child->frameRect();
866
867 // FIXME: Grid items should stretch to fill their cells. Once we
868 // implement grid-{column,row}-align, we can also shrink to fit. For
869 // now, just size as if we were a regular child.
870 child->layoutIfNeeded();
871
872 child->setLogicalLocation(findChildLogicalPosition(child, sizingData));
873
874 // For correctness, we disable some painting optimizations if we have a child overflowing its grid area.
875 m_gridItemOverflowGridArea = child->logicalHeight() > overrideContainingBlockContentLogicalHeight
876 || child->logicalWidth() > overrideContainingBlockContentLogicalWidth;
877
878 // If the child moved, we have to repaint it as well as any floating/positioned
879 // descendants. An exception is if we need a layout. In this case, we know we're going to
880 // repaint ourselves (and the child) anyway.
881 if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
882 child->repaintDuringLayoutIfMoved(oldChildRect);
883 }
884
885 for (size_t i = 0; i < sizingData.rowTracks.size(); ++i)
886 setLogicalHeight(logicalHeight() + sizingData.rowTracks[i].m_usedBreadth);
887
888 // Min / max logical height is handled by the call to updateLogicalHeight in layoutBlock.
889
890 setLogicalHeight(logicalHeight() + borderAndPaddingLogicalHeight());
891 }
892
cachedGridCoordinate(const RenderBox * gridItem) const893 GridCoordinate RenderGrid::cachedGridCoordinate(const RenderBox* gridItem) const
894 {
895 ASSERT(m_gridItemCoordinate.contains(gridItem));
896 return m_gridItemCoordinate.get(gridItem);
897 }
898
resolveGridPositionsFromAutoPlacementPosition(const RenderBox *,GridTrackSizingDirection,size_t initialPosition) const899 GridSpan RenderGrid::resolveGridPositionsFromAutoPlacementPosition(const RenderBox*, GridTrackSizingDirection, size_t initialPosition) const
900 {
901 // FIXME: We don't support spanning with auto positions yet. Once we do, this is wrong. Also we should make
902 // sure the grid can accomodate the new item as we only grow 1 position in a given direction.
903 return GridSpan(initialPosition, initialPosition);
904 }
905
resolveGridPositionsFromStyle(const RenderBox * gridItem,GridTrackSizingDirection direction) const906 PassOwnPtr<GridSpan> RenderGrid::resolveGridPositionsFromStyle(const RenderBox* gridItem, GridTrackSizingDirection direction) const
907 {
908 const GridPosition& initialPosition = (direction == ForColumns) ? gridItem->style()->gridColumnStart() : gridItem->style()->gridRowStart();
909 const GridPositionSide initialPositionSide = (direction == ForColumns) ? ColumnStartSide : RowStartSide;
910 const GridPosition& finalPosition = (direction == ForColumns) ? gridItem->style()->gridColumnEnd() : gridItem->style()->gridRowEnd();
911 const GridPositionSide finalPositionSide = (direction == ForColumns) ? ColumnEndSide : RowEndSide;
912
913 // We should NEVER see both spans as they should have been handled during style resolve.
914 ASSERT(!initialPosition.isSpan() || !finalPosition.isSpan());
915
916 if (initialPosition.shouldBeResolvedAgainstOppositePosition() && finalPosition.shouldBeResolvedAgainstOppositePosition()) {
917 if (style()->gridAutoFlow() == AutoFlowNone)
918 return adoptPtr(new GridSpan(0, 0));
919
920 // We can't get our grid positions without running the auto placement algorithm.
921 return nullptr;
922 }
923
924 if (initialPosition.shouldBeResolvedAgainstOppositePosition()) {
925 // Infer the position from the final position ('auto / 1' or 'span 2 / 3' case).
926 const size_t finalResolvedPosition = resolveGridPositionFromStyle(finalPosition, finalPositionSide);
927 return resolveGridPositionAgainstOppositePosition(finalResolvedPosition, initialPosition, initialPositionSide);
928 }
929
930 if (finalPosition.shouldBeResolvedAgainstOppositePosition()) {
931 // Infer our position from the initial position ('1 / auto' or '3 / span 2' case).
932 const size_t initialResolvedPosition = resolveGridPositionFromStyle(initialPosition, initialPositionSide);
933 return resolveGridPositionAgainstOppositePosition(initialResolvedPosition, finalPosition, finalPositionSide);
934 }
935
936 size_t resolvedInitialPosition = resolveGridPositionFromStyle(initialPosition, initialPositionSide);
937 size_t resolvedFinalPosition = resolveGridPositionFromStyle(finalPosition, finalPositionSide);
938
939 // If 'grid-after' specifies a line at or before that specified by 'grid-before', it computes to 'span 1'.
940 if (resolvedFinalPosition < resolvedInitialPosition)
941 resolvedFinalPosition = resolvedInitialPosition;
942
943 return adoptPtr(new GridSpan(resolvedInitialPosition, resolvedFinalPosition));
944 }
945
resolveNamedGridLinePositionFromStyle(const GridPosition & position,GridPositionSide side) const946 size_t RenderGrid::resolveNamedGridLinePositionFromStyle(const GridPosition& position, GridPositionSide side) const
947 {
948 ASSERT(!position.namedGridLine().isNull());
949
950 const NamedGridLinesMap& gridLinesNames = (side == ColumnStartSide || side == ColumnEndSide) ? style()->namedGridColumnLines() : style()->namedGridRowLines();
951 NamedGridLinesMap::const_iterator it = gridLinesNames.find(position.namedGridLine());
952 if (it == gridLinesNames.end()) {
953 if (position.isPositive())
954 return 0;
955 const size_t lastLine = explicitGridSizeForSide(side);
956 return GridPosition::adjustGridPositionForSide(lastLine, side);
957 }
958
959 size_t namedGridLineIndex;
960 if (position.isPositive())
961 namedGridLineIndex = std::min<size_t>(position.integerPosition(), it->value.size()) - 1;
962 else
963 namedGridLineIndex = std::max<int>(it->value.size() - abs(position.integerPosition()), 0);
964 return GridPosition::adjustGridPositionForSide(it->value[namedGridLineIndex], side);
965 }
966
resolveGridPositionFromStyle(const GridPosition & position,GridPositionSide side) const967 size_t RenderGrid::resolveGridPositionFromStyle(const GridPosition& position, GridPositionSide side) const
968 {
969 switch (position.type()) {
970 case ExplicitPosition: {
971 ASSERT(position.integerPosition());
972
973 if (!position.namedGridLine().isNull())
974 return resolveNamedGridLinePositionFromStyle(position, side);
975
976 // Handle <integer> explicit position.
977 if (position.isPositive())
978 return GridPosition::adjustGridPositionForSide(position.integerPosition() - 1, side);
979
980 size_t resolvedPosition = abs(position.integerPosition()) - 1;
981 const size_t endOfTrack = explicitGridSizeForSide(side);
982
983 // Per http://lists.w3.org/Archives/Public/www-style/2013Mar/0589.html, we clamp negative value to the first line.
984 if (endOfTrack < resolvedPosition)
985 return 0;
986
987 return GridPosition::adjustGridPositionForSide(endOfTrack - resolvedPosition, side);
988 }
989 case NamedGridAreaPosition:
990 {
991 NamedGridAreaMap::const_iterator it = style()->namedGridArea().find(position.namedGridLine());
992 // Unknown grid area should have been computed to 'auto' by now.
993 ASSERT_WITH_SECURITY_IMPLICATION(it != style()->namedGridArea().end());
994 const GridCoordinate& gridAreaCoordinate = it->value;
995 switch (side) {
996 case ColumnStartSide:
997 return gridAreaCoordinate.columns.initialPositionIndex;
998 case ColumnEndSide:
999 return gridAreaCoordinate.columns.finalPositionIndex;
1000 case RowStartSide:
1001 return gridAreaCoordinate.rows.initialPositionIndex;
1002 case RowEndSide:
1003 return gridAreaCoordinate.rows.finalPositionIndex;
1004 }
1005 ASSERT_NOT_REACHED();
1006 return 0;
1007 }
1008 case AutoPosition:
1009 case SpanPosition:
1010 // 'auto' and span depend on the opposite position for resolution (e.g. grid-row: auto / 1 or grid-column: span 3 / "myHeader").
1011 ASSERT_NOT_REACHED();
1012 return 0;
1013 }
1014 ASSERT_NOT_REACHED();
1015 return 0;
1016 }
1017
resolveGridPositionAgainstOppositePosition(size_t resolvedOppositePosition,const GridPosition & position,GridPositionSide side) const1018 PassOwnPtr<GridSpan> RenderGrid::resolveGridPositionAgainstOppositePosition(size_t resolvedOppositePosition, const GridPosition& position, GridPositionSide side) const
1019 {
1020 if (position.isAuto())
1021 return GridSpan::create(resolvedOppositePosition, resolvedOppositePosition);
1022
1023 ASSERT(position.isSpan());
1024 ASSERT(position.spanPosition() > 0);
1025
1026 if (!position.namedGridLine().isNull()) {
1027 // span 2 'c' -> we need to find the appropriate grid line before / after our opposite position.
1028 return resolveNamedGridLinePositionAgainstOppositePosition(resolvedOppositePosition, position, side);
1029 }
1030
1031 return GridSpan::createWithSpanAgainstOpposite(resolvedOppositePosition, position, side);
1032 }
1033
resolveNamedGridLinePositionAgainstOppositePosition(size_t resolvedOppositePosition,const GridPosition & position,GridPositionSide side) const1034 PassOwnPtr<GridSpan> RenderGrid::resolveNamedGridLinePositionAgainstOppositePosition(size_t resolvedOppositePosition, const GridPosition& position, GridPositionSide side) const
1035 {
1036 ASSERT(position.isSpan());
1037 ASSERT(!position.namedGridLine().isNull());
1038 // Negative positions are not allowed per the specification and should have been handled during parsing.
1039 ASSERT(position.spanPosition() > 0);
1040
1041 const NamedGridLinesMap& gridLinesNames = (side == ColumnStartSide || side == ColumnEndSide) ? style()->namedGridColumnLines() : style()->namedGridRowLines();
1042 NamedGridLinesMap::const_iterator it = gridLinesNames.find(position.namedGridLine());
1043
1044 // 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).
1045 // See http://lists.w3.org/Archives/Public/www-style/2013Jun/0394.html.
1046 if (it == gridLinesNames.end())
1047 return GridSpan::create(resolvedOppositePosition, resolvedOppositePosition);
1048
1049 return GridSpan::createWithNamedSpanAgainstOpposite(resolvedOppositePosition, position, side, it->value);
1050 }
1051
gridAreaBreadthForChild(const RenderBox * child,GridTrackSizingDirection direction,const Vector<GridTrack> & tracks) const1052 LayoutUnit RenderGrid::gridAreaBreadthForChild(const RenderBox* child, GridTrackSizingDirection direction, const Vector<GridTrack>& tracks) const
1053 {
1054 const GridCoordinate& coordinate = cachedGridCoordinate(child);
1055 const GridSpan& span = (direction == ForColumns) ? coordinate.columns : coordinate.rows;
1056 LayoutUnit gridAreaBreadth = 0;
1057 for (size_t trackIndex = span.initialPositionIndex; trackIndex <= span.finalPositionIndex; ++trackIndex)
1058 gridAreaBreadth += tracks[trackIndex].m_usedBreadth;
1059 return gridAreaBreadth;
1060 }
1061
populateGridPositions(const GridSizingData & sizingData)1062 void RenderGrid::populateGridPositions(const GridSizingData& sizingData)
1063 {
1064 m_columnPositions.resize(sizingData.columnTracks.size() + 1);
1065 m_columnPositions[0] = borderAndPaddingStart();
1066 for (size_t i = 0; i < m_columnPositions.size() - 1; ++i)
1067 m_columnPositions[i + 1] = m_columnPositions[i] + sizingData.columnTracks[i].m_usedBreadth;
1068
1069 m_rowPositions.resize(sizingData.rowTracks.size() + 1);
1070 m_rowPositions[0] = borderAndPaddingBefore();
1071 for (size_t i = 0; i < m_rowPositions.size() - 1; ++i)
1072 m_rowPositions[i + 1] = m_rowPositions[i] + sizingData.rowTracks[i].m_usedBreadth;
1073 }
1074
findChildLogicalPosition(RenderBox * child,const GridSizingData & sizingData)1075 LayoutPoint RenderGrid::findChildLogicalPosition(RenderBox* child, const GridSizingData& sizingData)
1076 {
1077 const GridCoordinate& coordinate = cachedGridCoordinate(child);
1078 ASSERT(coordinate.columns.initialPositionIndex < sizingData.columnTracks.size());
1079 ASSERT(coordinate.rows.initialPositionIndex < sizingData.rowTracks.size());
1080
1081 // The grid items should be inside the grid container's border box, that's why they need to be shifted.
1082 return LayoutPoint(m_columnPositions[coordinate.columns.initialPositionIndex] + marginStartForChild(child), m_rowPositions[coordinate.rows.initialPositionIndex] + marginBeforeForChild(child));
1083 }
1084
dirtiedGridAreas(const Vector<LayoutUnit> & coordinates,LayoutUnit start,LayoutUnit end)1085 static GridSpan dirtiedGridAreas(const Vector<LayoutUnit>& coordinates, LayoutUnit start, LayoutUnit end)
1086 {
1087 // This function does a binary search over the coordinates.
1088 // FIXME: This doesn't work with grid items overflowing their grid areas and should be tested & fixed.
1089
1090 size_t startGridAreaIndex = std::upper_bound(coordinates.begin(), coordinates.end() - 1, start) - coordinates.begin();
1091 if (startGridAreaIndex > 0)
1092 --startGridAreaIndex;
1093
1094 size_t endGridAreaIndex = std::upper_bound(coordinates.begin() + startGridAreaIndex, coordinates.end() - 1, end) - coordinates.begin();
1095 return GridSpan(startGridAreaIndex, endGridAreaIndex);
1096 }
1097
paintChildrenSlowCase(PaintInfo & paintInfo,const LayoutPoint & paintOffset)1098 void RenderGrid::paintChildrenSlowCase(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1099 {
1100 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next())
1101 paintChild(child, paintInfo, paintOffset);
1102 }
1103
paintChildren(PaintInfo & paintInfo,const LayoutPoint & paintOffset)1104 void RenderGrid::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1105 {
1106 ASSERT_WITH_SECURITY_IMPLICATION(!gridIsDirty());
1107
1108 if (m_gridItemOverflowGridArea) {
1109 paintChildrenSlowCase(paintInfo, paintOffset);
1110 return;
1111 }
1112
1113 LayoutRect localRepaintRect = paintInfo.rect;
1114 localRepaintRect.moveBy(-paintOffset);
1115
1116 GridSpan dirtiedColumns = dirtiedGridAreas(m_columnPositions, localRepaintRect.x(), localRepaintRect.maxX());
1117 GridSpan dirtiedRows = dirtiedGridAreas(m_rowPositions, localRepaintRect.y(), localRepaintRect.maxY());
1118
1119 OrderIterator paintIterator(this);
1120 {
1121 OrderIteratorPopulator populator(paintIterator);
1122
1123 for (size_t row = dirtiedRows.initialPositionIndex; row < dirtiedRows.finalPositionIndex; ++row) {
1124 for (size_t column = dirtiedColumns.initialPositionIndex; column < dirtiedColumns.finalPositionIndex; ++column) {
1125 const Vector<RenderBox*, 1>& children = m_grid[row][column];
1126 // FIXME: If we start adding spanning children in all grid areas they span, this
1127 // would make us paint them several times, which is wrong!
1128 for (size_t j = 0; j < children.size(); ++j)
1129 populator.storeChild(children[j]);
1130 }
1131 }
1132 }
1133
1134 for (RenderBox* child = paintIterator.first(); child; child = paintIterator.next())
1135 paintChild(child, paintInfo, paintOffset);
1136 }
1137
renderName() const1138 const char* RenderGrid::renderName() const
1139 {
1140 if (isFloating())
1141 return "RenderGrid (floating)";
1142 if (isOutOfFlowPositioned())
1143 return "RenderGrid (positioned)";
1144 if (isAnonymous())
1145 return "RenderGrid (generated)";
1146 if (isRelPositioned())
1147 return "RenderGrid (relative positioned)";
1148 return "RenderGrid";
1149 }
1150
1151 } // namespace WebCore
1152