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