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