1/* 2 * Copyright (C) 2011 Google 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 are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include "config.h" 32#include "core/rendering/RenderFlexibleBox.h" 33 34#include "core/frame/UseCounter.h" 35#include "core/paint/BlockPainter.h" 36#include "core/rendering/RenderLayer.h" 37#include "core/rendering/RenderView.h" 38#include "core/rendering/TextAutosizer.h" 39#include "platform/LengthFunctions.h" 40#include "wtf/MathExtras.h" 41#include <limits> 42 43namespace blink { 44 45struct RenderFlexibleBox::LineContext { 46 LineContext(LayoutUnit crossAxisOffset, LayoutUnit crossAxisExtent, size_t numberOfChildren, LayoutUnit maxAscent) 47 : crossAxisOffset(crossAxisOffset) 48 , crossAxisExtent(crossAxisExtent) 49 , numberOfChildren(numberOfChildren) 50 , maxAscent(maxAscent) 51 { 52 } 53 54 LayoutUnit crossAxisOffset; 55 LayoutUnit crossAxisExtent; 56 size_t numberOfChildren; 57 LayoutUnit maxAscent; 58}; 59 60struct RenderFlexibleBox::Violation { 61 Violation(RenderBox* child, LayoutUnit childSize) 62 : child(child) 63 , childSize(childSize) 64 { 65 } 66 67 RenderBox* child; 68 LayoutUnit childSize; 69}; 70 71 72RenderFlexibleBox::RenderFlexibleBox(Element* element) 73 : RenderBlock(element) 74 , m_orderIterator(this) 75 , m_numberOfInFlowChildrenOnFirstLine(-1) 76{ 77 ASSERT(!childrenInline()); 78} 79 80RenderFlexibleBox::~RenderFlexibleBox() 81{ 82} 83 84RenderFlexibleBox* RenderFlexibleBox::createAnonymous(Document* document) 85{ 86 RenderFlexibleBox* renderer = new RenderFlexibleBox(0); 87 renderer->setDocumentForAnonymous(document); 88 return renderer; 89} 90 91const char* RenderFlexibleBox::renderName() const 92{ 93 return "RenderFlexibleBox"; 94} 95 96void RenderFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const 97{ 98 // FIXME: We're ignoring flex-basis here and we shouldn't. We can't start honoring it though until 99 // the flex shorthand stops setting it to 0. 100 // See https://bugs.webkit.org/show_bug.cgi?id=116117 and http://crbug.com/240765. 101 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { 102 if (child->isOutOfFlowPositioned()) 103 continue; 104 105 LayoutUnit margin = marginIntrinsicLogicalWidthForChild(child); 106 bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode(); 107 LayoutUnit minPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->minPreferredLogicalWidth(); 108 LayoutUnit maxPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->maxPreferredLogicalWidth(); 109 minPreferredLogicalWidth += margin; 110 maxPreferredLogicalWidth += margin; 111 if (!isColumnFlow()) { 112 maxLogicalWidth += maxPreferredLogicalWidth; 113 if (isMultiline()) { 114 // For multiline, the min preferred width is if you put a break between each item. 115 minLogicalWidth = std::max(minLogicalWidth, minPreferredLogicalWidth); 116 } else 117 minLogicalWidth += minPreferredLogicalWidth; 118 } else { 119 minLogicalWidth = std::max(minPreferredLogicalWidth, minLogicalWidth); 120 maxLogicalWidth = std::max(maxPreferredLogicalWidth, maxLogicalWidth); 121 } 122 } 123 124 maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); 125 126 LayoutUnit scrollbarWidth = instrinsicScrollbarLogicalWidth(); 127 maxLogicalWidth += scrollbarWidth; 128 minLogicalWidth += scrollbarWidth; 129} 130 131static int synthesizedBaselineFromContentBox(const RenderBox& box, LineDirectionMode direction) 132{ 133 return direction == HorizontalLine ? box.borderTop() + box.paddingTop() + box.contentHeight() : box.borderRight() + box.paddingRight() + box.contentWidth(); 134} 135 136int RenderFlexibleBox::baselinePosition(FontBaseline, bool, LineDirectionMode direction, LinePositionMode mode) const 137{ 138 ASSERT(mode == PositionOnContainingLine); 139 int baseline = firstLineBoxBaseline(); 140 if (baseline == -1) 141 baseline = synthesizedBaselineFromContentBox(*this, direction); 142 143 return beforeMarginInLineDirection(direction) + baseline; 144} 145 146int RenderFlexibleBox::firstLineBoxBaseline() const 147{ 148 if (isWritingModeRoot() || m_numberOfInFlowChildrenOnFirstLine <= 0) 149 return -1; 150 RenderBox* baselineChild = 0; 151 int childNumber = 0; 152 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { 153 if (child->isOutOfFlowPositioned()) 154 continue; 155 if (alignmentForChild(*child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(*child)) { 156 baselineChild = child; 157 break; 158 } 159 if (!baselineChild) 160 baselineChild = child; 161 162 ++childNumber; 163 if (childNumber == m_numberOfInFlowChildrenOnFirstLine) 164 break; 165 } 166 167 if (!baselineChild) 168 return -1; 169 170 if (!isColumnFlow() && hasOrthogonalFlow(*baselineChild)) 171 return crossAxisExtentForChild(*baselineChild) + baselineChild->logicalTop(); 172 if (isColumnFlow() && !hasOrthogonalFlow(*baselineChild)) 173 return mainAxisExtentForChild(*baselineChild) + baselineChild->logicalTop(); 174 175 int baseline = baselineChild->firstLineBoxBaseline(); 176 if (baseline == -1) { 177 // FIXME: We should pass |direction| into firstLineBoxBaseline and stop bailing out if we're a writing mode root. 178 // This would also fix some cases where the flexbox is orthogonal to its container. 179 LineDirectionMode direction = isHorizontalWritingMode() ? HorizontalLine : VerticalLine; 180 return synthesizedBaselineFromContentBox(*baselineChild, direction) + baselineChild->logicalTop(); 181 } 182 183 return baseline + baselineChild->logicalTop(); 184} 185 186int RenderFlexibleBox::inlineBlockBaseline(LineDirectionMode direction) const 187{ 188 int baseline = firstLineBoxBaseline(); 189 if (baseline != -1) 190 return baseline; 191 192 int marginAscent = direction == HorizontalLine ? marginTop() : marginRight(); 193 return synthesizedBaselineFromContentBox(*this, direction) + marginAscent; 194} 195 196static ItemPosition resolveAlignment(const RenderStyle* parentStyle, const RenderStyle* childStyle) 197{ 198 ItemPosition align = childStyle->alignSelf(); 199 if (align == ItemPositionAuto) 200 align = (parentStyle->alignItems() == ItemPositionAuto) ? ItemPositionStretch : parentStyle->alignItems(); 201 return align; 202} 203 204void RenderFlexibleBox::removeChild(RenderObject* child) 205{ 206 RenderBlock::removeChild(child); 207 m_intrinsicSizeAlongMainAxis.remove(child); 208} 209 210void RenderFlexibleBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 211{ 212 RenderBlock::styleDidChange(diff, oldStyle); 213 214 if (oldStyle && oldStyle->alignItems() == ItemPositionStretch && diff.needsFullLayout()) { 215 // Flex items that were previously stretching need to be relayed out so we can compute new available cross axis space. 216 // This is only necessary for stretching since other alignment values don't change the size of the box. 217 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { 218 ItemPosition previousAlignment = resolveAlignment(oldStyle, child->style()); 219 if (previousAlignment == ItemPositionStretch && previousAlignment != resolveAlignment(style(), child->style())) 220 child->setChildNeedsLayout(MarkOnlyThis); 221 } 222 } 223} 224 225void RenderFlexibleBox::layoutBlock(bool relayoutChildren) 226{ 227 ASSERT(needsLayout()); 228 229 if (!relayoutChildren && simplifiedLayout()) 230 return; 231 232 if (updateLogicalWidthAndColumnWidth()) 233 relayoutChildren = true; 234 235 LayoutUnit previousHeight = logicalHeight(); 236 setLogicalHeight(borderAndPaddingLogicalHeight() + scrollbarLogicalHeight()); 237 238 { 239 TextAutosizer::LayoutScope textAutosizerLayoutScope(this); 240 LayoutState state(*this, locationOffset()); 241 242 m_numberOfInFlowChildrenOnFirstLine = -1; 243 244 RenderBlock::startDelayUpdateScrollInfo(); 245 246 prepareOrderIteratorAndMargins(); 247 248 ChildFrameRects oldChildRects; 249 appendChildFrameRects(oldChildRects); 250 251 layoutFlexItems(relayoutChildren); 252 253 RenderBlock::finishDelayUpdateScrollInfo(); 254 255 if (logicalHeight() != previousHeight) 256 relayoutChildren = true; 257 258 layoutPositionedObjects(relayoutChildren || isDocumentElement()); 259 260 // FIXME: css3/flexbox/repaint-rtl-column.html seems to issue paint invalidations for more overflow than it needs to. 261 computeOverflow(clientLogicalBottomAfterRepositioning()); 262 } 263 264 updateLayerTransformAfterLayout(); 265 266 // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if 267 // we overflow or not. 268 updateScrollInfoAfterLayout(); 269 270 clearNeedsLayout(); 271} 272 273void RenderFlexibleBox::appendChildFrameRects(ChildFrameRects& childFrameRects) 274{ 275 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { 276 if (!child->isOutOfFlowPositioned()) 277 childFrameRects.append(child->frameRect()); 278 } 279} 280 281void RenderFlexibleBox::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 282{ 283 BlockPainter::paintChildrenOfFlexibleBox(*this, paintInfo, paintOffset); 284} 285 286void RenderFlexibleBox::repositionLogicalHeightDependentFlexItems(Vector<LineContext>& lineContexts) 287{ 288 LayoutUnit crossAxisStartEdge = lineContexts.isEmpty() ? LayoutUnit() : lineContexts[0].crossAxisOffset; 289 alignFlexLines(lineContexts); 290 291 alignChildren(lineContexts); 292 293 if (style()->flexWrap() == FlexWrapReverse) 294 flipForWrapReverse(lineContexts, crossAxisStartEdge); 295 296 // direction:rtl + flex-direction:column means the cross-axis direction is flipped. 297 flipForRightToLeftColumn(); 298} 299 300LayoutUnit RenderFlexibleBox::clientLogicalBottomAfterRepositioning() 301{ 302 LayoutUnit maxChildLogicalBottom = 0; 303 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { 304 if (child->isOutOfFlowPositioned()) 305 continue; 306 LayoutUnit childLogicalBottom = logicalTopForChild(child) + logicalHeightForChild(child) + marginAfterForChild(child); 307 maxChildLogicalBottom = std::max(maxChildLogicalBottom, childLogicalBottom); 308 } 309 return std::max(clientLogicalBottom(), maxChildLogicalBottom + paddingAfter()); 310} 311 312bool RenderFlexibleBox::hasOrthogonalFlow(RenderBox& child) const 313{ 314 // FIXME: If the child is a flexbox, then we need to check isHorizontalFlow. 315 return isHorizontalFlow() != child.isHorizontalWritingMode(); 316} 317 318bool RenderFlexibleBox::isColumnFlow() const 319{ 320 return style()->isColumnFlexDirection(); 321} 322 323bool RenderFlexibleBox::isHorizontalFlow() const 324{ 325 if (isHorizontalWritingMode()) 326 return !isColumnFlow(); 327 return isColumnFlow(); 328} 329 330bool RenderFlexibleBox::isLeftToRightFlow() const 331{ 332 if (isColumnFlow()) 333 return style()->writingMode() == TopToBottomWritingMode || style()->writingMode() == LeftToRightWritingMode; 334 return style()->isLeftToRightDirection() ^ (style()->flexDirection() == FlowRowReverse); 335} 336 337bool RenderFlexibleBox::isMultiline() const 338{ 339 return style()->flexWrap() != FlexNoWrap; 340} 341 342Length RenderFlexibleBox::flexBasisForChild(RenderBox& child) const 343{ 344 Length flexLength = child.style()->flexBasis(); 345 if (flexLength.isAuto()) 346 flexLength = isHorizontalFlow() ? child.style()->width() : child.style()->height(); 347 return flexLength; 348} 349 350LayoutUnit RenderFlexibleBox::crossAxisExtentForChild(RenderBox& child) const 351{ 352 return isHorizontalFlow() ? child.height() : child.width(); 353} 354 355static inline LayoutUnit constrainedChildIntrinsicContentLogicalHeight(RenderBox& child) 356{ 357 LayoutUnit childIntrinsicContentLogicalHeight = child.intrinsicContentLogicalHeight(); 358 return child.constrainLogicalHeightByMinMax(childIntrinsicContentLogicalHeight + child.borderAndPaddingLogicalHeight(), childIntrinsicContentLogicalHeight); 359} 360 361LayoutUnit RenderFlexibleBox::childIntrinsicHeight(RenderBox& child) const 362{ 363 if (child.isHorizontalWritingMode() && needToStretchChildLogicalHeight(child)) 364 return constrainedChildIntrinsicContentLogicalHeight(child); 365 return child.height(); 366} 367 368LayoutUnit RenderFlexibleBox::childIntrinsicWidth(RenderBox& child) const 369{ 370 if (!child.isHorizontalWritingMode() && needToStretchChildLogicalHeight(child)) 371 return constrainedChildIntrinsicContentLogicalHeight(child); 372 return child.width(); 373} 374 375LayoutUnit RenderFlexibleBox::crossAxisIntrinsicExtentForChild(RenderBox& child) const 376{ 377 return isHorizontalFlow() ? childIntrinsicHeight(child) : childIntrinsicWidth(child); 378} 379 380LayoutUnit RenderFlexibleBox::mainAxisExtentForChild(RenderBox& child) const 381{ 382 return isHorizontalFlow() ? child.width() : child.height(); 383} 384 385LayoutUnit RenderFlexibleBox::crossAxisExtent() const 386{ 387 return isHorizontalFlow() ? height() : width(); 388} 389 390LayoutUnit RenderFlexibleBox::mainAxisExtent() const 391{ 392 return isHorizontalFlow() ? width() : height(); 393} 394 395LayoutUnit RenderFlexibleBox::crossAxisContentExtent() const 396{ 397 return isHorizontalFlow() ? contentHeight() : contentWidth(); 398} 399 400LayoutUnit RenderFlexibleBox::mainAxisContentExtent(LayoutUnit contentLogicalHeight) 401{ 402 if (isColumnFlow()) { 403 LogicalExtentComputedValues computedValues; 404 LayoutUnit borderPaddingAndScrollbar = borderAndPaddingLogicalHeight() + scrollbarLogicalHeight(); 405 LayoutUnit borderBoxLogicalHeight = contentLogicalHeight + borderPaddingAndScrollbar; 406 computeLogicalHeight(borderBoxLogicalHeight, logicalTop(), computedValues); 407 if (computedValues.m_extent == LayoutUnit::max()) 408 return computedValues.m_extent; 409 return std::max(LayoutUnit(0), computedValues.m_extent - borderPaddingAndScrollbar); 410 } 411 return contentLogicalWidth(); 412} 413 414LayoutUnit RenderFlexibleBox::computeMainAxisExtentForChild(RenderBox& child, SizeType sizeType, const Length& size) 415{ 416 // FIXME: This is wrong for orthogonal flows. It should use the flexbox's writing-mode, not the child's in order 417 // to figure out the logical height/width. 418 if (isColumnFlow()) { 419 // We don't have to check for "auto" here - computeContentLogicalHeight will just return -1 for that case anyway. 420 if (size.isIntrinsic()) 421 child.layoutIfNeeded(); 422 return child.computeContentLogicalHeight(size, child.logicalHeight() - child.borderAndPaddingLogicalHeight()) + child.scrollbarLogicalHeight(); 423 } 424 return child.computeLogicalWidthUsing(sizeType, size, contentLogicalWidth(), this) - child.borderAndPaddingLogicalWidth(); 425} 426 427WritingMode RenderFlexibleBox::transformedWritingMode() const 428{ 429 WritingMode mode = style()->writingMode(); 430 if (!isColumnFlow()) 431 return mode; 432 433 switch (mode) { 434 case TopToBottomWritingMode: 435 case BottomToTopWritingMode: 436 return style()->isLeftToRightDirection() ? LeftToRightWritingMode : RightToLeftWritingMode; 437 case LeftToRightWritingMode: 438 case RightToLeftWritingMode: 439 return style()->isLeftToRightDirection() ? TopToBottomWritingMode : BottomToTopWritingMode; 440 } 441 ASSERT_NOT_REACHED(); 442 return TopToBottomWritingMode; 443} 444 445LayoutUnit RenderFlexibleBox::flowAwareBorderStart() const 446{ 447 if (isHorizontalFlow()) 448 return isLeftToRightFlow() ? borderLeft() : borderRight(); 449 return isLeftToRightFlow() ? borderTop() : borderBottom(); 450} 451 452LayoutUnit RenderFlexibleBox::flowAwareBorderEnd() const 453{ 454 if (isHorizontalFlow()) 455 return isLeftToRightFlow() ? borderRight() : borderLeft(); 456 return isLeftToRightFlow() ? borderBottom() : borderTop(); 457} 458 459LayoutUnit RenderFlexibleBox::flowAwareBorderBefore() const 460{ 461 switch (transformedWritingMode()) { 462 case TopToBottomWritingMode: 463 return borderTop(); 464 case BottomToTopWritingMode: 465 return borderBottom(); 466 case LeftToRightWritingMode: 467 return borderLeft(); 468 case RightToLeftWritingMode: 469 return borderRight(); 470 } 471 ASSERT_NOT_REACHED(); 472 return borderTop(); 473} 474 475LayoutUnit RenderFlexibleBox::flowAwareBorderAfter() const 476{ 477 switch (transformedWritingMode()) { 478 case TopToBottomWritingMode: 479 return borderBottom(); 480 case BottomToTopWritingMode: 481 return borderTop(); 482 case LeftToRightWritingMode: 483 return borderRight(); 484 case RightToLeftWritingMode: 485 return borderLeft(); 486 } 487 ASSERT_NOT_REACHED(); 488 return borderTop(); 489} 490 491LayoutUnit RenderFlexibleBox::flowAwarePaddingStart() const 492{ 493 if (isHorizontalFlow()) 494 return isLeftToRightFlow() ? paddingLeft() : paddingRight(); 495 return isLeftToRightFlow() ? paddingTop() : paddingBottom(); 496} 497 498LayoutUnit RenderFlexibleBox::flowAwarePaddingEnd() const 499{ 500 if (isHorizontalFlow()) 501 return isLeftToRightFlow() ? paddingRight() : paddingLeft(); 502 return isLeftToRightFlow() ? paddingBottom() : paddingTop(); 503} 504 505LayoutUnit RenderFlexibleBox::flowAwarePaddingBefore() const 506{ 507 switch (transformedWritingMode()) { 508 case TopToBottomWritingMode: 509 return paddingTop(); 510 case BottomToTopWritingMode: 511 return paddingBottom(); 512 case LeftToRightWritingMode: 513 return paddingLeft(); 514 case RightToLeftWritingMode: 515 return paddingRight(); 516 } 517 ASSERT_NOT_REACHED(); 518 return paddingTop(); 519} 520 521LayoutUnit RenderFlexibleBox::flowAwarePaddingAfter() const 522{ 523 switch (transformedWritingMode()) { 524 case TopToBottomWritingMode: 525 return paddingBottom(); 526 case BottomToTopWritingMode: 527 return paddingTop(); 528 case LeftToRightWritingMode: 529 return paddingRight(); 530 case RightToLeftWritingMode: 531 return paddingLeft(); 532 } 533 ASSERT_NOT_REACHED(); 534 return paddingTop(); 535} 536 537LayoutUnit RenderFlexibleBox::flowAwareMarginStartForChild(RenderBox& child) const 538{ 539 if (isHorizontalFlow()) 540 return isLeftToRightFlow() ? child.marginLeft() : child.marginRight(); 541 return isLeftToRightFlow() ? child.marginTop() : child.marginBottom(); 542} 543 544LayoutUnit RenderFlexibleBox::flowAwareMarginEndForChild(RenderBox& child) const 545{ 546 if (isHorizontalFlow()) 547 return isLeftToRightFlow() ? child.marginRight() : child.marginLeft(); 548 return isLeftToRightFlow() ? child.marginBottom() : child.marginTop(); 549} 550 551LayoutUnit RenderFlexibleBox::flowAwareMarginBeforeForChild(RenderBox& child) const 552{ 553 switch (transformedWritingMode()) { 554 case TopToBottomWritingMode: 555 return child.marginTop(); 556 case BottomToTopWritingMode: 557 return child.marginBottom(); 558 case LeftToRightWritingMode: 559 return child.marginLeft(); 560 case RightToLeftWritingMode: 561 return child.marginRight(); 562 } 563 ASSERT_NOT_REACHED(); 564 return marginTop(); 565} 566 567LayoutUnit RenderFlexibleBox::crossAxisMarginExtentForChild(RenderBox& child) const 568{ 569 return isHorizontalFlow() ? child.marginHeight() : child.marginWidth(); 570} 571 572LayoutUnit RenderFlexibleBox::crossAxisScrollbarExtent() const 573{ 574 return isHorizontalFlow() ? horizontalScrollbarHeight() : verticalScrollbarWidth(); 575} 576 577LayoutUnit RenderFlexibleBox::crossAxisScrollbarExtentForChild(RenderBox& child) const 578{ 579 return isHorizontalFlow() ? child.horizontalScrollbarHeight() : child.verticalScrollbarWidth(); 580} 581 582LayoutPoint RenderFlexibleBox::flowAwareLocationForChild(RenderBox& child) const 583{ 584 return isHorizontalFlow() ? child.location() : child.location().transposedPoint(); 585} 586 587void RenderFlexibleBox::setFlowAwareLocationForChild(RenderBox& child, const LayoutPoint& location) 588{ 589 if (isHorizontalFlow()) 590 child.setLocation(location); 591 else 592 child.setLocation(location.transposedPoint()); 593} 594 595LayoutUnit RenderFlexibleBox::mainAxisBorderAndPaddingExtentForChild(RenderBox& child) const 596{ 597 return isHorizontalFlow() ? child.borderAndPaddingWidth() : child.borderAndPaddingHeight(); 598} 599 600static inline bool preferredMainAxisExtentDependsOnLayout(const Length& flexBasis, bool hasInfiniteLineLength) 601{ 602 return flexBasis.isAuto() || (flexBasis.isPercent() && hasInfiniteLineLength); 603} 604 605bool RenderFlexibleBox::childPreferredMainAxisContentExtentRequiresLayout(RenderBox& child, bool hasInfiniteLineLength) const 606{ 607 return preferredMainAxisExtentDependsOnLayout(flexBasisForChild(child), hasInfiniteLineLength) && hasOrthogonalFlow(child); 608} 609 610LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox& child, bool hasInfiniteLineLength, bool relayoutChildren) 611{ 612 child.clearOverrideSize(); 613 614 if (child.style()->hasAspectRatio() || child.isImage() || child.isVideo() || child.isCanvas()) 615 UseCounter::count(document(), UseCounter::AspectRatioFlexItem); 616 617 Length flexBasis = flexBasisForChild(child); 618 if (preferredMainAxisExtentDependsOnLayout(flexBasis, hasInfiniteLineLength)) { 619 LayoutUnit mainAxisExtent; 620 if (hasOrthogonalFlow(child)) { 621 if (child.needsLayout() || relayoutChildren) { 622 m_intrinsicSizeAlongMainAxis.remove(&child); 623 child.forceChildLayout(); 624 m_intrinsicSizeAlongMainAxis.set(&child, child.logicalHeight()); 625 } 626 ASSERT(m_intrinsicSizeAlongMainAxis.contains(&child)); 627 mainAxisExtent = m_intrinsicSizeAlongMainAxis.get(&child); 628 } else { 629 mainAxisExtent = child.maxPreferredLogicalWidth(); 630 } 631 ASSERT(mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child) >= 0); 632 return mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child); 633 } 634 return std::max(LayoutUnit(0), computeMainAxisExtentForChild(child, MainOrPreferredSize, flexBasis)); 635} 636 637void RenderFlexibleBox::layoutFlexItems(bool relayoutChildren) 638{ 639 Vector<LineContext> lineContexts; 640 OrderedFlexItemList orderedChildren; 641 LayoutUnit sumFlexBaseSize; 642 double totalFlexGrow; 643 double totalWeightedFlexShrink; 644 LayoutUnit sumHypotheticalMainSize; 645 646 Vector<LayoutUnit, 16> childSizes; 647 648 m_orderIterator.first(); 649 LayoutUnit crossAxisOffset = flowAwareBorderBefore() + flowAwarePaddingBefore(); 650 bool hasInfiniteLineLength = false; 651 while (computeNextFlexLine(orderedChildren, sumFlexBaseSize, totalFlexGrow, totalWeightedFlexShrink, sumHypotheticalMainSize, hasInfiniteLineLength, relayoutChildren)) { 652 LayoutUnit containerMainInnerSize = mainAxisContentExtent(sumHypotheticalMainSize); 653 LayoutUnit availableFreeSpace = containerMainInnerSize - sumFlexBaseSize; 654 FlexSign flexSign = (sumHypotheticalMainSize < containerMainInnerSize) ? PositiveFlexibility : NegativeFlexibility; 655 InflexibleFlexItemSize inflexibleItems; 656 childSizes.reserveCapacity(orderedChildren.size()); 657 while (!resolveFlexibleLengths(flexSign, orderedChildren, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, childSizes, hasInfiniteLineLength)) { 658 ASSERT(totalFlexGrow >= 0 && totalWeightedFlexShrink >= 0); 659 ASSERT(inflexibleItems.size() > 0); 660 } 661 662 layoutAndPlaceChildren(crossAxisOffset, orderedChildren, childSizes, availableFreeSpace, relayoutChildren, lineContexts, hasInfiniteLineLength); 663 } 664 if (hasLineIfEmpty()) { 665 // Even if computeNextFlexLine returns true, the flexbox might not have 666 // a line because all our children might be out of flow positioned. 667 // Instead of just checking if we have a line, make sure the flexbox 668 // has at least a line's worth of height to cover this case. 669 LayoutUnit minHeight = borderAndPaddingLogicalHeight() 670 + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes) 671 + scrollbarLogicalHeight(); 672 if (height() < minHeight) 673 setLogicalHeight(minHeight); 674 } 675 676 updateLogicalHeight(); 677 repositionLogicalHeightDependentFlexItems(lineContexts); 678} 679 680LayoutUnit RenderFlexibleBox::autoMarginOffsetInMainAxis(const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace) 681{ 682 if (availableFreeSpace <= 0) 683 return 0; 684 685 int numberOfAutoMargins = 0; 686 bool isHorizontal = isHorizontalFlow(); 687 for (size_t i = 0; i < children.size(); ++i) { 688 RenderBox* child = children[i]; 689 if (child->isOutOfFlowPositioned()) 690 continue; 691 if (isHorizontal) { 692 if (child->style()->marginLeft().isAuto()) 693 ++numberOfAutoMargins; 694 if (child->style()->marginRight().isAuto()) 695 ++numberOfAutoMargins; 696 } else { 697 if (child->style()->marginTop().isAuto()) 698 ++numberOfAutoMargins; 699 if (child->style()->marginBottom().isAuto()) 700 ++numberOfAutoMargins; 701 } 702 } 703 if (!numberOfAutoMargins) 704 return 0; 705 706 LayoutUnit sizeOfAutoMargin = availableFreeSpace / numberOfAutoMargins; 707 availableFreeSpace = 0; 708 return sizeOfAutoMargin; 709} 710 711void RenderFlexibleBox::updateAutoMarginsInMainAxis(RenderBox& child, LayoutUnit autoMarginOffset) 712{ 713 ASSERT(autoMarginOffset >= 0); 714 715 if (isHorizontalFlow()) { 716 if (child.style()->marginLeft().isAuto()) 717 child.setMarginLeft(autoMarginOffset); 718 if (child.style()->marginRight().isAuto()) 719 child.setMarginRight(autoMarginOffset); 720 } else { 721 if (child.style()->marginTop().isAuto()) 722 child.setMarginTop(autoMarginOffset); 723 if (child.style()->marginBottom().isAuto()) 724 child.setMarginBottom(autoMarginOffset); 725 } 726} 727 728bool RenderFlexibleBox::hasAutoMarginsInCrossAxis(RenderBox& child) const 729{ 730 if (isHorizontalFlow()) 731 return child.style()->marginTop().isAuto() || child.style()->marginBottom().isAuto(); 732 return child.style()->marginLeft().isAuto() || child.style()->marginRight().isAuto(); 733} 734 735LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChild(LayoutUnit lineCrossAxisExtent, RenderBox& child) 736{ 737 ASSERT(!child.isOutOfFlowPositioned()); 738 LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child); 739 return lineCrossAxisExtent - childCrossExtent; 740} 741 742LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChildBeforeStretching(LayoutUnit lineCrossAxisExtent, RenderBox& child) 743{ 744 ASSERT(!child.isOutOfFlowPositioned()); 745 LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + crossAxisIntrinsicExtentForChild(child); 746 return lineCrossAxisExtent - childCrossExtent; 747} 748 749bool RenderFlexibleBox::updateAutoMarginsInCrossAxis(RenderBox& child, LayoutUnit availableAlignmentSpace) 750{ 751 ASSERT(!child.isOutOfFlowPositioned()); 752 ASSERT(availableAlignmentSpace >= 0); 753 754 bool isHorizontal = isHorizontalFlow(); 755 Length topOrLeft = isHorizontal ? child.style()->marginTop() : child.style()->marginLeft(); 756 Length bottomOrRight = isHorizontal ? child.style()->marginBottom() : child.style()->marginRight(); 757 if (topOrLeft.isAuto() && bottomOrRight.isAuto()) { 758 adjustAlignmentForChild(child, availableAlignmentSpace / 2); 759 if (isHorizontal) { 760 child.setMarginTop(availableAlignmentSpace / 2); 761 child.setMarginBottom(availableAlignmentSpace / 2); 762 } else { 763 child.setMarginLeft(availableAlignmentSpace / 2); 764 child.setMarginRight(availableAlignmentSpace / 2); 765 } 766 return true; 767 } 768 bool shouldAdjustTopOrLeft = true; 769 if (isColumnFlow() && !child.style()->isLeftToRightDirection()) { 770 // For column flows, only make this adjustment if topOrLeft corresponds to the "before" margin, 771 // so that flipForRightToLeftColumn will do the right thing. 772 shouldAdjustTopOrLeft = false; 773 } 774 if (!isColumnFlow() && child.style()->isFlippedBlocksWritingMode()) { 775 // If we are a flipped writing mode, we need to adjust the opposite side. This is only needed 776 // for row flows because this only affects the block-direction axis. 777 shouldAdjustTopOrLeft = false; 778 } 779 780 if (topOrLeft.isAuto()) { 781 if (shouldAdjustTopOrLeft) 782 adjustAlignmentForChild(child, availableAlignmentSpace); 783 784 if (isHorizontal) 785 child.setMarginTop(availableAlignmentSpace); 786 else 787 child.setMarginLeft(availableAlignmentSpace); 788 return true; 789 } 790 if (bottomOrRight.isAuto()) { 791 if (!shouldAdjustTopOrLeft) 792 adjustAlignmentForChild(child, availableAlignmentSpace); 793 794 if (isHorizontal) 795 child.setMarginBottom(availableAlignmentSpace); 796 else 797 child.setMarginRight(availableAlignmentSpace); 798 return true; 799 } 800 return false; 801} 802 803LayoutUnit RenderFlexibleBox::marginBoxAscentForChild(RenderBox& child) 804{ 805 LayoutUnit ascent = child.firstLineBoxBaseline(); 806 if (ascent == -1) 807 ascent = crossAxisExtentForChild(child); 808 return ascent + flowAwareMarginBeforeForChild(child); 809} 810 811LayoutUnit RenderFlexibleBox::computeChildMarginValue(Length margin) 812{ 813 // When resolving the margins, we use the content size for resolving percent and calc (for percents in calc expressions) margins. 814 // Fortunately, percent margins are always computed with respect to the block's width, even for margin-top and margin-bottom. 815 LayoutUnit availableSize = contentLogicalWidth(); 816 return minimumValueForLength(margin, availableSize); 817} 818 819void RenderFlexibleBox::prepareOrderIteratorAndMargins() 820{ 821 OrderIteratorPopulator populator(m_orderIterator); 822 823 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { 824 populator.collectChild(child); 825 826 if (child->isOutOfFlowPositioned()) 827 continue; 828 829 // Before running the flex algorithm, 'auto' has a margin of 0. 830 // Also, if we're not auto sizing, we don't do a layout that computes the start/end margins. 831 if (isHorizontalFlow()) { 832 child->setMarginLeft(computeChildMarginValue(child->style()->marginLeft())); 833 child->setMarginRight(computeChildMarginValue(child->style()->marginRight())); 834 } else { 835 child->setMarginTop(computeChildMarginValue(child->style()->marginTop())); 836 child->setMarginBottom(computeChildMarginValue(child->style()->marginBottom())); 837 } 838 } 839} 840 841LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(RenderBox& child, LayoutUnit childSize) 842{ 843 Length max = isHorizontalFlow() ? child.style()->maxWidth() : child.style()->maxHeight(); 844 if (max.isSpecifiedOrIntrinsic()) { 845 LayoutUnit maxExtent = computeMainAxisExtentForChild(child, MaxSize, max); 846 if (maxExtent != -1 && childSize > maxExtent) 847 childSize = maxExtent; 848 } 849 850 Length min = isHorizontalFlow() ? child.style()->minWidth() : child.style()->minHeight(); 851 LayoutUnit minExtent = 0; 852 if (min.isSpecifiedOrIntrinsic()) 853 minExtent = computeMainAxisExtentForChild(child, MinSize, min); 854 return std::max(childSize, minExtent); 855} 856 857bool RenderFlexibleBox::computeNextFlexLine(OrderedFlexItemList& orderedChildren, LayoutUnit& sumFlexBaseSize, double& totalFlexGrow, double& totalWeightedFlexShrink, LayoutUnit& sumHypotheticalMainSize, bool& hasInfiniteLineLength, bool relayoutChildren) 858{ 859 orderedChildren.clear(); 860 sumFlexBaseSize = 0; 861 totalFlexGrow = totalWeightedFlexShrink = 0; 862 sumHypotheticalMainSize = 0; 863 864 if (!m_orderIterator.currentChild()) 865 return false; 866 867 LayoutUnit lineBreakLength = mainAxisContentExtent(LayoutUnit::max()); 868 hasInfiniteLineLength = lineBreakLength == LayoutUnit::max(); 869 870 bool lineHasInFlowItem = false; 871 872 for (RenderBox* child = m_orderIterator.currentChild(); child; child = m_orderIterator.next()) { 873 if (child->isOutOfFlowPositioned()) { 874 orderedChildren.append(child); 875 continue; 876 } 877 878 LayoutUnit childMainAxisExtent = preferredMainAxisContentExtentForChild(*child, hasInfiniteLineLength, relayoutChildren); 879 LayoutUnit childMainAxisMarginBorderPadding = mainAxisBorderAndPaddingExtentForChild(*child) 880 + (isHorizontalFlow() ? child->marginWidth() : child->marginHeight()); 881 LayoutUnit childFlexBaseSize = childMainAxisExtent + childMainAxisMarginBorderPadding; 882 883 LayoutUnit childMinMaxAppliedMainAxisExtent = adjustChildSizeForMinAndMax(*child, childMainAxisExtent); 884 LayoutUnit childHypotheticalMainSize = childMinMaxAppliedMainAxisExtent + childMainAxisMarginBorderPadding; 885 886 if (isMultiline() && sumHypotheticalMainSize + childHypotheticalMainSize > lineBreakLength && lineHasInFlowItem) 887 break; 888 orderedChildren.append(child); 889 lineHasInFlowItem = true; 890 sumFlexBaseSize += childFlexBaseSize; 891 totalFlexGrow += child->style()->flexGrow(); 892 totalWeightedFlexShrink += child->style()->flexShrink() * childMainAxisExtent; 893 sumHypotheticalMainSize += childHypotheticalMainSize; 894 } 895 return true; 896} 897 898void RenderFlexibleBox::freezeViolations(const Vector<Violation>& violations, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, bool hasInfiniteLineLength) 899{ 900 for (size_t i = 0; i < violations.size(); ++i) { 901 RenderBox* child = violations[i].child; 902 LayoutUnit childSize = violations[i].childSize; 903 LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(*child, hasInfiniteLineLength); 904 availableFreeSpace -= childSize - preferredChildSize; 905 totalFlexGrow -= child->style()->flexGrow(); 906 totalWeightedFlexShrink -= child->style()->flexShrink() * preferredChildSize; 907 inflexibleItems.set(child, childSize); 908 } 909} 910 911// Returns true if we successfully ran the algorithm and sized the flex items. 912bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, Vector<LayoutUnit, 16>& childSizes, bool hasInfiniteLineLength) 913{ 914 childSizes.resize(0); 915 LayoutUnit totalViolation = 0; 916 LayoutUnit usedFreeSpace = 0; 917 Vector<Violation> minViolations; 918 Vector<Violation> maxViolations; 919 for (size_t i = 0; i < children.size(); ++i) { 920 RenderBox* child = children[i]; 921 if (child->isOutOfFlowPositioned()) { 922 childSizes.append(0); 923 continue; 924 } 925 926 if (inflexibleItems.contains(child)) 927 childSizes.append(inflexibleItems.get(child)); 928 else { 929 LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(*child, hasInfiniteLineLength); 930 LayoutUnit childSize = preferredChildSize; 931 double extraSpace = 0; 932 if (availableFreeSpace > 0 && totalFlexGrow > 0 && flexSign == PositiveFlexibility && std::isfinite(totalFlexGrow)) 933 extraSpace = availableFreeSpace * child->style()->flexGrow() / totalFlexGrow; 934 else if (availableFreeSpace < 0 && totalWeightedFlexShrink > 0 && flexSign == NegativeFlexibility && std::isfinite(totalWeightedFlexShrink)) 935 extraSpace = availableFreeSpace * child->style()->flexShrink() * preferredChildSize / totalWeightedFlexShrink; 936 if (std::isfinite(extraSpace)) 937 childSize += LayoutUnit::fromFloatRound(extraSpace); 938 939 LayoutUnit adjustedChildSize = adjustChildSizeForMinAndMax(*child, childSize); 940 childSizes.append(adjustedChildSize); 941 usedFreeSpace += adjustedChildSize - preferredChildSize; 942 943 LayoutUnit violation = adjustedChildSize - childSize; 944 if (violation > 0) 945 minViolations.append(Violation(child, adjustedChildSize)); 946 else if (violation < 0) 947 maxViolations.append(Violation(child, adjustedChildSize)); 948 totalViolation += violation; 949 } 950 } 951 952 if (totalViolation) 953 freezeViolations(totalViolation < 0 ? maxViolations : minViolations, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, hasInfiniteLineLength); 954 else 955 availableFreeSpace -= usedFreeSpace; 956 957 return !totalViolation; 958} 959 960static LayoutUnit initialJustifyContentOffset(LayoutUnit availableFreeSpace, EJustifyContent justifyContent, unsigned numberOfChildren) 961{ 962 if (justifyContent == JustifyFlexEnd) 963 return availableFreeSpace; 964 if (justifyContent == JustifyCenter) 965 return availableFreeSpace / 2; 966 if (justifyContent == JustifySpaceAround) { 967 if (availableFreeSpace > 0 && numberOfChildren) 968 return availableFreeSpace / (2 * numberOfChildren); 969 else 970 return availableFreeSpace / 2; 971 } 972 return 0; 973} 974 975static LayoutUnit justifyContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, EJustifyContent justifyContent, unsigned numberOfChildren) 976{ 977 if (availableFreeSpace > 0 && numberOfChildren > 1) { 978 if (justifyContent == JustifySpaceBetween) 979 return availableFreeSpace / (numberOfChildren - 1); 980 if (justifyContent == JustifySpaceAround) 981 return availableFreeSpace / numberOfChildren; 982 } 983 return 0; 984} 985 986void RenderFlexibleBox::setLogicalOverrideSize(RenderBox& child, LayoutUnit childPreferredSize) 987{ 988 if (hasOrthogonalFlow(child)) 989 child.setOverrideLogicalContentHeight(childPreferredSize - child.borderAndPaddingLogicalHeight()); 990 else 991 child.setOverrideLogicalContentWidth(childPreferredSize - child.borderAndPaddingLogicalWidth()); 992} 993 994void RenderFlexibleBox::prepareChildForPositionedLayout(RenderBox& child, LayoutUnit mainAxisOffset, LayoutUnit crossAxisOffset, PositionedLayoutMode layoutMode) 995{ 996 ASSERT(child.isOutOfFlowPositioned()); 997 child.containingBlock()->insertPositionedObject(&child); 998 RenderLayer* childLayer = child.layer(); 999 LayoutUnit inlinePosition = isColumnFlow() ? crossAxisOffset : mainAxisOffset; 1000 if (layoutMode == FlipForRowReverse && style()->flexDirection() == FlowRowReverse) 1001 inlinePosition = mainAxisExtent() - mainAxisOffset; 1002 childLayer->setStaticInlinePosition(inlinePosition); 1003 1004 LayoutUnit staticBlockPosition = isColumnFlow() ? mainAxisOffset : crossAxisOffset; 1005 if (childLayer->staticBlockPosition() != staticBlockPosition) { 1006 childLayer->setStaticBlockPosition(staticBlockPosition); 1007 if (child.style()->hasStaticBlockPosition(style()->isHorizontalWritingMode())) 1008 child.setChildNeedsLayout(MarkOnlyThis); 1009 } 1010} 1011 1012ItemPosition RenderFlexibleBox::alignmentForChild(RenderBox& child) const 1013{ 1014 ItemPosition align = resolveAlignment(style(), child.style()); 1015 1016 if (align == ItemPositionBaseline && hasOrthogonalFlow(child)) 1017 align = ItemPositionFlexStart; 1018 1019 if (style()->flexWrap() == FlexWrapReverse) { 1020 if (align == ItemPositionFlexStart) 1021 align = ItemPositionFlexEnd; 1022 else if (align == ItemPositionFlexEnd) 1023 align = ItemPositionFlexStart; 1024 } 1025 1026 return align; 1027} 1028 1029size_t RenderFlexibleBox::numberOfInFlowPositionedChildren(const OrderedFlexItemList& children) const 1030{ 1031 size_t count = 0; 1032 for (size_t i = 0; i < children.size(); ++i) { 1033 RenderBox* child = children[i]; 1034 if (!child->isOutOfFlowPositioned()) 1035 ++count; 1036 } 1037 return count; 1038} 1039 1040void RenderFlexibleBox::resetAutoMarginsAndLogicalTopInCrossAxis(RenderBox& child) 1041{ 1042 if (hasAutoMarginsInCrossAxis(child)) { 1043 child.updateLogicalHeight(); 1044 if (isHorizontalFlow()) { 1045 if (child.style()->marginTop().isAuto()) 1046 child.setMarginTop(0); 1047 if (child.style()->marginBottom().isAuto()) 1048 child.setMarginBottom(0); 1049 } else { 1050 if (child.style()->marginLeft().isAuto()) 1051 child.setMarginLeft(0); 1052 if (child.style()->marginRight().isAuto()) 1053 child.setMarginRight(0); 1054 } 1055 } 1056} 1057 1058bool RenderFlexibleBox::needToStretchChildLogicalHeight(RenderBox& child) const 1059{ 1060 if (alignmentForChild(child) != ItemPositionStretch) 1061 return false; 1062 1063 return isHorizontalFlow() && child.style()->height().isAuto(); 1064} 1065 1066void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList& children, const Vector<LayoutUnit, 16>& childSizes, LayoutUnit availableFreeSpace, bool relayoutChildren, Vector<LineContext>& lineContexts, bool hasInfiniteLineLength) 1067{ 1068 ASSERT(childSizes.size() == children.size()); 1069 1070 size_t numberOfChildrenForJustifyContent = numberOfInFlowPositionedChildren(children); 1071 LayoutUnit autoMarginOffset = autoMarginOffsetInMainAxis(children, availableFreeSpace); 1072 LayoutUnit mainAxisOffset = flowAwareBorderStart() + flowAwarePaddingStart(); 1073 mainAxisOffset += initialJustifyContentOffset(availableFreeSpace, style()->justifyContent(), numberOfChildrenForJustifyContent); 1074 if (style()->flexDirection() == FlowRowReverse) 1075 mainAxisOffset += isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight(); 1076 1077 LayoutUnit totalMainExtent = mainAxisExtent(); 1078 LayoutUnit maxAscent = 0, maxDescent = 0; // Used when align-items: baseline. 1079 LayoutUnit maxChildCrossAxisExtent = 0; 1080 size_t seenInFlowPositionedChildren = 0; 1081 bool shouldFlipMainAxis = !isColumnFlow() && !isLeftToRightFlow(); 1082 for (size_t i = 0; i < children.size(); ++i) { 1083 RenderBox* child = children[i]; 1084 1085 if (child->isOutOfFlowPositioned()) { 1086 prepareChildForPositionedLayout(*child, mainAxisOffset, crossAxisOffset, FlipForRowReverse); 1087 continue; 1088 } 1089 1090 // FIXME Investigate if this can be removed based on other flags. crbug.com/370010 1091 child->setMayNeedPaintInvalidation(true); 1092 1093 LayoutUnit childPreferredSize = childSizes[i] + mainAxisBorderAndPaddingExtentForChild(*child); 1094 setLogicalOverrideSize(*child, childPreferredSize); 1095 if (childPreferredSize != mainAxisExtentForChild(*child)) { 1096 child->setChildNeedsLayout(MarkOnlyThis); 1097 } else { 1098 // To avoid double applying margin changes in updateAutoMarginsInCrossAxis, we reset the margins here. 1099 resetAutoMarginsAndLogicalTopInCrossAxis(*child); 1100 } 1101 // We may have already forced relayout for orthogonal flowing children in preferredMainAxisContentExtentForChild. 1102 bool forceChildRelayout = relayoutChildren && !childPreferredMainAxisContentExtentRequiresLayout(*child, hasInfiniteLineLength); 1103 updateBlockChildDirtyBitsBeforeLayout(forceChildRelayout, child); 1104 child->layoutIfNeeded(); 1105 1106 updateAutoMarginsInMainAxis(*child, autoMarginOffset); 1107 1108 LayoutUnit childCrossAxisMarginBoxExtent; 1109 if (alignmentForChild(*child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(*child)) { 1110 LayoutUnit ascent = marginBoxAscentForChild(*child); 1111 LayoutUnit descent = (crossAxisMarginExtentForChild(*child) + crossAxisExtentForChild(*child)) - ascent; 1112 1113 maxAscent = std::max(maxAscent, ascent); 1114 maxDescent = std::max(maxDescent, descent); 1115 1116 childCrossAxisMarginBoxExtent = maxAscent + maxDescent; 1117 } else { 1118 childCrossAxisMarginBoxExtent = crossAxisIntrinsicExtentForChild(*child) + crossAxisMarginExtentForChild(*child) + crossAxisScrollbarExtentForChild(*child); 1119 } 1120 if (!isColumnFlow()) 1121 setLogicalHeight(std::max(logicalHeight(), crossAxisOffset + flowAwareBorderAfter() + flowAwarePaddingAfter() + childCrossAxisMarginBoxExtent + crossAxisScrollbarExtent())); 1122 maxChildCrossAxisExtent = std::max(maxChildCrossAxisExtent, childCrossAxisMarginBoxExtent); 1123 1124 mainAxisOffset += flowAwareMarginStartForChild(*child); 1125 1126 LayoutUnit childMainExtent = mainAxisExtentForChild(*child); 1127 // In an RTL column situation, this will apply the margin-right/margin-end on the left. 1128 // This will be fixed later in flipForRightToLeftColumn. 1129 LayoutPoint childLocation(shouldFlipMainAxis ? totalMainExtent - mainAxisOffset - childMainExtent : mainAxisOffset, 1130 crossAxisOffset + flowAwareMarginBeforeForChild(*child)); 1131 1132 // FIXME: Supporting layout deltas. 1133 setFlowAwareLocationForChild(*child, childLocation); 1134 mainAxisOffset += childMainExtent + flowAwareMarginEndForChild(*child); 1135 1136 ++seenInFlowPositionedChildren; 1137 if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent) 1138 mainAxisOffset += justifyContentSpaceBetweenChildren(availableFreeSpace, style()->justifyContent(), numberOfChildrenForJustifyContent); 1139 } 1140 1141 if (isColumnFlow()) 1142 setLogicalHeight(mainAxisOffset + flowAwareBorderEnd() + flowAwarePaddingEnd() + scrollbarLogicalHeight()); 1143 1144 if (style()->flexDirection() == FlowColumnReverse) { 1145 // We have to do an extra pass for column-reverse to reposition the flex items since the start depends 1146 // on the height of the flexbox, which we only know after we've positioned all the flex items. 1147 updateLogicalHeight(); 1148 layoutColumnReverse(children, crossAxisOffset, availableFreeSpace); 1149 } 1150 1151 if (m_numberOfInFlowChildrenOnFirstLine == -1) 1152 m_numberOfInFlowChildrenOnFirstLine = seenInFlowPositionedChildren; 1153 lineContexts.append(LineContext(crossAxisOffset, maxChildCrossAxisExtent, children.size(), maxAscent)); 1154 crossAxisOffset += maxChildCrossAxisExtent; 1155} 1156 1157void RenderFlexibleBox::layoutColumnReverse(const OrderedFlexItemList& children, LayoutUnit crossAxisOffset, LayoutUnit availableFreeSpace) 1158{ 1159 // This is similar to the logic in layoutAndPlaceChildren, except we place the children 1160 // starting from the end of the flexbox. We also don't need to layout anything since we're 1161 // just moving the children to a new position. 1162 size_t numberOfChildrenForJustifyContent = numberOfInFlowPositionedChildren(children); 1163 LayoutUnit mainAxisOffset = logicalHeight() - flowAwareBorderEnd() - flowAwarePaddingEnd(); 1164 mainAxisOffset -= initialJustifyContentOffset(availableFreeSpace, style()->justifyContent(), numberOfChildrenForJustifyContent); 1165 mainAxisOffset -= isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight(); 1166 1167 size_t seenInFlowPositionedChildren = 0; 1168 for (size_t i = 0; i < children.size(); ++i) { 1169 RenderBox* child = children[i]; 1170 1171 if (child->isOutOfFlowPositioned()) { 1172 child->layer()->setStaticBlockPosition(mainAxisOffset); 1173 continue; 1174 } 1175 mainAxisOffset -= mainAxisExtentForChild(*child) + flowAwareMarginEndForChild(*child); 1176 1177 setFlowAwareLocationForChild(*child, LayoutPoint(mainAxisOffset, crossAxisOffset + flowAwareMarginBeforeForChild(*child))); 1178 1179 mainAxisOffset -= flowAwareMarginStartForChild(*child); 1180 1181 ++seenInFlowPositionedChildren; 1182 if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent) 1183 mainAxisOffset -= justifyContentSpaceBetweenChildren(availableFreeSpace, style()->justifyContent(), numberOfChildrenForJustifyContent); 1184 } 1185} 1186 1187static LayoutUnit initialAlignContentOffset(LayoutUnit availableFreeSpace, EAlignContent alignContent, unsigned numberOfLines) 1188{ 1189 if (numberOfLines <= 1) 1190 return 0; 1191 if (alignContent == AlignContentFlexEnd) 1192 return availableFreeSpace; 1193 if (alignContent == AlignContentCenter) 1194 return availableFreeSpace / 2; 1195 if (alignContent == AlignContentSpaceAround) { 1196 if (availableFreeSpace > 0 && numberOfLines) 1197 return availableFreeSpace / (2 * numberOfLines); 1198 if (availableFreeSpace < 0) 1199 return availableFreeSpace / 2; 1200 } 1201 return 0; 1202} 1203 1204static LayoutUnit alignContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, EAlignContent alignContent, unsigned numberOfLines) 1205{ 1206 if (availableFreeSpace > 0 && numberOfLines > 1) { 1207 if (alignContent == AlignContentSpaceBetween) 1208 return availableFreeSpace / (numberOfLines - 1); 1209 if (alignContent == AlignContentSpaceAround || alignContent == AlignContentStretch) 1210 return availableFreeSpace / numberOfLines; 1211 } 1212 return 0; 1213} 1214 1215void RenderFlexibleBox::alignFlexLines(Vector<LineContext>& lineContexts) 1216{ 1217 // If we have a single line flexbox or a multiline line flexbox with only one flex line, 1218 // the line height is all the available space. 1219 // For flex-direction: row, this means we need to use the height, so we do this after calling updateLogicalHeight. 1220 if (lineContexts.size() == 1) { 1221 lineContexts[0].crossAxisExtent = crossAxisContentExtent(); 1222 return; 1223 } 1224 1225 if (style()->alignContent() == AlignContentFlexStart) 1226 return; 1227 1228 LayoutUnit availableCrossAxisSpace = crossAxisContentExtent(); 1229 for (size_t i = 0; i < lineContexts.size(); ++i) 1230 availableCrossAxisSpace -= lineContexts[i].crossAxisExtent; 1231 1232 RenderBox* child = m_orderIterator.first(); 1233 LayoutUnit lineOffset = initialAlignContentOffset(availableCrossAxisSpace, style()->alignContent(), lineContexts.size()); 1234 for (unsigned lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { 1235 lineContexts[lineNumber].crossAxisOffset += lineOffset; 1236 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) 1237 adjustAlignmentForChild(*child, lineOffset); 1238 1239 if (style()->alignContent() == AlignContentStretch && availableCrossAxisSpace > 0) 1240 lineContexts[lineNumber].crossAxisExtent += availableCrossAxisSpace / static_cast<unsigned>(lineContexts.size()); 1241 1242 lineOffset += alignContentSpaceBetweenChildren(availableCrossAxisSpace, style()->alignContent(), lineContexts.size()); 1243 } 1244} 1245 1246void RenderFlexibleBox::adjustAlignmentForChild(RenderBox& child, LayoutUnit delta) 1247{ 1248 if (child.isOutOfFlowPositioned()) { 1249 LayoutUnit staticInlinePosition = child.layer()->staticInlinePosition(); 1250 LayoutUnit staticBlockPosition = child.layer()->staticBlockPosition(); 1251 LayoutUnit mainAxis = isColumnFlow() ? staticBlockPosition : staticInlinePosition; 1252 LayoutUnit crossAxis = isColumnFlow() ? staticInlinePosition : staticBlockPosition; 1253 crossAxis += delta; 1254 prepareChildForPositionedLayout(child, mainAxis, crossAxis, NoFlipForRowReverse); 1255 return; 1256 } 1257 1258 setFlowAwareLocationForChild(child, flowAwareLocationForChild(child) + LayoutSize(0, delta)); 1259} 1260 1261void RenderFlexibleBox::alignChildren(const Vector<LineContext>& lineContexts) 1262{ 1263 // Keep track of the space between the baseline edge and the after edge of the box for each line. 1264 Vector<LayoutUnit> minMarginAfterBaselines; 1265 1266 RenderBox* child = m_orderIterator.first(); 1267 for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { 1268 LayoutUnit minMarginAfterBaseline = LayoutUnit::max(); 1269 LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent; 1270 LayoutUnit maxAscent = lineContexts[lineNumber].maxAscent; 1271 1272 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) { 1273 ASSERT(child); 1274 if (child->isOutOfFlowPositioned()) { 1275 if (style()->flexWrap() == FlexWrapReverse) 1276 adjustAlignmentForChild(*child, lineCrossAxisExtent); 1277 continue; 1278 } 1279 1280 if (updateAutoMarginsInCrossAxis(*child, std::max(LayoutUnit(0), availableAlignmentSpaceForChild(lineCrossAxisExtent, *child)))) 1281 continue; 1282 1283 switch (alignmentForChild(*child)) { 1284 case ItemPositionAuto: 1285 ASSERT_NOT_REACHED(); 1286 break; 1287 case ItemPositionStretch: { 1288 applyStretchAlignmentToChild(*child, lineCrossAxisExtent); 1289 // Since wrap-reverse flips cross start and cross end, strech children should be aligned with the cross end. 1290 if (style()->flexWrap() == FlexWrapReverse) 1291 adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child)); 1292 break; 1293 } 1294 case ItemPositionFlexStart: 1295 break; 1296 case ItemPositionFlexEnd: 1297 adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child)); 1298 break; 1299 case ItemPositionCenter: 1300 adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child) / 2); 1301 break; 1302 case ItemPositionBaseline: { 1303 // FIXME: If we get here in columns, we want the use the descent, except we currently can't get the ascent/descent of orthogonal children. 1304 // https://bugs.webkit.org/show_bug.cgi?id=98076 1305 LayoutUnit ascent = marginBoxAscentForChild(*child); 1306 LayoutUnit startOffset = maxAscent - ascent; 1307 adjustAlignmentForChild(*child, startOffset); 1308 1309 if (style()->flexWrap() == FlexWrapReverse) 1310 minMarginAfterBaseline = std::min(minMarginAfterBaseline, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child) - startOffset); 1311 break; 1312 } 1313 case ItemPositionLastBaseline: 1314 case ItemPositionSelfStart: 1315 case ItemPositionSelfEnd: 1316 case ItemPositionStart: 1317 case ItemPositionEnd: 1318 case ItemPositionLeft: 1319 case ItemPositionRight: 1320 // FIXME: File a bug about implementing that. The extended grammar 1321 // is not enabled by default so we shouldn't hit this codepath. 1322 ASSERT_NOT_REACHED(); 1323 break; 1324 } 1325 } 1326 minMarginAfterBaselines.append(minMarginAfterBaseline); 1327 } 1328 1329 if (style()->flexWrap() != FlexWrapReverse) 1330 return; 1331 1332 // wrap-reverse flips the cross axis start and end. For baseline alignment, this means we 1333 // need to align the after edge of baseline elements with the after edge of the flex line. 1334 child = m_orderIterator.first(); 1335 for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { 1336 LayoutUnit minMarginAfterBaseline = minMarginAfterBaselines[lineNumber]; 1337 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) { 1338 ASSERT(child); 1339 if (alignmentForChild(*child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(*child) && minMarginAfterBaseline) 1340 adjustAlignmentForChild(*child, minMarginAfterBaseline); 1341 } 1342 } 1343} 1344 1345void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox& child, LayoutUnit lineCrossAxisExtent) 1346{ 1347 if (!isColumnFlow() && child.style()->logicalHeight().isAuto()) { 1348 // FIXME: If the child has orthogonal flow, then it already has an override height set, so use it. 1349 if (!hasOrthogonalFlow(child)) { 1350 LayoutUnit heightBeforeStretching = needToStretchChildLogicalHeight(child) ? constrainedChildIntrinsicContentLogicalHeight(child) : child.logicalHeight(); 1351 LayoutUnit stretchedLogicalHeight = heightBeforeStretching + availableAlignmentSpaceForChildBeforeStretching(lineCrossAxisExtent, child); 1352 ASSERT(!child.needsLayout()); 1353 LayoutUnit desiredLogicalHeight = child.constrainLogicalHeightByMinMax(stretchedLogicalHeight, heightBeforeStretching - child.borderAndPaddingLogicalHeight()); 1354 1355 // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905. 1356 if (desiredLogicalHeight != child.logicalHeight()) { 1357 child.setOverrideLogicalContentHeight(desiredLogicalHeight - child.borderAndPaddingLogicalHeight()); 1358 child.setLogicalHeight(0); 1359 // We cache the child's intrinsic content logical height to avoid it being reset to the stretched height. 1360 // FIXME: This is fragile. RenderBoxes should be smart enough to determine their intrinsic content logical 1361 // height correctly even when there's an overrideHeight. 1362 LayoutUnit childIntrinsicContentLogicalHeight = child.intrinsicContentLogicalHeight(); 1363 child.forceChildLayout(); 1364 child.updateIntrinsicContentLogicalHeight(childIntrinsicContentLogicalHeight); 1365 } 1366 } 1367 } else if (isColumnFlow() && child.style()->logicalWidth().isAuto()) { 1368 // FIXME: If the child doesn't have orthogonal flow, then it already has an override width set, so use it. 1369 if (hasOrthogonalFlow(child)) { 1370 LayoutUnit childWidth = std::max<LayoutUnit>(0, lineCrossAxisExtent - crossAxisMarginExtentForChild(child)); 1371 childWidth = child.constrainLogicalWidthByMinMax(childWidth, childWidth, this); 1372 1373 if (childWidth != child.logicalWidth()) { 1374 child.setOverrideLogicalContentWidth(childWidth - child.borderAndPaddingLogicalWidth()); 1375 child.forceChildLayout(); 1376 } 1377 } 1378 } 1379} 1380 1381void RenderFlexibleBox::flipForRightToLeftColumn() 1382{ 1383 if (style()->isLeftToRightDirection() || !isColumnFlow()) 1384 return; 1385 1386 LayoutUnit crossExtent = crossAxisExtent(); 1387 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { 1388 if (child->isOutOfFlowPositioned()) 1389 continue; 1390 LayoutPoint location = flowAwareLocationForChild(*child); 1391 // For vertical flows, setFlowAwareLocationForChild will transpose x and y, 1392 // so using the y axis for a column cross axis extent is correct. 1393 location.setY(crossExtent - crossAxisExtentForChild(*child) - location.y()); 1394 setFlowAwareLocationForChild(*child, location); 1395 } 1396} 1397 1398void RenderFlexibleBox::flipForWrapReverse(const Vector<LineContext>& lineContexts, LayoutUnit crossAxisStartEdge) 1399{ 1400 LayoutUnit contentExtent = crossAxisContentExtent(); 1401 RenderBox* child = m_orderIterator.first(); 1402 for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { 1403 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) { 1404 ASSERT(child); 1405 LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent; 1406 LayoutUnit originalOffset = lineContexts[lineNumber].crossAxisOffset - crossAxisStartEdge; 1407 LayoutUnit newOffset = contentExtent - originalOffset - lineCrossAxisExtent; 1408 adjustAlignmentForChild(*child, newOffset - originalOffset); 1409 } 1410 } 1411} 1412 1413} 1414