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