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