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