2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "RenderFlexibleBox.h"
34 #include "LayoutRepainter.h"
35 #include "RenderLayer.h"
36 #include "RenderView.h"
38 #include <wtf/MathExtras.h>
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)
51 LayoutUnit crossAxisOffset;
52 LayoutUnit crossAxisExtent;
53 size_t numberOfChildren;
57 struct RenderFlexibleBox::Violation {
58 Violation(RenderBox& child, LayoutUnit childSize)
60 , childSize(childSize)
69 RenderFlexibleBox::RenderFlexibleBox(Element& element, PassRef<RenderStyle> style)
70 : RenderBlock(element, std::move(style), 0)
71 , m_numberOfInFlowChildrenOnFirstLine(-1)
73 setChildrenInline(false); // All of our children must be block-level.
76 RenderFlexibleBox::RenderFlexibleBox(Document& document, PassRef<RenderStyle> style)
77 : RenderBlock(document, std::move(style), 0)
78 , m_numberOfInFlowChildrenOnFirstLine(-1)
80 setChildrenInline(false); // All of our children must be block-level.
83 RenderFlexibleBox::~RenderFlexibleBox()
87 const char* RenderFlexibleBox::renderName() const
89 return "RenderFlexibleBox";
92 void RenderFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
94 // FIXME: We're ignoring flex-basis here and we shouldn't. We can't start honoring it though until
95 // the flex shorthand stops setting it to 0.
96 // See https://bugs.webkit.org/show_bug.cgi?id=116117,
97 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
98 if (child->isOutOfFlowPositioned())
101 LayoutUnit margin = marginIntrinsicLogicalWidthForChild(*child);
102 bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode();
103 LayoutUnit minPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->minPreferredLogicalWidth();
104 LayoutUnit maxPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->maxPreferredLogicalWidth();
105 minPreferredLogicalWidth += margin;
106 maxPreferredLogicalWidth += margin;
107 if (!isColumnFlow()) {
108 maxLogicalWidth += maxPreferredLogicalWidth;
110 // For multiline, the min preferred width is if you put a break between each item.
111 minLogicalWidth = std::max(minLogicalWidth, minPreferredLogicalWidth);
113 minLogicalWidth += minPreferredLogicalWidth;
115 minLogicalWidth = std::max(minPreferredLogicalWidth, minLogicalWidth);
117 // For multiline, the max preferred width is if you never break between items.
118 maxLogicalWidth += maxPreferredLogicalWidth;
120 maxLogicalWidth = std::max(maxPreferredLogicalWidth, maxLogicalWidth);
124 maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth);
126 LayoutUnit scrollbarWidth = instrinsicScrollbarLogicalWidth();
127 maxLogicalWidth += scrollbarWidth;
128 minLogicalWidth += scrollbarWidth;
131 void RenderFlexibleBox::computePreferredLogicalWidths()
133 ASSERT(preferredLogicalWidthsDirty());
135 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0;
137 const RenderStyle& styleToUse = style();
138 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for width.
139 if (styleToUse.logicalWidth().isFixed() && styleToUse.logicalWidth().value() > 0)
140 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalWidth().value());
142 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
144 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for min-width.
145 if (styleToUse.logicalMinWidth().isFixed() && styleToUse.logicalMinWidth().value() > 0) {
146 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value()));
147 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value()));
150 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for maxWidth.
151 if (styleToUse.logicalMaxWidth().isFixed()) {
152 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value()));
153 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value()));
156 LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth();
157 m_minPreferredLogicalWidth += borderAndPadding;
158 m_maxPreferredLogicalWidth += borderAndPadding;
160 setPreferredLogicalWidthsDirty(false);
163 static int synthesizedBaselineFromContentBox(const RenderBox& box, LineDirectionMode direction)
165 return direction == HorizontalLine ? box.borderTop() + box.paddingTop() + box.contentHeight() : box.borderRight() + box.paddingRight() + box.contentWidth();
168 int RenderFlexibleBox::baselinePosition(FontBaseline, bool, LineDirectionMode direction, LinePositionMode) const
170 int baseline = firstLineBaseline();
172 baseline = synthesizedBaselineFromContentBox(*this, direction);
174 int marginAscent = direction == HorizontalLine ? marginTop() : marginRight();
175 return baseline + marginAscent;
178 int RenderFlexibleBox::firstLineBaseline() const
180 if (isWritingModeRoot() || m_numberOfInFlowChildrenOnFirstLine <= 0)
182 RenderBox* baselineChild = 0;
184 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
185 if (child->isOutOfFlowPositioned())
187 if (alignmentForChild(*child) == AlignBaseline && !hasAutoMarginsInCrossAxis(*child)) {
188 baselineChild = child;
192 baselineChild = child;
195 if (childNumber == m_numberOfInFlowChildrenOnFirstLine)
202 if (!isColumnFlow() && hasOrthogonalFlow(*baselineChild))
203 return crossAxisExtentForChild(*baselineChild) + baselineChild->logicalTop();
204 if (isColumnFlow() && !hasOrthogonalFlow(*baselineChild))
205 return mainAxisExtentForChild(*baselineChild) + baselineChild->logicalTop();
207 int baseline = baselineChild->firstLineBaseline();
208 if (baseline == -1) {
209 // FIXME: We should pass |direction| into firstLineBoxBaseline and stop bailing out if we're a writing mode root.
210 // This would also fix some cases where the flexbox is orthogonal to its container.
211 LineDirectionMode direction = isHorizontalWritingMode() ? HorizontalLine : VerticalLine;
212 return synthesizedBaselineFromContentBox(*baselineChild, direction) + baselineChild->logicalTop();
215 return baseline + baselineChild->logicalTop();
218 int RenderFlexibleBox::inlineBlockBaseline(LineDirectionMode direction) const
220 int baseline = firstLineBaseline();
224 int marginAscent = direction == HorizontalLine ? marginTop() : marginRight();
225 return synthesizedBaselineFromContentBox(*this, direction) + marginAscent;
228 static EAlignItems resolveAlignment(const RenderStyle* parentStyle, const RenderStyle* childStyle)
230 EAlignItems align = childStyle->alignSelf();
231 if (align == AlignAuto)
232 align = parentStyle->alignItems();
236 void RenderFlexibleBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
238 RenderBlock::styleDidChange(diff, oldStyle);
240 if (oldStyle && oldStyle->alignItems() == AlignStretch && diff == StyleDifferenceLayout) {
241 // Flex items that were previously stretching need to be relayed out so we can compute new available cross axis space.
242 // This is only necessary for stretching since other alignment values don't change the size of the box.
243 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
244 EAlignItems previousAlignment = resolveAlignment(oldStyle, &child->style());
245 if (previousAlignment == AlignStretch && previousAlignment != resolveAlignment(&style(), &child->style()))
246 child->setChildNeedsLayout(MarkOnlyThis);
251 void RenderFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit)
253 ASSERT(needsLayout());
255 if (!relayoutChildren && simplifiedLayout())
258 LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
260 if (updateLogicalWidthAndColumnWidth())
261 relayoutChildren = true;
263 LayoutUnit previousHeight = logicalHeight();
264 setLogicalHeight(borderAndPaddingLogicalHeight() + scrollbarLogicalHeight());
266 LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
268 preparePaginationBeforeBlockLayout(relayoutChildren);
270 m_numberOfInFlowChildrenOnFirstLine = -1;
272 RenderBlock::startDelayUpdateScrollInfo();
274 dirtyForLayoutFromPercentageHeightDescendants();
276 prepareOrderIteratorAndMargins();
278 ChildFrameRects oldChildRects;
279 appendChildFrameRects(oldChildRects);
280 Vector<LineContext> lineContexts;
281 layoutFlexItems(relayoutChildren, lineContexts);
283 updateLogicalHeight();
284 repositionLogicalHeightDependentFlexItems(lineContexts);
286 RenderBlock::finishDelayUpdateScrollInfo();
288 if (logicalHeight() != previousHeight)
289 relayoutChildren = true;
291 layoutPositionedObjects(relayoutChildren || isRoot());
293 repaintChildrenDuringLayoutIfMoved(oldChildRects);
294 // FIXME: css3/flexbox/repaint-rtl-column.html seems to repaint more overflow than it needs to.
295 computeOverflow(clientLogicalBottomAfterRepositioning());
298 updateLayerTransform();
300 // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
301 // we overflow or not.
302 updateScrollInfoAfterLayout();
304 repainter.repaintAfterLayout();
309 void RenderFlexibleBox::appendChildFrameRects(ChildFrameRects& childFrameRects)
311 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
312 if (!child->isOutOfFlowPositioned())
313 childFrameRects.append(child->frameRect());
317 void RenderFlexibleBox::repaintChildrenDuringLayoutIfMoved(const ChildFrameRects& oldChildRects)
319 size_t childIndex = 0;
320 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
321 if (child->isOutOfFlowPositioned())
324 // If the child moved, we have to repaint it as well as any floating/positioned
325 // descendants. An exception is if we need a layout. In this case, we know we're going to
326 // repaint ourselves (and the child) anyway.
327 if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
328 child->repaintDuringLayoutIfMoved(oldChildRects[childIndex]);
331 ASSERT(childIndex == oldChildRects.size());
334 void RenderFlexibleBox::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
336 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
337 if (!paintChild(*child, paintInfo, paintOffset, paintInfoForChild, usePrintRect))
342 void RenderFlexibleBox::repositionLogicalHeightDependentFlexItems(Vector<LineContext>& lineContexts)
344 LayoutUnit crossAxisStartEdge = lineContexts.isEmpty() ? LayoutUnit() : lineContexts[0].crossAxisOffset;
345 alignFlexLines(lineContexts);
347 // If we have a single line flexbox, the line height is all the available space.
348 // For flex-direction: row, this means we need to use the height, so we do this after calling updateLogicalHeight.
349 if (!isMultiline() && lineContexts.size() == 1)
350 lineContexts[0].crossAxisExtent = crossAxisContentExtent();
351 alignChildren(lineContexts);
353 if (style().flexWrap() == FlexWrapReverse)
354 flipForWrapReverse(lineContexts, crossAxisStartEdge);
356 // direction:rtl + flex-direction:column means the cross-axis direction is flipped.
357 flipForRightToLeftColumn();
360 LayoutUnit RenderFlexibleBox::clientLogicalBottomAfterRepositioning()
362 LayoutUnit maxChildLogicalBottom = 0;
363 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
364 if (child->isOutOfFlowPositioned())
366 LayoutUnit childLogicalBottom = logicalTopForChild(*child) + logicalHeightForChild(*child) + marginAfterForChild(*child);
367 maxChildLogicalBottom = std::max(maxChildLogicalBottom, childLogicalBottom);
369 return std::max(clientLogicalBottom(), maxChildLogicalBottom);
372 bool RenderFlexibleBox::hasOrthogonalFlow(RenderBox& child) const
374 // FIXME: If the child is a flexbox, then we need to check isHorizontalFlow.
375 return isHorizontalFlow() != child.isHorizontalWritingMode();
378 bool RenderFlexibleBox::isColumnFlow() const
380 return style().isColumnFlexDirection();
383 bool RenderFlexibleBox::isHorizontalFlow() const
385 if (isHorizontalWritingMode())
386 return !isColumnFlow();
387 return isColumnFlow();
390 bool RenderFlexibleBox::isLeftToRightFlow() const
393 return style().writingMode() == TopToBottomWritingMode || style().writingMode() == LeftToRightWritingMode;
394 return style().isLeftToRightDirection() ^ (style().flexDirection() == FlowRowReverse);
397 bool RenderFlexibleBox::isMultiline() const
399 return style().flexWrap() != FlexNoWrap;
402 Length RenderFlexibleBox::flexBasisForChild(RenderBox& child) const
404 Length flexLength = child.style().flexBasis();
405 if (flexLength.isAuto())
406 flexLength = isHorizontalFlow() ? child.style().width() : child.style().height();
410 void RenderFlexibleBox::setCrossAxisExtent(LayoutUnit extent)
412 if (isHorizontalFlow())
418 LayoutUnit RenderFlexibleBox::crossAxisExtentForChild(RenderBox& child) const
420 return isHorizontalFlow() ? child.height() : child.width();
423 LayoutUnit RenderFlexibleBox::mainAxisExtentForChild(RenderBox& child) const
425 return isHorizontalFlow() ? child.width() : child.height();
428 LayoutUnit RenderFlexibleBox::crossAxisExtent() const
430 return isHorizontalFlow() ? height() : width();
433 LayoutUnit RenderFlexibleBox::mainAxisExtent() const
435 return isHorizontalFlow() ? width() : height();
438 LayoutUnit RenderFlexibleBox::crossAxisContentExtent() const
440 return isHorizontalFlow() ? contentHeight() : contentWidth();
443 LayoutUnit RenderFlexibleBox::mainAxisContentExtent(LayoutUnit contentLogicalHeight)
445 if (isColumnFlow()) {
446 LogicalExtentComputedValues computedValues;
447 LayoutUnit borderPaddingAndScrollbar = borderAndPaddingLogicalHeight() + scrollbarLogicalHeight();
448 if (contentLogicalHeight > LayoutUnit::max() - borderPaddingAndScrollbar)
449 contentLogicalHeight -= borderPaddingAndScrollbar;
450 LayoutUnit borderBoxLogicalHeight = contentLogicalHeight + borderPaddingAndScrollbar;
451 computeLogicalHeight(borderBoxLogicalHeight, logicalTop(), computedValues);
452 if (computedValues.m_extent == LayoutUnit::max())
453 return computedValues.m_extent;
454 return std::max(LayoutUnit::fromPixel(0), computedValues.m_extent - borderPaddingAndScrollbar);
456 return contentLogicalWidth();
459 LayoutUnit RenderFlexibleBox::computeMainAxisExtentForChild(RenderBox& child, SizeType sizeType, const Length& size)
461 // FIXME: This is wrong for orthogonal flows. It should use the flexbox's writing-mode, not the child's in order
462 // to figure out the logical height/width.
463 // FIXME: This is wrong if the height is set to an intrinsic keyword value. computeContentLogicalHeight will return -1.
464 // Instead, we need to layout the child an get the appropriate height value.
465 // https://bugs.webkit.org/show_bug.cgi?id=113610
467 return child.computeContentLogicalHeight(size);
468 // FIXME: Figure out how this should work for regions and pass in the appropriate values.
469 RenderRegion* region = 0;
470 return child.computeLogicalWidthInRegionUsing(sizeType, size, contentLogicalWidth(), this, region) - child.borderAndPaddingLogicalWidth();
473 WritingMode RenderFlexibleBox::transformedWritingMode() const
475 WritingMode mode = style().writingMode();
480 case TopToBottomWritingMode:
481 case BottomToTopWritingMode:
482 return style().isLeftToRightDirection() ? LeftToRightWritingMode : RightToLeftWritingMode;
483 case LeftToRightWritingMode:
484 case RightToLeftWritingMode:
485 return style().isLeftToRightDirection() ? TopToBottomWritingMode : BottomToTopWritingMode;
487 ASSERT_NOT_REACHED();
488 return TopToBottomWritingMode;
491 LayoutUnit RenderFlexibleBox::flowAwareBorderStart() const
493 if (isHorizontalFlow())
494 return isLeftToRightFlow() ? borderLeft() : borderRight();
495 return isLeftToRightFlow() ? borderTop() : borderBottom();
498 LayoutUnit RenderFlexibleBox::flowAwareBorderEnd() const
500 if (isHorizontalFlow())
501 return isLeftToRightFlow() ? borderRight() : borderLeft();
502 return isLeftToRightFlow() ? borderBottom() : borderTop();
505 LayoutUnit RenderFlexibleBox::flowAwareBorderBefore() const
507 switch (transformedWritingMode()) {
508 case TopToBottomWritingMode:
510 case BottomToTopWritingMode:
511 return borderBottom();
512 case LeftToRightWritingMode:
514 case RightToLeftWritingMode:
515 return borderRight();
517 ASSERT_NOT_REACHED();
521 LayoutUnit RenderFlexibleBox::flowAwareBorderAfter() const
523 switch (transformedWritingMode()) {
524 case TopToBottomWritingMode:
525 return borderBottom();
526 case BottomToTopWritingMode:
528 case LeftToRightWritingMode:
529 return borderRight();
530 case RightToLeftWritingMode:
533 ASSERT_NOT_REACHED();
537 LayoutUnit RenderFlexibleBox::flowAwarePaddingStart() const
539 if (isHorizontalFlow())
540 return isLeftToRightFlow() ? paddingLeft() : paddingRight();
541 return isLeftToRightFlow() ? paddingTop() : paddingBottom();
544 LayoutUnit RenderFlexibleBox::flowAwarePaddingEnd() const
546 if (isHorizontalFlow())
547 return isLeftToRightFlow() ? paddingRight() : paddingLeft();
548 return isLeftToRightFlow() ? paddingBottom() : paddingTop();
551 LayoutUnit RenderFlexibleBox::flowAwarePaddingBefore() const
553 switch (transformedWritingMode()) {
554 case TopToBottomWritingMode:
556 case BottomToTopWritingMode:
557 return paddingBottom();
558 case LeftToRightWritingMode:
559 return paddingLeft();
560 case RightToLeftWritingMode:
561 return paddingRight();
563 ASSERT_NOT_REACHED();
567 LayoutUnit RenderFlexibleBox::flowAwarePaddingAfter() const
569 switch (transformedWritingMode()) {
570 case TopToBottomWritingMode:
571 return paddingBottom();
572 case BottomToTopWritingMode:
574 case LeftToRightWritingMode:
575 return paddingRight();
576 case RightToLeftWritingMode:
577 return paddingLeft();
579 ASSERT_NOT_REACHED();
583 LayoutUnit RenderFlexibleBox::flowAwareMarginStartForChild(RenderBox& child) const
585 if (isHorizontalFlow())
586 return isLeftToRightFlow() ? child.marginLeft() : child.marginRight();
587 return isLeftToRightFlow() ? child.marginTop() : child.marginBottom();
590 LayoutUnit RenderFlexibleBox::flowAwareMarginEndForChild(RenderBox& child) const
592 if (isHorizontalFlow())
593 return isLeftToRightFlow() ? child.marginRight() : child.marginLeft();
594 return isLeftToRightFlow() ? child.marginBottom() : child.marginTop();
597 LayoutUnit RenderFlexibleBox::flowAwareMarginBeforeForChild(RenderBox& child) const
599 switch (transformedWritingMode()) {
600 case TopToBottomWritingMode:
601 return child.marginTop();
602 case BottomToTopWritingMode:
603 return child.marginBottom();
604 case LeftToRightWritingMode:
605 return child.marginLeft();
606 case RightToLeftWritingMode:
607 return child.marginRight();
609 ASSERT_NOT_REACHED();
613 LayoutUnit RenderFlexibleBox::flowAwareMarginAfterForChild(RenderBox& child) const
615 switch (transformedWritingMode()) {
616 case TopToBottomWritingMode:
617 return child.marginBottom();
618 case BottomToTopWritingMode:
619 return child.marginTop();
620 case LeftToRightWritingMode:
621 return child.marginRight();
622 case RightToLeftWritingMode:
623 return child.marginLeft();
625 ASSERT_NOT_REACHED();
626 return marginBottom();
629 LayoutUnit RenderFlexibleBox::crossAxisMarginExtentForChild(RenderBox& child) const
631 return isHorizontalFlow() ? child.verticalMarginExtent() : child.horizontalMarginExtent();
634 LayoutUnit RenderFlexibleBox::crossAxisScrollbarExtent() const
636 return isHorizontalFlow() ? horizontalScrollbarHeight() : verticalScrollbarWidth();
639 LayoutPoint RenderFlexibleBox::flowAwareLocationForChild(RenderBox& child) const
641 return isHorizontalFlow() ? child.location() : child.location().transposedPoint();
644 void RenderFlexibleBox::setFlowAwareLocationForChild(RenderBox& child, const LayoutPoint& location)
646 if (isHorizontalFlow())
647 child.setLocation(location);
649 child.setLocation(location.transposedPoint());
652 LayoutUnit RenderFlexibleBox::mainAxisBorderAndPaddingExtentForChild(RenderBox& child) const
654 return isHorizontalFlow() ? child.horizontalBorderAndPaddingExtent() : child.verticalBorderAndPaddingExtent();
657 LayoutUnit RenderFlexibleBox::mainAxisScrollbarExtentForChild(RenderBox& child) const
659 return isHorizontalFlow() ? child.verticalScrollbarWidth() : child.horizontalScrollbarHeight();
662 LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox& child, bool hasInfiniteLineLength)
664 bool hasOverrideSize = child.hasOverrideWidth() || child.hasOverrideHeight();
666 child.clearOverrideSize();
668 Length flexBasis = flexBasisForChild(child);
669 if (flexBasis.isAuto() || (flexBasis.isFixed() && !flexBasis.value() && hasInfiniteLineLength)) {
670 if (hasOrthogonalFlow(child)) {
672 child.setChildNeedsLayout(MarkOnlyThis);
673 child.layoutIfNeeded();
675 LayoutUnit mainAxisExtent = hasOrthogonalFlow(child) ? child.logicalHeight() : child.maxPreferredLogicalWidth();
676 ASSERT(mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child) >= 0);
677 return mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child);
679 return std::max(LayoutUnit::fromPixel(0), computeMainAxisExtentForChild(child, MainOrPreferredSize, flexBasis));
682 void RenderFlexibleBox::layoutFlexItems(bool relayoutChildren, Vector<LineContext>& lineContexts)
684 OrderedFlexItemList orderedChildren;
685 LayoutUnit preferredMainAxisExtent;
686 double totalFlexGrow;
687 double totalWeightedFlexShrink;
688 LayoutUnit minMaxAppliedMainAxisExtent;
690 m_orderIterator.first();
691 LayoutUnit crossAxisOffset = flowAwareBorderBefore() + flowAwarePaddingBefore();
692 bool hasInfiniteLineLength = false;
693 while (computeNextFlexLine(orderedChildren, preferredMainAxisExtent, totalFlexGrow, totalWeightedFlexShrink, minMaxAppliedMainAxisExtent, hasInfiniteLineLength)) {
694 LayoutUnit availableFreeSpace = mainAxisContentExtent(preferredMainAxisExtent) - preferredMainAxisExtent;
695 FlexSign flexSign = (minMaxAppliedMainAxisExtent < preferredMainAxisExtent + availableFreeSpace) ? PositiveFlexibility : NegativeFlexibility;
696 InflexibleFlexItemSize inflexibleItems;
697 Vector<LayoutUnit> childSizes;
698 while (!resolveFlexibleLengths(flexSign, orderedChildren, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, childSizes, hasInfiniteLineLength)) {
699 ASSERT(totalFlexGrow >= 0 && totalWeightedFlexShrink >= 0);
700 ASSERT(inflexibleItems.size() > 0);
703 layoutAndPlaceChildren(crossAxisOffset, orderedChildren, childSizes, availableFreeSpace, relayoutChildren, lineContexts);
705 if (hasLineIfEmpty()) {
706 // Even if computeNextFlexLine returns true, the flexbox might not have
707 // a line because all our children might be out of flow positioned.
708 // Instead of just checking if we have a line, make sure the flexbox
709 // has at least a line's worth of height to cover this case.
710 LayoutUnit minHeight = borderAndPaddingLogicalHeight()
711 + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)
712 + scrollbarLogicalHeight();
713 if (height() < minHeight)
714 setLogicalHeight(minHeight);
718 LayoutUnit RenderFlexibleBox::autoMarginOffsetInMainAxis(const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace)
720 if (availableFreeSpace <= 0)
723 int numberOfAutoMargins = 0;
724 bool isHorizontal = isHorizontalFlow();
725 for (size_t i = 0; i < children.size(); ++i) {
726 RenderBox* child = children[i];
727 if (child->isOutOfFlowPositioned())
730 if (child->style().marginLeft().isAuto())
731 ++numberOfAutoMargins;
732 if (child->style().marginRight().isAuto())
733 ++numberOfAutoMargins;
735 if (child->style().marginTop().isAuto())
736 ++numberOfAutoMargins;
737 if (child->style().marginBottom().isAuto())
738 ++numberOfAutoMargins;
741 if (!numberOfAutoMargins)
744 LayoutUnit sizeOfAutoMargin = availableFreeSpace / numberOfAutoMargins;
745 availableFreeSpace = 0;
746 return sizeOfAutoMargin;
749 void RenderFlexibleBox::updateAutoMarginsInMainAxis(RenderBox& child, LayoutUnit autoMarginOffset)
751 ASSERT(autoMarginOffset >= 0);
753 if (isHorizontalFlow()) {
754 if (child.style().marginLeft().isAuto())
755 child.setMarginLeft(autoMarginOffset);
756 if (child.style().marginRight().isAuto())
757 child.setMarginRight(autoMarginOffset);
759 if (child.style().marginTop().isAuto())
760 child.setMarginTop(autoMarginOffset);
761 if (child.style().marginBottom().isAuto())
762 child.setMarginBottom(autoMarginOffset);
766 bool RenderFlexibleBox::hasAutoMarginsInCrossAxis(RenderBox& child) const
768 if (isHorizontalFlow())
769 return child.style().marginTop().isAuto() || child.style().marginBottom().isAuto();
770 return child.style().marginLeft().isAuto() || child.style().marginRight().isAuto();
773 LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChild(LayoutUnit lineCrossAxisExtent, RenderBox& child)
775 ASSERT(!child.isOutOfFlowPositioned());
776 LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child);
777 return lineCrossAxisExtent - childCrossExtent;
780 bool RenderFlexibleBox::updateAutoMarginsInCrossAxis(RenderBox& child, LayoutUnit availableAlignmentSpace)
782 ASSERT(!child.isOutOfFlowPositioned());
783 ASSERT(availableAlignmentSpace >= 0);
785 bool isHorizontal = isHorizontalFlow();
786 Length start = isHorizontal ? child.style().marginTop() : child.style().marginLeft();
787 Length end = isHorizontal ? child.style().marginBottom() : child.style().marginRight();
788 if (start.isAuto() && end.isAuto()) {
789 adjustAlignmentForChild(child, availableAlignmentSpace / 2);
791 child.setMarginTop(availableAlignmentSpace / 2);
792 child.setMarginBottom(availableAlignmentSpace / 2);
794 child.setMarginLeft(availableAlignmentSpace / 2);
795 child.setMarginRight(availableAlignmentSpace / 2);
799 if (start.isAuto()) {
800 adjustAlignmentForChild(child, availableAlignmentSpace);
802 child.setMarginTop(availableAlignmentSpace);
804 child.setMarginLeft(availableAlignmentSpace);
809 child.setMarginBottom(availableAlignmentSpace);
811 child.setMarginRight(availableAlignmentSpace);
817 LayoutUnit RenderFlexibleBox::marginBoxAscentForChild(RenderBox& child)
819 LayoutUnit ascent = child.firstLineBaseline();
821 ascent = crossAxisExtentForChild(child);
822 return ascent + flowAwareMarginBeforeForChild(child);
825 LayoutUnit RenderFlexibleBox::computeChildMarginValue(const Length& margin)
827 // When resolving the margins, we use the content size for resolving percent and calc (for percents in calc expressions) margins.
828 // Fortunately, percent margins are always computed with respect to the block's width, even for margin-top and margin-bottom.
829 LayoutUnit availableSize = contentLogicalWidth();
830 return minimumValueForLength(margin, availableSize);
833 void RenderFlexibleBox::prepareOrderIteratorAndMargins()
835 OrderIteratorPopulator populator(m_orderIterator);
837 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
838 populator.collectChild(*child);
840 if (child->isOutOfFlowPositioned())
843 // Before running the flex algorithm, 'auto' has a margin of 0.
844 // Also, if we're not auto sizing, we don't do a layout that computes the start/end margins.
845 if (isHorizontalFlow()) {
846 child->setMarginLeft(computeChildMarginValue(child->style().marginLeft()));
847 child->setMarginRight(computeChildMarginValue(child->style().marginRight()));
849 child->setMarginTop(computeChildMarginValue(child->style().marginTop()));
850 child->setMarginBottom(computeChildMarginValue(child->style().marginBottom()));
855 LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(RenderBox& child, LayoutUnit childSize)
857 Length max = isHorizontalFlow() ? child.style().maxWidth() : child.style().maxHeight();
858 if (max.isSpecifiedOrIntrinsic()) {
859 LayoutUnit maxExtent = computeMainAxisExtentForChild(child, MaxSize, max);
860 if (maxExtent != -1 && childSize > maxExtent)
861 childSize = maxExtent;
864 Length min = isHorizontalFlow() ? child.style().minWidth() : child.style().minHeight();
865 LayoutUnit minExtent = 0;
866 if (min.isSpecifiedOrIntrinsic())
867 minExtent = computeMainAxisExtentForChild(child, MinSize, min);
868 return std::max(childSize, minExtent);
871 bool RenderFlexibleBox::computeNextFlexLine(OrderedFlexItemList& orderedChildren, LayoutUnit& preferredMainAxisExtent, double& totalFlexGrow, double& totalWeightedFlexShrink, LayoutUnit& minMaxAppliedMainAxisExtent, bool& hasInfiniteLineLength)
873 orderedChildren.clear();
874 preferredMainAxisExtent = 0;
875 totalFlexGrow = totalWeightedFlexShrink = 0;
876 minMaxAppliedMainAxisExtent = 0;
878 if (!m_orderIterator.currentChild())
881 LayoutUnit lineBreakLength = mainAxisContentExtent(LayoutUnit::max());
882 hasInfiniteLineLength = lineBreakLength == LayoutUnit::max();
884 bool lineHasInFlowItem = false;
886 for (RenderBox* child = m_orderIterator.currentChild(); child; child = m_orderIterator.next()) {
887 if (child->isOutOfFlowPositioned()) {
888 orderedChildren.append(child);
892 LayoutUnit childMainAxisExtent = preferredMainAxisContentExtentForChild(*child, hasInfiniteLineLength);
893 LayoutUnit childMainAxisMarginBoxExtent = mainAxisBorderAndPaddingExtentForChild(*child) + childMainAxisExtent;
894 childMainAxisMarginBoxExtent += isHorizontalFlow() ? child->horizontalMarginExtent() : child->verticalMarginExtent();
896 if (isMultiline() && preferredMainAxisExtent + childMainAxisMarginBoxExtent > lineBreakLength && lineHasInFlowItem)
898 orderedChildren.append(child);
899 lineHasInFlowItem = true;
900 preferredMainAxisExtent += childMainAxisMarginBoxExtent;
901 totalFlexGrow += child->style().flexGrow();
902 totalWeightedFlexShrink += child->style().flexShrink() * childMainAxisExtent;
904 LayoutUnit childMinMaxAppliedMainAxisExtent = adjustChildSizeForMinAndMax(*child, childMainAxisExtent);
905 minMaxAppliedMainAxisExtent += childMinMaxAppliedMainAxisExtent - childMainAxisExtent + childMainAxisMarginBoxExtent;
910 void RenderFlexibleBox::freezeViolations(const Vector<Violation>& violations, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, bool hasInfiniteLineLength)
912 for (size_t i = 0; i < violations.size(); ++i) {
913 RenderBox& child = violations[i].child;
914 LayoutUnit childSize = violations[i].childSize;
915 LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength);
916 availableFreeSpace -= childSize - preferredChildSize;
917 totalFlexGrow -= child.style().flexGrow();
918 totalWeightedFlexShrink -= child.style().flexShrink() * preferredChildSize;
919 inflexibleItems.set(&child, childSize);
923 // Returns true if we successfully ran the algorithm and sized the flex items.
924 bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, Vector<LayoutUnit>& childSizes, bool hasInfiniteLineLength)
927 LayoutUnit totalViolation = 0;
928 LayoutUnit usedFreeSpace = 0;
929 Vector<Violation> minViolations;
930 Vector<Violation> maxViolations;
931 for (size_t i = 0; i < children.size(); ++i) {
932 RenderBox& child = *children[i];
933 if (child.isOutOfFlowPositioned()) {
934 childSizes.append(0);
938 if (inflexibleItems.contains(&child))
939 childSizes.append(inflexibleItems.get(&child));
941 LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength);
942 LayoutUnit childSize = preferredChildSize;
943 double extraSpace = 0;
944 if (availableFreeSpace > 0 && totalFlexGrow > 0 && flexSign == PositiveFlexibility && std::isfinite(totalFlexGrow))
945 extraSpace = availableFreeSpace * child.style().flexGrow() / totalFlexGrow;
946 else if (availableFreeSpace < 0 && totalWeightedFlexShrink > 0 && flexSign == NegativeFlexibility && std::isfinite(totalWeightedFlexShrink))
947 extraSpace = availableFreeSpace * child.style().flexShrink() * preferredChildSize / totalWeightedFlexShrink;
948 if (std::isfinite(extraSpace))
949 childSize += roundedLayoutUnit(extraSpace);
951 LayoutUnit adjustedChildSize = adjustChildSizeForMinAndMax(child, childSize);
952 childSizes.append(adjustedChildSize);
953 usedFreeSpace += adjustedChildSize - preferredChildSize;
955 LayoutUnit violation = adjustedChildSize - childSize;
957 minViolations.append(Violation(child, adjustedChildSize));
958 else if (violation < 0)
959 maxViolations.append(Violation(child, adjustedChildSize));
960 totalViolation += violation;
965 freezeViolations(totalViolation < 0 ? maxViolations : minViolations, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, hasInfiniteLineLength);
967 availableFreeSpace -= usedFreeSpace;
969 return !totalViolation;
972 static LayoutUnit initialJustifyContentOffset(LayoutUnit availableFreeSpace, EJustifyContent justifyContent, unsigned numberOfChildren)
974 if (justifyContent == JustifyFlexEnd)
975 return availableFreeSpace;
976 if (justifyContent == JustifyCenter)
977 return availableFreeSpace / 2;
978 if (justifyContent == JustifySpaceAround) {
979 if (availableFreeSpace > 0 && numberOfChildren)
980 return availableFreeSpace / (2 * numberOfChildren);
982 return availableFreeSpace / 2;
987 static LayoutUnit justifyContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, EJustifyContent justifyContent, unsigned numberOfChildren)
989 if (availableFreeSpace > 0 && numberOfChildren > 1) {
990 if (justifyContent == JustifySpaceBetween)
991 return availableFreeSpace / (numberOfChildren - 1);
992 if (justifyContent == JustifySpaceAround)
993 return availableFreeSpace / numberOfChildren;
998 void RenderFlexibleBox::setLogicalOverrideSize(RenderBox& child, LayoutUnit childPreferredSize)
1000 if (hasOrthogonalFlow(child))
1001 child.setOverrideLogicalContentHeight(childPreferredSize - child.borderAndPaddingLogicalHeight());
1003 child.setOverrideLogicalContentWidth(childPreferredSize - child.borderAndPaddingLogicalWidth());
1006 void RenderFlexibleBox::prepareChildForPositionedLayout(RenderBox& child, LayoutUnit mainAxisOffset, LayoutUnit crossAxisOffset, PositionedLayoutMode layoutMode)
1008 ASSERT(child.isOutOfFlowPositioned());
1009 child.containingBlock()->insertPositionedObject(child);
1010 RenderLayer* childLayer = child.layer();
1011 LayoutUnit inlinePosition = isColumnFlow() ? crossAxisOffset : mainAxisOffset;
1012 if (layoutMode == FlipForRowReverse && style().flexDirection() == FlowRowReverse)
1013 inlinePosition = mainAxisExtent() - mainAxisOffset;
1014 childLayer->setStaticInlinePosition(inlinePosition); // FIXME: Not right for regions.
1016 LayoutUnit staticBlockPosition = isColumnFlow() ? mainAxisOffset : crossAxisOffset;
1017 if (childLayer->staticBlockPosition() != staticBlockPosition) {
1018 childLayer->setStaticBlockPosition(staticBlockPosition);
1019 if (child.style().hasStaticBlockPosition(style().isHorizontalWritingMode()))
1020 child.setChildNeedsLayout(MarkOnlyThis);
1024 EAlignItems RenderFlexibleBox::alignmentForChild(RenderBox& child) const
1026 EAlignItems align = resolveAlignment(&style(), &child.style());
1028 if (align == AlignBaseline && hasOrthogonalFlow(child))
1029 align = AlignFlexStart;
1031 if (style().flexWrap() == FlexWrapReverse) {
1032 if (align == AlignFlexStart)
1033 align = AlignFlexEnd;
1034 else if (align == AlignFlexEnd)
1035 align = AlignFlexStart;
1041 size_t RenderFlexibleBox::numberOfInFlowPositionedChildren(const OrderedFlexItemList& children) const
1044 for (size_t i = 0; i < children.size(); ++i) {
1045 RenderBox* child = children[i];
1046 if (!child->isOutOfFlowPositioned())
1052 bool RenderFlexibleBox::needToStretchChild(RenderBox& child)
1054 if (alignmentForChild(child) != AlignStretch)
1057 Length crossAxisLength = isHorizontalFlow() ? child.style().height() : child.style().width();
1058 return crossAxisLength.isAuto();
1061 void RenderFlexibleBox::resetAutoMarginsAndLogicalTopInCrossAxis(RenderBox& child)
1063 if (hasAutoMarginsInCrossAxis(child))
1064 child.updateLogicalHeight();
1067 void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList& children, const Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, bool relayoutChildren, Vector<LineContext>& lineContexts)
1069 ASSERT(childSizes.size() == children.size());
1071 size_t numberOfChildrenForJustifyContent = numberOfInFlowPositionedChildren(children);
1072 LayoutUnit autoMarginOffset = autoMarginOffsetInMainAxis(children, availableFreeSpace);
1073 LayoutUnit mainAxisOffset = flowAwareBorderStart() + flowAwarePaddingStart();
1074 mainAxisOffset += initialJustifyContentOffset(availableFreeSpace, style().justifyContent(), numberOfChildrenForJustifyContent);
1075 if (style().flexDirection() == FlowRowReverse)
1076 mainAxisOffset += isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight();
1078 LayoutUnit totalMainExtent = mainAxisExtent();
1079 LayoutUnit maxAscent = 0, maxDescent = 0; // Used when align-items: baseline.
1080 LayoutUnit maxChildCrossAxisExtent = 0;
1081 size_t seenInFlowPositionedChildren = 0;
1082 bool shouldFlipMainAxis = !isColumnFlow() && !isLeftToRightFlow();
1083 for (size_t i = 0; i < children.size(); ++i) {
1084 RenderBox& child = *children[i];
1085 if (child.isOutOfFlowPositioned()) {
1086 prepareChildForPositionedLayout(child, mainAxisOffset, crossAxisOffset, FlipForRowReverse);
1090 LayoutUnit childPreferredSize = childSizes[i] + mainAxisBorderAndPaddingExtentForChild(child);
1091 setLogicalOverrideSize(child, childPreferredSize);
1092 // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905.
1093 if (needToStretchChild(child) || childPreferredSize != mainAxisExtentForChild(child))
1094 child.setChildNeedsLayout(MarkOnlyThis);
1096 // To avoid double applying margin changes in updateAutoMarginsInCrossAxis, we reset the margins here.
1097 resetAutoMarginsAndLogicalTopInCrossAxis(child);
1099 updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child);
1100 child.layoutIfNeeded();
1102 updateAutoMarginsInMainAxis(child, autoMarginOffset);
1104 LayoutUnit childCrossAxisMarginBoxExtent;
1105 if (alignmentForChild(child) == AlignBaseline && !hasAutoMarginsInCrossAxis(child)) {
1106 LayoutUnit ascent = marginBoxAscentForChild(child);
1107 LayoutUnit descent = (crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child)) - ascent;
1109 maxAscent = std::max(maxAscent, ascent);
1110 maxDescent = std::max(maxDescent, descent);
1112 childCrossAxisMarginBoxExtent = maxAscent + maxDescent;
1114 childCrossAxisMarginBoxExtent = crossAxisExtentForChild(child) + crossAxisMarginExtentForChild(child);
1115 if (!isColumnFlow())
1116 setLogicalHeight(std::max(logicalHeight(), crossAxisOffset + flowAwareBorderAfter() + flowAwarePaddingAfter() + childCrossAxisMarginBoxExtent + crossAxisScrollbarExtent()));
1117 maxChildCrossAxisExtent = std::max(maxChildCrossAxisExtent, childCrossAxisMarginBoxExtent);
1119 mainAxisOffset += flowAwareMarginStartForChild(child);
1121 LayoutUnit childMainExtent = mainAxisExtentForChild(child);
1122 LayoutPoint childLocation(shouldFlipMainAxis ? totalMainExtent - mainAxisOffset - childMainExtent : mainAxisOffset,
1123 crossAxisOffset + flowAwareMarginBeforeForChild(child));
1125 // FIXME: Supporting layout deltas.
1126 setFlowAwareLocationForChild(child, childLocation);
1127 mainAxisOffset += childMainExtent + flowAwareMarginEndForChild(child);
1129 ++seenInFlowPositionedChildren;
1130 if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent)
1131 mainAxisOffset += justifyContentSpaceBetweenChildren(availableFreeSpace, style().justifyContent(), numberOfChildrenForJustifyContent);
1135 setLogicalHeight(mainAxisOffset + flowAwareBorderEnd() + flowAwarePaddingEnd() + scrollbarLogicalHeight());
1137 if (style().flexDirection() == FlowColumnReverse) {
1138 // We have to do an extra pass for column-reverse to reposition the flex items since the start depends
1139 // on the height of the flexbox, which we only know after we've positioned all the flex items.
1140 updateLogicalHeight();
1141 layoutColumnReverse(children, crossAxisOffset, availableFreeSpace);
1144 if (m_numberOfInFlowChildrenOnFirstLine == -1)
1145 m_numberOfInFlowChildrenOnFirstLine = seenInFlowPositionedChildren;
1146 lineContexts.append(LineContext(crossAxisOffset, maxChildCrossAxisExtent, children.size(), maxAscent));
1147 crossAxisOffset += maxChildCrossAxisExtent;
1150 void RenderFlexibleBox::layoutColumnReverse(const OrderedFlexItemList& children, LayoutUnit crossAxisOffset, LayoutUnit availableFreeSpace)
1152 // This is similar to the logic in layoutAndPlaceChildren, except we place the children
1153 // starting from the end of the flexbox. We also don't need to layout anything since we're
1154 // just moving the children to a new position.
1155 size_t numberOfChildrenForJustifyContent = numberOfInFlowPositionedChildren(children);
1156 LayoutUnit mainAxisOffset = logicalHeight() - flowAwareBorderEnd() - flowAwarePaddingEnd();
1157 mainAxisOffset -= initialJustifyContentOffset(availableFreeSpace, style().justifyContent(), numberOfChildrenForJustifyContent);
1158 mainAxisOffset -= isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight();
1160 size_t seenInFlowPositionedChildren = 0;
1161 for (size_t i = 0; i < children.size(); ++i) {
1162 RenderBox& child = *children[i];
1163 if (child.isOutOfFlowPositioned()) {
1164 child.layer()->setStaticBlockPosition(mainAxisOffset);
1167 mainAxisOffset -= mainAxisExtentForChild(child) + flowAwareMarginEndForChild(child);
1169 setFlowAwareLocationForChild(child, LayoutPoint(mainAxisOffset, crossAxisOffset + flowAwareMarginBeforeForChild(child)));
1171 mainAxisOffset -= flowAwareMarginStartForChild(child);
1173 ++seenInFlowPositionedChildren;
1174 if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent)
1175 mainAxisOffset -= justifyContentSpaceBetweenChildren(availableFreeSpace, style().justifyContent(), numberOfChildrenForJustifyContent);
1179 static LayoutUnit initialAlignContentOffset(LayoutUnit availableFreeSpace, EAlignContent alignContent, unsigned numberOfLines)
1181 if (alignContent == AlignContentFlexEnd)
1182 return availableFreeSpace;
1183 if (alignContent == AlignContentCenter)
1184 return availableFreeSpace / 2;
1185 if (alignContent == AlignContentSpaceAround) {
1186 if (availableFreeSpace > 0 && numberOfLines)
1187 return availableFreeSpace / (2 * numberOfLines);
1188 if (availableFreeSpace < 0)
1189 return availableFreeSpace / 2;
1194 static LayoutUnit alignContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, EAlignContent alignContent, unsigned numberOfLines)
1196 if (availableFreeSpace > 0 && numberOfLines > 1) {
1197 if (alignContent == AlignContentSpaceBetween)
1198 return availableFreeSpace / (numberOfLines - 1);
1199 if (alignContent == AlignContentSpaceAround || alignContent == AlignContentStretch)
1200 return availableFreeSpace / numberOfLines;
1205 void RenderFlexibleBox::alignFlexLines(Vector<LineContext>& lineContexts)
1207 if (!isMultiline() || style().alignContent() == AlignContentFlexStart)
1210 LayoutUnit availableCrossAxisSpace = crossAxisContentExtent();
1211 for (size_t i = 0; i < lineContexts.size(); ++i)
1212 availableCrossAxisSpace -= lineContexts[i].crossAxisExtent;
1214 RenderBox* child = m_orderIterator.first();
1215 LayoutUnit lineOffset = initialAlignContentOffset(availableCrossAxisSpace, style().alignContent(), lineContexts.size());
1216 for (unsigned lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1217 lineContexts[lineNumber].crossAxisOffset += lineOffset;
1218 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next())
1219 adjustAlignmentForChild(*child, lineOffset);
1221 if (style().alignContent() == AlignContentStretch && availableCrossAxisSpace > 0)
1222 lineContexts[lineNumber].crossAxisExtent += availableCrossAxisSpace / static_cast<unsigned>(lineContexts.size());
1224 lineOffset += alignContentSpaceBetweenChildren(availableCrossAxisSpace, style().alignContent(), lineContexts.size());
1228 void RenderFlexibleBox::adjustAlignmentForChild(RenderBox& child, LayoutUnit delta)
1230 if (child.isOutOfFlowPositioned()) {
1231 LayoutUnit staticInlinePosition = child.layer()->staticInlinePosition();
1232 LayoutUnit staticBlockPosition = child.layer()->staticBlockPosition();
1233 LayoutUnit mainAxis = isColumnFlow() ? staticBlockPosition : staticInlinePosition;
1234 LayoutUnit crossAxis = isColumnFlow() ? staticInlinePosition : staticBlockPosition;
1236 prepareChildForPositionedLayout(child, mainAxis, crossAxis, NoFlipForRowReverse);
1240 setFlowAwareLocationForChild(child, flowAwareLocationForChild(child) + LayoutSize(0, delta));
1243 void RenderFlexibleBox::alignChildren(const Vector<LineContext>& lineContexts)
1245 // Keep track of the space between the baseline edge and the after edge of the box for each line.
1246 Vector<LayoutUnit> minMarginAfterBaselines;
1248 RenderBox* child = m_orderIterator.first();
1249 for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1250 LayoutUnit minMarginAfterBaseline = LayoutUnit::max();
1251 LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent;
1252 LayoutUnit maxAscent = lineContexts[lineNumber].maxAscent;
1254 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) {
1256 if (child->isOutOfFlowPositioned()) {
1257 if (style().flexWrap() == FlexWrapReverse)
1258 adjustAlignmentForChild(*child, lineCrossAxisExtent);
1262 if (updateAutoMarginsInCrossAxis(*child, std::max(LayoutUnit::fromPixel(0), availableAlignmentSpaceForChild(lineCrossAxisExtent, *child))))
1265 switch (alignmentForChild(*child)) {
1267 ASSERT_NOT_REACHED();
1269 case AlignStretch: {
1270 applyStretchAlignmentToChild(*child, lineCrossAxisExtent);
1271 // Since wrap-reverse flips cross start and cross end, strech children should be aligned with the cross end.
1272 if (style().flexWrap() == FlexWrapReverse)
1273 adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child));
1276 case AlignFlexStart:
1279 adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child));
1282 adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child) / 2);
1284 case AlignBaseline: {
1285 // 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.
1286 // https://bugs.webkit.org/show_bug.cgi?id=98076
1287 LayoutUnit ascent = marginBoxAscentForChild(*child);
1288 LayoutUnit startOffset = maxAscent - ascent;
1289 adjustAlignmentForChild(*child, startOffset);
1291 if (style().flexWrap() == FlexWrapReverse)
1292 minMarginAfterBaseline = std::min(minMarginAfterBaseline, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child) - startOffset);
1297 minMarginAfterBaselines.append(minMarginAfterBaseline);
1300 if (style().flexWrap() != FlexWrapReverse)
1303 // wrap-reverse flips the cross axis start and end. For baseline alignment, this means we
1304 // need to align the after edge of baseline elements with the after edge of the flex line.
1305 child = m_orderIterator.first();
1306 for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1307 LayoutUnit minMarginAfterBaseline = minMarginAfterBaselines[lineNumber];
1308 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) {
1310 if (alignmentForChild(*child) == AlignBaseline && !hasAutoMarginsInCrossAxis(*child) && minMarginAfterBaseline)
1311 adjustAlignmentForChild(*child, minMarginAfterBaseline);
1316 void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox& child, LayoutUnit lineCrossAxisExtent)
1318 if (!isColumnFlow() && child.style().logicalHeight().isAuto()) {
1319 // FIXME: If the child has orthogonal flow, then it already has an override height set, so use it.
1320 if (!hasOrthogonalFlow(child)) {
1321 LayoutUnit stretchedLogicalHeight = child.logicalHeight() + availableAlignmentSpaceForChild(lineCrossAxisExtent, child);
1322 LayoutUnit desiredLogicalHeight = child.constrainLogicalHeightByMinMax(stretchedLogicalHeight);
1324 // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905.
1325 if (desiredLogicalHeight != child.logicalHeight()) {
1326 child.setOverrideLogicalContentHeight(desiredLogicalHeight - child.borderAndPaddingLogicalHeight());
1327 child.setLogicalHeight(0);
1328 child.setChildNeedsLayout(MarkOnlyThis);
1332 } else if (isColumnFlow() && child.style().logicalWidth().isAuto()) {
1333 // FIXME: If the child doesn't have orthogonal flow, then it already has an override width set, so use it.
1334 if (hasOrthogonalFlow(child)) {
1335 LayoutUnit childWidth = std::max<LayoutUnit>(0, lineCrossAxisExtent - crossAxisMarginExtentForChild(child));
1336 childWidth = child.constrainLogicalWidthInRegionByMinMax(childWidth, childWidth, this);
1338 if (childWidth != child.logicalWidth()) {
1339 child.setOverrideLogicalContentWidth(childWidth - child.borderAndPaddingLogicalWidth());
1340 child.setChildNeedsLayout(MarkOnlyThis);
1347 void RenderFlexibleBox::flipForRightToLeftColumn()
1349 if (style().isLeftToRightDirection() || !isColumnFlow())
1352 LayoutUnit crossExtent = crossAxisExtent();
1353 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
1354 if (child->isOutOfFlowPositioned())
1356 LayoutPoint location = flowAwareLocationForChild(*child);
1357 location.setY(crossExtent - crossAxisExtentForChild(*child) - location.y());
1358 setFlowAwareLocationForChild(*child, location);
1362 void RenderFlexibleBox::flipForWrapReverse(const Vector<LineContext>& lineContexts, LayoutUnit crossAxisStartEdge)
1364 LayoutUnit contentExtent = crossAxisContentExtent();
1365 RenderBox* child = m_orderIterator.first();
1366 for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1367 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) {
1369 LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent;
1370 LayoutUnit originalOffset = lineContexts[lineNumber].crossAxisOffset - crossAxisStartEdge;
1371 LayoutUnit newOffset = contentExtent - originalOffset - lineCrossAxisExtent;
1372 adjustAlignmentForChild(*child, newOffset - originalOffset);
1377 bool RenderFlexibleBox::isTopLayoutOverflowAllowed() const
1379 bool hasTopOverflow = RenderBlock::isTopLayoutOverflowAllowed();
1380 if (hasTopOverflow || !style().isReverseFlexDirection())
1381 return hasTopOverflow;
1383 return !isHorizontalFlow();
1386 bool RenderFlexibleBox::isLeftLayoutOverflowAllowed() const
1388 bool hasLeftOverflow = RenderBlock::isLeftLayoutOverflowAllowed();
1389 if (hasLeftOverflow || !style().isReverseFlexDirection())
1390 return hasLeftOverflow;
1392 return isHorizontalFlow();
1395 void RenderFlexibleBox::removeChild(RenderObject& child)
1397 RenderBlock::removeChild(child);
1398 m_orderIterator.invalidate();