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 43 namespace blink { 44 45 struct 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 60 struct 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 72 RenderFlexibleBox::RenderFlexibleBox(Element* element) 73 : RenderBlock(element) 74 , m_orderIterator(this) 75 , m_numberOfInFlowChildrenOnFirstLine(-1) 76 { 77 ASSERT(!childrenInline()); 78 } 79 80 RenderFlexibleBox::~RenderFlexibleBox() 81 { 82 } 83 84 RenderFlexibleBox* RenderFlexibleBox::createAnonymous(Document* document) 85 { 86 RenderFlexibleBox* renderer = new RenderFlexibleBox(0); 87 renderer->setDocumentForAnonymous(document); 88 return renderer; 89 } 90 91 const char* RenderFlexibleBox::renderName() const 92 { 93 return "RenderFlexibleBox"; 94 } 95 96 void 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 131 static 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 136 int 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 146 int 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 186 int 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 196 static 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 204 void RenderFlexibleBox::removeChild(RenderObject* child) 205 { 206 RenderBlock::removeChild(child); 207 m_intrinsicSizeAlongMainAxis.remove(child); 208 } 209 210 void 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 225 void 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 273 void 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 281 void RenderFlexibleBox::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 282 { 283 BlockPainter::paintChildrenOfFlexibleBox(*this, paintInfo, paintOffset); 284 } 285 286 void 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 300 LayoutUnit 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 312 bool 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 318 bool RenderFlexibleBox::isColumnFlow() const 319 { 320 return style()->isColumnFlexDirection(); 321 } 322 323 bool RenderFlexibleBox::isHorizontalFlow() const 324 { 325 if (isHorizontalWritingMode()) 326 return !isColumnFlow(); 327 return isColumnFlow(); 328 } 329 330 bool RenderFlexibleBox::isLeftToRightFlow() const 331 { 332 if (isColumnFlow()) 333 return style()->writingMode() == TopToBottomWritingMode || style()->writingMode() == LeftToRightWritingMode; 334 return style()->isLeftToRightDirection() ^ (style()->flexDirection() == FlowRowReverse); 335 } 336 337 bool RenderFlexibleBox::isMultiline() const 338 { 339 return style()->flexWrap() != FlexNoWrap; 340 } 341 342 Length 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 350 LayoutUnit RenderFlexibleBox::crossAxisExtentForChild(RenderBox& child) const 351 { 352 return isHorizontalFlow() ? child.height() : child.width(); 353 } 354 355 static inline LayoutUnit constrainedChildIntrinsicContentLogicalHeight(RenderBox& child) 356 { 357 LayoutUnit childIntrinsicContentLogicalHeight = child.intrinsicContentLogicalHeight(); 358 return child.constrainLogicalHeightByMinMax(childIntrinsicContentLogicalHeight + child.borderAndPaddingLogicalHeight(), childIntrinsicContentLogicalHeight); 359 } 360 361 LayoutUnit RenderFlexibleBox::childIntrinsicHeight(RenderBox& child) const 362 { 363 if (child.isHorizontalWritingMode() && needToStretchChildLogicalHeight(child)) 364 return constrainedChildIntrinsicContentLogicalHeight(child); 365 return child.height(); 366 } 367 368 LayoutUnit RenderFlexibleBox::childIntrinsicWidth(RenderBox& child) const 369 { 370 if (!child.isHorizontalWritingMode() && needToStretchChildLogicalHeight(child)) 371 return constrainedChildIntrinsicContentLogicalHeight(child); 372 return child.width(); 373 } 374 375 LayoutUnit RenderFlexibleBox::crossAxisIntrinsicExtentForChild(RenderBox& child) const 376 { 377 return isHorizontalFlow() ? childIntrinsicHeight(child) : childIntrinsicWidth(child); 378 } 379 380 LayoutUnit RenderFlexibleBox::mainAxisExtentForChild(RenderBox& child) const 381 { 382 return isHorizontalFlow() ? child.width() : child.height(); 383 } 384 385 LayoutUnit RenderFlexibleBox::crossAxisExtent() const 386 { 387 return isHorizontalFlow() ? height() : width(); 388 } 389 390 LayoutUnit RenderFlexibleBox::mainAxisExtent() const 391 { 392 return isHorizontalFlow() ? width() : height(); 393 } 394 395 LayoutUnit RenderFlexibleBox::crossAxisContentExtent() const 396 { 397 return isHorizontalFlow() ? contentHeight() : contentWidth(); 398 } 399 400 LayoutUnit 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 414 LayoutUnit 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 427 WritingMode 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 445 LayoutUnit RenderFlexibleBox::flowAwareBorderStart() const 446 { 447 if (isHorizontalFlow()) 448 return isLeftToRightFlow() ? borderLeft() : borderRight(); 449 return isLeftToRightFlow() ? borderTop() : borderBottom(); 450 } 451 452 LayoutUnit RenderFlexibleBox::flowAwareBorderEnd() const 453 { 454 if (isHorizontalFlow()) 455 return isLeftToRightFlow() ? borderRight() : borderLeft(); 456 return isLeftToRightFlow() ? borderBottom() : borderTop(); 457 } 458 459 LayoutUnit 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 475 LayoutUnit 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 491 LayoutUnit RenderFlexibleBox::flowAwarePaddingStart() const 492 { 493 if (isHorizontalFlow()) 494 return isLeftToRightFlow() ? paddingLeft() : paddingRight(); 495 return isLeftToRightFlow() ? paddingTop() : paddingBottom(); 496 } 497 498 LayoutUnit RenderFlexibleBox::flowAwarePaddingEnd() const 499 { 500 if (isHorizontalFlow()) 501 return isLeftToRightFlow() ? paddingRight() : paddingLeft(); 502 return isLeftToRightFlow() ? paddingBottom() : paddingTop(); 503 } 504 505 LayoutUnit 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 521 LayoutUnit 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 537 LayoutUnit 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 544 LayoutUnit 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 551 LayoutUnit 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 567 LayoutUnit RenderFlexibleBox::crossAxisMarginExtentForChild(RenderBox& child) const 568 { 569 return isHorizontalFlow() ? child.marginHeight() : child.marginWidth(); 570 } 571 572 LayoutUnit RenderFlexibleBox::crossAxisScrollbarExtent() const 573 { 574 return isHorizontalFlow() ? horizontalScrollbarHeight() : verticalScrollbarWidth(); 575 } 576 577 LayoutUnit RenderFlexibleBox::crossAxisScrollbarExtentForChild(RenderBox& child) const 578 { 579 return isHorizontalFlow() ? child.horizontalScrollbarHeight() : child.verticalScrollbarWidth(); 580 } 581 582 LayoutPoint RenderFlexibleBox::flowAwareLocationForChild(RenderBox& child) const 583 { 584 return isHorizontalFlow() ? child.location() : child.location().transposedPoint(); 585 } 586 587 void RenderFlexibleBox::setFlowAwareLocationForChild(RenderBox& child, const LayoutPoint& location) 588 { 589 if (isHorizontalFlow()) 590 child.setLocation(location); 591 else 592 child.setLocation(location.transposedPoint()); 593 } 594 595 LayoutUnit RenderFlexibleBox::mainAxisBorderAndPaddingExtentForChild(RenderBox& child) const 596 { 597 return isHorizontalFlow() ? child.borderAndPaddingWidth() : child.borderAndPaddingHeight(); 598 } 599 600 static inline bool preferredMainAxisExtentDependsOnLayout(const Length& flexBasis, bool hasInfiniteLineLength) 601 { 602 return flexBasis.isAuto() || (flexBasis.isPercent() && hasInfiniteLineLength); 603 } 604 605 bool RenderFlexibleBox::childPreferredMainAxisContentExtentRequiresLayout(RenderBox& child, bool hasInfiniteLineLength) const 606 { 607 return preferredMainAxisExtentDependsOnLayout(flexBasisForChild(child), hasInfiniteLineLength) && hasOrthogonalFlow(child); 608 } 609 610 LayoutUnit 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 637 void 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 680 LayoutUnit 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 711 void 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 728 bool 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 735 LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChild(LayoutUnit lineCrossAxisExtent, RenderBox& child) 736 { 737 ASSERT(!child.isOutOfFlowPositioned()); 738 LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child); 739 return lineCrossAxisExtent - childCrossExtent; 740 } 741 742 LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChildBeforeStretching(LayoutUnit lineCrossAxisExtent, RenderBox& child) 743 { 744 ASSERT(!child.isOutOfFlowPositioned()); 745 LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + crossAxisIntrinsicExtentForChild(child); 746 return lineCrossAxisExtent - childCrossExtent; 747 } 748 749 bool 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 803 LayoutUnit 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 811 LayoutUnit 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 819 void 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 841 LayoutUnit 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 857 bool 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 898 void 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. 912 bool 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 960 static 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 975 static 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 986 void 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 994 void 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 1012 ItemPosition 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 1029 size_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 1040 void 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 1058 bool RenderFlexibleBox::needToStretchChildLogicalHeight(RenderBox& child) const 1059 { 1060 if (alignmentForChild(child) != ItemPositionStretch) 1061 return false; 1062 1063 return isHorizontalFlow() && child.style()->height().isAuto(); 1064 } 1065 1066 void 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 1157 void 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 1187 static 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 1204 static 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 1215 void 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 1246 void 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 1261 void 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 1345 void 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 1381 void 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 1398 void 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