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_orderIterator(*this)
72 , m_numberOfInFlowChildrenOnFirstLine(-1)
74 setChildrenInline(false); // All of our children must be block-level.
77 RenderFlexibleBox::RenderFlexibleBox(Document& document, PassRef<RenderStyle> style)
78 : RenderBlock(document, std::move(style), 0)
79 , m_orderIterator(*this)
80 , m_numberOfInFlowChildrenOnFirstLine(-1)
82 setChildrenInline(false); // All of our children must be block-level.
85 RenderFlexibleBox::~RenderFlexibleBox()
89 const char* RenderFlexibleBox::renderName() const
91 return "RenderFlexibleBox";
94 void RenderFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
96 // FIXME: We're ignoring flex-basis here and we shouldn't. We can't start honoring it though until
97 // the flex shorthand stops setting it to 0.
98 // See https://bugs.webkit.org/show_bug.cgi?id=116117,
99 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
100 if (child->isOutOfFlowPositioned())
103 LayoutUnit margin = marginIntrinsicLogicalWidthForChild(*child);
104 bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode();
105 LayoutUnit minPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->minPreferredLogicalWidth();
106 LayoutUnit maxPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->maxPreferredLogicalWidth();
107 minPreferredLogicalWidth += margin;
108 maxPreferredLogicalWidth += margin;
109 if (!isColumnFlow()) {
110 maxLogicalWidth += maxPreferredLogicalWidth;
112 // For multiline, the min preferred width is if you put a break between each item.
113 minLogicalWidth = std::max(minLogicalWidth, minPreferredLogicalWidth);
115 minLogicalWidth += minPreferredLogicalWidth;
117 minLogicalWidth = std::max(minPreferredLogicalWidth, minLogicalWidth);
119 // For multiline, the max preferred width is if you never break between items.
120 maxLogicalWidth += maxPreferredLogicalWidth;
122 maxLogicalWidth = std::max(maxPreferredLogicalWidth, maxLogicalWidth);
126 maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth);
128 LayoutUnit scrollbarWidth = instrinsicScrollbarLogicalWidth();
129 maxLogicalWidth += scrollbarWidth;
130 minLogicalWidth += scrollbarWidth;
133 void RenderFlexibleBox::computePreferredLogicalWidths()
135 ASSERT(preferredLogicalWidthsDirty());
137 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0;
139 const RenderStyle& styleToUse = style();
140 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for width.
141 if (styleToUse.logicalWidth().isFixed() && styleToUse.logicalWidth().value() > 0)
142 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalWidth().value());
144 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
146 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for min-width.
147 if (styleToUse.logicalMinWidth().isFixed() && styleToUse.logicalMinWidth().value() > 0) {
148 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value()));
149 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value()));
152 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for maxWidth.
153 if (styleToUse.logicalMaxWidth().isFixed()) {
154 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value()));
155 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value()));
158 LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth();
159 m_minPreferredLogicalWidth += borderAndPadding;
160 m_maxPreferredLogicalWidth += borderAndPadding;
162 setPreferredLogicalWidthsDirty(false);
165 static int synthesizedBaselineFromContentBox(const RenderBox& box, LineDirectionMode direction)
167 return direction == HorizontalLine ? box.borderTop() + box.paddingTop() + box.contentHeight() : box.borderRight() + box.paddingRight() + box.contentWidth();
170 int RenderFlexibleBox::baselinePosition(FontBaseline, bool, LineDirectionMode direction, LinePositionMode) const
172 int baseline = firstLineBaseline();
174 baseline = synthesizedBaselineFromContentBox(*this, direction);
176 int marginAscent = direction == HorizontalLine ? marginTop() : marginRight();
177 return baseline + marginAscent;
180 int RenderFlexibleBox::firstLineBaseline() const
182 if (isWritingModeRoot() || m_numberOfInFlowChildrenOnFirstLine <= 0)
184 RenderBox* baselineChild = 0;
186 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
187 if (child->isOutOfFlowPositioned())
189 if (alignmentForChild(*child) == AlignBaseline && !hasAutoMarginsInCrossAxis(*child)) {
190 baselineChild = child;
194 baselineChild = child;
197 if (childNumber == m_numberOfInFlowChildrenOnFirstLine)
204 if (!isColumnFlow() && hasOrthogonalFlow(*baselineChild))
205 return crossAxisExtentForChild(*baselineChild) + baselineChild->logicalTop();
206 if (isColumnFlow() && !hasOrthogonalFlow(*baselineChild))
207 return mainAxisExtentForChild(*baselineChild) + baselineChild->logicalTop();
209 int baseline = baselineChild->firstLineBaseline();
210 if (baseline == -1) {
211 // FIXME: We should pass |direction| into firstLineBoxBaseline and stop bailing out if we're a writing mode root.
212 // This would also fix some cases where the flexbox is orthogonal to its container.
213 LineDirectionMode direction = isHorizontalWritingMode() ? HorizontalLine : VerticalLine;
214 return synthesizedBaselineFromContentBox(*baselineChild, direction) + baselineChild->logicalTop();
217 return baseline + baselineChild->logicalTop();
220 int RenderFlexibleBox::inlineBlockBaseline(LineDirectionMode direction) const
222 int baseline = firstLineBaseline();
226 int marginAscent = direction == HorizontalLine ? marginTop() : marginRight();
227 return synthesizedBaselineFromContentBox(*this, direction) + marginAscent;
230 static EAlignItems resolveAlignment(const RenderStyle* parentStyle, const RenderStyle* childStyle)
232 EAlignItems align = childStyle->alignSelf();
233 if (align == AlignAuto)
234 align = parentStyle->alignItems();
238 void RenderFlexibleBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
240 RenderBlock::styleDidChange(diff, oldStyle);
242 if (oldStyle && oldStyle->alignItems() == AlignStretch && diff == StyleDifferenceLayout) {
243 // Flex items that were previously stretching need to be relayed out so we can compute new available cross axis space.
244 // This is only necessary for stretching since other alignment values don't change the size of the box.
245 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
246 EAlignItems previousAlignment = resolveAlignment(oldStyle, &child->style());
247 if (previousAlignment == AlignStretch && previousAlignment != resolveAlignment(&style(), &child->style()))
248 child->setChildNeedsLayout(MarkOnlyThis);
253 void RenderFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit)
255 ASSERT(needsLayout());
257 if (!relayoutChildren && simplifiedLayout())
260 LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
262 if (recomputeLogicalWidth())
263 relayoutChildren = true;
265 LayoutUnit previousHeight = logicalHeight();
266 setLogicalHeight(borderAndPaddingLogicalHeight() + scrollbarLogicalHeight());
268 LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
270 preparePaginationBeforeBlockLayout(relayoutChildren);
272 m_numberOfInFlowChildrenOnFirstLine = -1;
274 beginUpdateScrollInfoAfterLayoutTransaction();
276 dirtyForLayoutFromPercentageHeightDescendants();
278 Vector<LineContext> lineContexts;
279 OrderIterator::OrderValues orderValues;
280 computeMainAxisPreferredSizes(orderValues);
281 m_orderIterator.setOrderValues(std::move(orderValues));
283 ChildFrameRects oldChildRects;
284 appendChildFrameRects(oldChildRects);
285 layoutFlexItems(relayoutChildren, lineContexts);
287 updateLogicalHeight();
288 repositionLogicalHeightDependentFlexItems(lineContexts);
290 endAndCommitUpdateScrollInfoAfterLayoutTransaction();
292 if (logicalHeight() != previousHeight)
293 relayoutChildren = true;
295 layoutPositionedObjects(relayoutChildren || isRoot());
297 repaintChildrenDuringLayoutIfMoved(oldChildRects);
298 // FIXME: css3/flexbox/repaint-rtl-column.html seems to repaint more overflow than it needs to.
299 computeOverflow(clientLogicalBottomAfterRepositioning());
302 updateLayerTransform();
304 // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
305 // we overflow or not.
306 updateScrollInfoAfterLayout();
308 repainter.repaintAfterLayout();
313 void RenderFlexibleBox::appendChildFrameRects(ChildFrameRects& childFrameRects)
315 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
316 if (!child->isOutOfFlowPositioned())
317 childFrameRects.append(child->frameRect());
321 void RenderFlexibleBox::repaintChildrenDuringLayoutIfMoved(const ChildFrameRects& oldChildRects)
323 size_t childIndex = 0;
324 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
325 if (child->isOutOfFlowPositioned())
328 // If the child moved, we have to repaint it as well as any floating/positioned
329 // descendants. An exception is if we need a layout. In this case, we know we're going to
330 // repaint ourselves (and the child) anyway.
331 if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
332 child->repaintDuringLayoutIfMoved(oldChildRects[childIndex]);
335 ASSERT(childIndex == oldChildRects.size());
338 void RenderFlexibleBox::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
340 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
341 if (!paintChild(*child, paintInfo, paintOffset, paintInfoForChild, usePrintRect))
346 void RenderFlexibleBox::repositionLogicalHeightDependentFlexItems(Vector<LineContext>& lineContexts)
348 LayoutUnit crossAxisStartEdge = lineContexts.isEmpty() ? LayoutUnit() : lineContexts[0].crossAxisOffset;
349 alignFlexLines(lineContexts);
351 // If we have a single line flexbox, the line height is all the available space.
352 // For flex-direction: row, this means we need to use the height, so we do this after calling updateLogicalHeight.
353 if (!isMultiline() && lineContexts.size() == 1)
354 lineContexts[0].crossAxisExtent = crossAxisContentExtent();
355 alignChildren(lineContexts);
357 if (style().flexWrap() == FlexWrapReverse)
358 flipForWrapReverse(lineContexts, crossAxisStartEdge);
360 // direction:rtl + flex-direction:column means the cross-axis direction is flipped.
361 flipForRightToLeftColumn();
364 LayoutUnit RenderFlexibleBox::clientLogicalBottomAfterRepositioning()
366 LayoutUnit maxChildLogicalBottom = 0;
367 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
368 if (child->isOutOfFlowPositioned())
370 LayoutUnit childLogicalBottom = logicalTopForChild(*child) + logicalHeightForChild(*child) + marginAfterForChild(*child);
371 maxChildLogicalBottom = std::max(maxChildLogicalBottom, childLogicalBottom);
373 return std::max(clientLogicalBottom(), maxChildLogicalBottom);
376 bool RenderFlexibleBox::hasOrthogonalFlow(RenderBox& child) const
378 // FIXME: If the child is a flexbox, then we need to check isHorizontalFlow.
379 return isHorizontalFlow() != child.isHorizontalWritingMode();
382 bool RenderFlexibleBox::isColumnFlow() const
384 return style().isColumnFlexDirection();
387 bool RenderFlexibleBox::isHorizontalFlow() const
389 if (isHorizontalWritingMode())
390 return !isColumnFlow();
391 return isColumnFlow();
394 bool RenderFlexibleBox::isLeftToRightFlow() const
397 return style().writingMode() == TopToBottomWritingMode || style().writingMode() == LeftToRightWritingMode;
398 return style().isLeftToRightDirection() ^ (style().flexDirection() == FlowRowReverse);
401 bool RenderFlexibleBox::isMultiline() const
403 return style().flexWrap() != FlexNoWrap;
406 Length RenderFlexibleBox::flexBasisForChild(RenderBox& child) const
408 Length flexLength = child.style().flexBasis();
409 if (flexLength.isAuto())
410 flexLength = isHorizontalFlow() ? child.style().width() : child.style().height();
414 void RenderFlexibleBox::setCrossAxisExtent(LayoutUnit extent)
416 if (isHorizontalFlow())
422 LayoutUnit RenderFlexibleBox::crossAxisExtentForChild(RenderBox& child) const
424 return isHorizontalFlow() ? child.height() : child.width();
427 LayoutUnit RenderFlexibleBox::mainAxisExtentForChild(RenderBox& child) const
429 return isHorizontalFlow() ? child.width() : child.height();
432 LayoutUnit RenderFlexibleBox::crossAxisExtent() const
434 return isHorizontalFlow() ? height() : width();
437 LayoutUnit RenderFlexibleBox::mainAxisExtent() const
439 return isHorizontalFlow() ? width() : height();
442 LayoutUnit RenderFlexibleBox::crossAxisContentExtent() const
444 return isHorizontalFlow() ? contentHeight() : contentWidth();
447 LayoutUnit RenderFlexibleBox::mainAxisContentExtent(LayoutUnit contentLogicalHeight)
449 if (isColumnFlow()) {
450 LogicalExtentComputedValues computedValues;
451 LayoutUnit borderPaddingAndScrollbar = borderAndPaddingLogicalHeight() + scrollbarLogicalHeight();
452 if (contentLogicalHeight > LayoutUnit::max() - borderPaddingAndScrollbar)
453 contentLogicalHeight -= borderPaddingAndScrollbar;
454 LayoutUnit borderBoxLogicalHeight = contentLogicalHeight + borderPaddingAndScrollbar;
455 computeLogicalHeight(borderBoxLogicalHeight, logicalTop(), computedValues);
456 if (computedValues.m_extent == LayoutUnit::max())
457 return computedValues.m_extent;
458 return std::max(LayoutUnit::fromPixel(0), computedValues.m_extent - borderPaddingAndScrollbar);
460 return contentLogicalWidth();
463 LayoutUnit RenderFlexibleBox::computeMainAxisExtentForChild(RenderBox& child, SizeType sizeType, const Length& size)
465 // FIXME: This is wrong for orthogonal flows. It should use the flexbox's writing-mode, not the child's in order
466 // to figure out the logical height/width.
467 // FIXME: This is wrong if the height is set to an intrinsic keyword value. computeContentLogicalHeight will return -1.
468 // Instead, we need to layout the child an get the appropriate height value.
469 // https://bugs.webkit.org/show_bug.cgi?id=113610
471 return child.computeContentLogicalHeight(size);
472 // FIXME: Figure out how this should work for regions and pass in the appropriate values.
473 RenderRegion* region = 0;
474 return child.computeLogicalWidthInRegionUsing(sizeType, size, contentLogicalWidth(), this, region) - child.borderAndPaddingLogicalWidth();
477 WritingMode RenderFlexibleBox::transformedWritingMode() const
479 WritingMode mode = style().writingMode();
484 case TopToBottomWritingMode:
485 case BottomToTopWritingMode:
486 return style().isLeftToRightDirection() ? LeftToRightWritingMode : RightToLeftWritingMode;
487 case LeftToRightWritingMode:
488 case RightToLeftWritingMode:
489 return style().isLeftToRightDirection() ? TopToBottomWritingMode : BottomToTopWritingMode;
491 ASSERT_NOT_REACHED();
492 return TopToBottomWritingMode;
495 LayoutUnit RenderFlexibleBox::flowAwareBorderStart() const
497 if (isHorizontalFlow())
498 return isLeftToRightFlow() ? borderLeft() : borderRight();
499 return isLeftToRightFlow() ? borderTop() : borderBottom();
502 LayoutUnit RenderFlexibleBox::flowAwareBorderEnd() const
504 if (isHorizontalFlow())
505 return isLeftToRightFlow() ? borderRight() : borderLeft();
506 return isLeftToRightFlow() ? borderBottom() : borderTop();
509 LayoutUnit RenderFlexibleBox::flowAwareBorderBefore() const
511 switch (transformedWritingMode()) {
512 case TopToBottomWritingMode:
514 case BottomToTopWritingMode:
515 return borderBottom();
516 case LeftToRightWritingMode:
518 case RightToLeftWritingMode:
519 return borderRight();
521 ASSERT_NOT_REACHED();
525 LayoutUnit RenderFlexibleBox::flowAwareBorderAfter() const
527 switch (transformedWritingMode()) {
528 case TopToBottomWritingMode:
529 return borderBottom();
530 case BottomToTopWritingMode:
532 case LeftToRightWritingMode:
533 return borderRight();
534 case RightToLeftWritingMode:
537 ASSERT_NOT_REACHED();
541 LayoutUnit RenderFlexibleBox::flowAwarePaddingStart() const
543 if (isHorizontalFlow())
544 return isLeftToRightFlow() ? paddingLeft() : paddingRight();
545 return isLeftToRightFlow() ? paddingTop() : paddingBottom();
548 LayoutUnit RenderFlexibleBox::flowAwarePaddingEnd() const
550 if (isHorizontalFlow())
551 return isLeftToRightFlow() ? paddingRight() : paddingLeft();
552 return isLeftToRightFlow() ? paddingBottom() : paddingTop();
555 LayoutUnit RenderFlexibleBox::flowAwarePaddingBefore() const
557 switch (transformedWritingMode()) {
558 case TopToBottomWritingMode:
560 case BottomToTopWritingMode:
561 return paddingBottom();
562 case LeftToRightWritingMode:
563 return paddingLeft();
564 case RightToLeftWritingMode:
565 return paddingRight();
567 ASSERT_NOT_REACHED();
571 LayoutUnit RenderFlexibleBox::flowAwarePaddingAfter() const
573 switch (transformedWritingMode()) {
574 case TopToBottomWritingMode:
575 return paddingBottom();
576 case BottomToTopWritingMode:
578 case LeftToRightWritingMode:
579 return paddingRight();
580 case RightToLeftWritingMode:
581 return paddingLeft();
583 ASSERT_NOT_REACHED();
587 LayoutUnit RenderFlexibleBox::flowAwareMarginStartForChild(RenderBox& child) const
589 if (isHorizontalFlow())
590 return isLeftToRightFlow() ? child.marginLeft() : child.marginRight();
591 return isLeftToRightFlow() ? child.marginTop() : child.marginBottom();
594 LayoutUnit RenderFlexibleBox::flowAwareMarginEndForChild(RenderBox& child) const
596 if (isHorizontalFlow())
597 return isLeftToRightFlow() ? child.marginRight() : child.marginLeft();
598 return isLeftToRightFlow() ? child.marginBottom() : child.marginTop();
601 LayoutUnit RenderFlexibleBox::flowAwareMarginBeforeForChild(RenderBox& child) const
603 switch (transformedWritingMode()) {
604 case TopToBottomWritingMode:
605 return child.marginTop();
606 case BottomToTopWritingMode:
607 return child.marginBottom();
608 case LeftToRightWritingMode:
609 return child.marginLeft();
610 case RightToLeftWritingMode:
611 return child.marginRight();
613 ASSERT_NOT_REACHED();
617 LayoutUnit RenderFlexibleBox::flowAwareMarginAfterForChild(RenderBox& child) const
619 switch (transformedWritingMode()) {
620 case TopToBottomWritingMode:
621 return child.marginBottom();
622 case BottomToTopWritingMode:
623 return child.marginTop();
624 case LeftToRightWritingMode:
625 return child.marginRight();
626 case RightToLeftWritingMode:
627 return child.marginLeft();
629 ASSERT_NOT_REACHED();
630 return marginBottom();
633 LayoutUnit RenderFlexibleBox::crossAxisMarginExtentForChild(RenderBox& child) const
635 return isHorizontalFlow() ? child.verticalMarginExtent() : child.horizontalMarginExtent();
638 LayoutUnit RenderFlexibleBox::crossAxisScrollbarExtent() const
640 return isHorizontalFlow() ? horizontalScrollbarHeight() : verticalScrollbarWidth();
643 LayoutPoint RenderFlexibleBox::flowAwareLocationForChild(RenderBox& child) const
645 return isHorizontalFlow() ? child.location() : child.location().transposedPoint();
648 void RenderFlexibleBox::setFlowAwareLocationForChild(RenderBox& child, const LayoutPoint& location)
650 if (isHorizontalFlow())
651 child.setLocation(location);
653 child.setLocation(location.transposedPoint());
656 LayoutUnit RenderFlexibleBox::mainAxisBorderAndPaddingExtentForChild(RenderBox& child) const
658 return isHorizontalFlow() ? child.horizontalBorderAndPaddingExtent() : child.verticalBorderAndPaddingExtent();
661 LayoutUnit RenderFlexibleBox::mainAxisScrollbarExtentForChild(RenderBox& child) const
663 return isHorizontalFlow() ? child.verticalScrollbarWidth() : child.horizontalScrollbarHeight();
666 LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox& child, bool hasInfiniteLineLength)
668 bool hasOverrideSize = child.hasOverrideWidth() || child.hasOverrideHeight();
670 child.clearOverrideSize();
672 Length flexBasis = flexBasisForChild(child);
673 if (flexBasis.isAuto() || (flexBasis.isFixed() && !flexBasis.value() && hasInfiniteLineLength)) {
674 if (hasOrthogonalFlow(child)) {
676 child.setChildNeedsLayout(MarkOnlyThis);
677 child.layoutIfNeeded();
679 LayoutUnit mainAxisExtent = hasOrthogonalFlow(child) ? child.logicalHeight() : child.maxPreferredLogicalWidth();
680 ASSERT(mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child) >= 0);
681 return mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child);
683 return std::max(LayoutUnit::fromPixel(0), computeMainAxisExtentForChild(child, MainOrPreferredSize, flexBasis));
686 void RenderFlexibleBox::layoutFlexItems(bool relayoutChildren, Vector<LineContext>& lineContexts)
688 OrderedFlexItemList orderedChildren;
689 LayoutUnit preferredMainAxisExtent;
690 double totalFlexGrow;
691 double totalWeightedFlexShrink;
692 LayoutUnit minMaxAppliedMainAxisExtent;
694 m_orderIterator.first();
695 LayoutUnit crossAxisOffset = flowAwareBorderBefore() + flowAwarePaddingBefore();
696 bool hasInfiniteLineLength = false;
697 while (computeNextFlexLine(orderedChildren, preferredMainAxisExtent, totalFlexGrow, totalWeightedFlexShrink, minMaxAppliedMainAxisExtent, hasInfiniteLineLength)) {
698 LayoutUnit availableFreeSpace = mainAxisContentExtent(preferredMainAxisExtent) - preferredMainAxisExtent;
699 FlexSign flexSign = (minMaxAppliedMainAxisExtent < preferredMainAxisExtent + availableFreeSpace) ? PositiveFlexibility : NegativeFlexibility;
700 InflexibleFlexItemSize inflexibleItems;
701 Vector<LayoutUnit> childSizes;
702 while (!resolveFlexibleLengths(flexSign, orderedChildren, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, childSizes, hasInfiniteLineLength)) {
703 ASSERT(totalFlexGrow >= 0 && totalWeightedFlexShrink >= 0);
704 ASSERT(inflexibleItems.size() > 0);
707 layoutAndPlaceChildren(crossAxisOffset, orderedChildren, childSizes, availableFreeSpace, relayoutChildren, lineContexts);
709 if (hasLineIfEmpty()) {
710 // Even if computeNextFlexLine returns true, the flexbox might not have
711 // a line because all our children might be out of flow positioned.
712 // Instead of just checking if we have a line, make sure the flexbox
713 // has at least a line's worth of height to cover this case.
714 LayoutUnit minHeight = borderAndPaddingLogicalHeight()
715 + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)
716 + scrollbarLogicalHeight();
717 if (height() < minHeight)
718 setLogicalHeight(minHeight);
722 LayoutUnit RenderFlexibleBox::autoMarginOffsetInMainAxis(const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace)
724 if (availableFreeSpace <= 0)
727 int numberOfAutoMargins = 0;
728 bool isHorizontal = isHorizontalFlow();
729 for (size_t i = 0; i < children.size(); ++i) {
730 RenderBox* child = children[i];
731 if (child->isOutOfFlowPositioned())
734 if (child->style().marginLeft().isAuto())
735 ++numberOfAutoMargins;
736 if (child->style().marginRight().isAuto())
737 ++numberOfAutoMargins;
739 if (child->style().marginTop().isAuto())
740 ++numberOfAutoMargins;
741 if (child->style().marginBottom().isAuto())
742 ++numberOfAutoMargins;
745 if (!numberOfAutoMargins)
748 LayoutUnit sizeOfAutoMargin = availableFreeSpace / numberOfAutoMargins;
749 availableFreeSpace = 0;
750 return sizeOfAutoMargin;
753 void RenderFlexibleBox::updateAutoMarginsInMainAxis(RenderBox& child, LayoutUnit autoMarginOffset)
755 ASSERT(autoMarginOffset >= 0);
757 if (isHorizontalFlow()) {
758 if (child.style().marginLeft().isAuto())
759 child.setMarginLeft(autoMarginOffset);
760 if (child.style().marginRight().isAuto())
761 child.setMarginRight(autoMarginOffset);
763 if (child.style().marginTop().isAuto())
764 child.setMarginTop(autoMarginOffset);
765 if (child.style().marginBottom().isAuto())
766 child.setMarginBottom(autoMarginOffset);
770 bool RenderFlexibleBox::hasAutoMarginsInCrossAxis(RenderBox& child) const
772 if (isHorizontalFlow())
773 return child.style().marginTop().isAuto() || child.style().marginBottom().isAuto();
774 return child.style().marginLeft().isAuto() || child.style().marginRight().isAuto();
777 LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChild(LayoutUnit lineCrossAxisExtent, RenderBox& child)
779 ASSERT(!child.isOutOfFlowPositioned());
780 LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child);
781 return lineCrossAxisExtent - childCrossExtent;
784 bool RenderFlexibleBox::updateAutoMarginsInCrossAxis(RenderBox& child, LayoutUnit availableAlignmentSpace)
786 ASSERT(!child.isOutOfFlowPositioned());
787 ASSERT(availableAlignmentSpace >= 0);
789 bool isHorizontal = isHorizontalFlow();
790 Length start = isHorizontal ? child.style().marginTop() : child.style().marginLeft();
791 Length end = isHorizontal ? child.style().marginBottom() : child.style().marginRight();
792 if (start.isAuto() && end.isAuto()) {
793 adjustAlignmentForChild(child, availableAlignmentSpace / 2);
795 child.setMarginTop(availableAlignmentSpace / 2);
796 child.setMarginBottom(availableAlignmentSpace / 2);
798 child.setMarginLeft(availableAlignmentSpace / 2);
799 child.setMarginRight(availableAlignmentSpace / 2);
803 if (start.isAuto()) {
804 adjustAlignmentForChild(child, availableAlignmentSpace);
806 child.setMarginTop(availableAlignmentSpace);
808 child.setMarginLeft(availableAlignmentSpace);
813 child.setMarginBottom(availableAlignmentSpace);
815 child.setMarginRight(availableAlignmentSpace);
821 LayoutUnit RenderFlexibleBox::marginBoxAscentForChild(RenderBox& child)
823 LayoutUnit ascent = child.firstLineBaseline();
825 ascent = crossAxisExtentForChild(child);
826 return ascent + flowAwareMarginBeforeForChild(child);
829 LayoutUnit RenderFlexibleBox::computeChildMarginValue(const Length& margin)
831 // When resolving the margins, we use the content size for resolving percent and calc (for percents in calc expressions) margins.
832 // Fortunately, percent margins are always computed with respect to the block's width, even for margin-top and margin-bottom.
833 LayoutUnit availableSize = contentLogicalWidth();
834 return minimumValueForLength(margin, availableSize);
837 void RenderFlexibleBox::computeMainAxisPreferredSizes(OrderIterator::OrderValues& orderValues)
839 ASSERT(orderValues.isEmpty());
841 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
842 // Avoid growing the vector for the common-case default value of 0. This optimizes the most common case which is
843 // one or a few values with the default order 0
844 int order = child->style().order();
845 if (orderValues.isEmpty() || orderValues.last() != order)
846 orderValues.append(order);
848 if (child->isOutOfFlowPositioned())
851 // Before running the flex algorithm, 'auto' has a margin of 0.
852 // Also, if we're not auto sizing, we don't do a layout that computes the start/end margins.
853 if (isHorizontalFlow()) {
854 child->setMarginLeft(computeChildMarginValue(child->style().marginLeft()));
855 child->setMarginRight(computeChildMarginValue(child->style().marginRight()));
857 child->setMarginTop(computeChildMarginValue(child->style().marginTop()));
858 child->setMarginBottom(computeChildMarginValue(child->style().marginBottom()));
863 LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(RenderBox& child, LayoutUnit childSize)
865 Length max = isHorizontalFlow() ? child.style().maxWidth() : child.style().maxHeight();
866 if (max.isSpecifiedOrIntrinsic()) {
867 LayoutUnit maxExtent = computeMainAxisExtentForChild(child, MaxSize, max);
868 if (maxExtent != -1 && childSize > maxExtent)
869 childSize = maxExtent;
872 Length min = isHorizontalFlow() ? child.style().minWidth() : child.style().minHeight();
873 LayoutUnit minExtent = 0;
874 if (min.isSpecifiedOrIntrinsic())
875 minExtent = computeMainAxisExtentForChild(child, MinSize, min);
876 return std::max(childSize, minExtent);
879 bool RenderFlexibleBox::computeNextFlexLine(OrderedFlexItemList& orderedChildren, LayoutUnit& preferredMainAxisExtent, double& totalFlexGrow, double& totalWeightedFlexShrink, LayoutUnit& minMaxAppliedMainAxisExtent, bool& hasInfiniteLineLength)
881 orderedChildren.clear();
882 preferredMainAxisExtent = 0;
883 totalFlexGrow = totalWeightedFlexShrink = 0;
884 minMaxAppliedMainAxisExtent = 0;
886 if (!m_orderIterator.currentChild())
889 LayoutUnit lineBreakLength = mainAxisContentExtent(LayoutUnit::max());
890 hasInfiniteLineLength = lineBreakLength == LayoutUnit::max();
892 bool lineHasInFlowItem = false;
894 for (RenderBox* child = m_orderIterator.currentChild(); child; child = m_orderIterator.next()) {
895 if (child->isOutOfFlowPositioned()) {
896 orderedChildren.append(child);
900 LayoutUnit childMainAxisExtent = preferredMainAxisContentExtentForChild(*child, hasInfiniteLineLength);
901 LayoutUnit childMainAxisMarginBoxExtent = mainAxisBorderAndPaddingExtentForChild(*child) + childMainAxisExtent;
902 childMainAxisMarginBoxExtent += isHorizontalFlow() ? child->horizontalMarginExtent() : child->verticalMarginExtent();
904 if (isMultiline() && preferredMainAxisExtent + childMainAxisMarginBoxExtent > lineBreakLength && lineHasInFlowItem)
906 orderedChildren.append(child);
907 lineHasInFlowItem = true;
908 preferredMainAxisExtent += childMainAxisMarginBoxExtent;
909 totalFlexGrow += child->style().flexGrow();
910 totalWeightedFlexShrink += child->style().flexShrink() * childMainAxisExtent;
912 LayoutUnit childMinMaxAppliedMainAxisExtent = adjustChildSizeForMinAndMax(*child, childMainAxisExtent);
913 minMaxAppliedMainAxisExtent += childMinMaxAppliedMainAxisExtent - childMainAxisExtent + childMainAxisMarginBoxExtent;
918 void RenderFlexibleBox::freezeViolations(const Vector<Violation>& violations, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, bool hasInfiniteLineLength)
920 for (size_t i = 0; i < violations.size(); ++i) {
921 RenderBox& child = violations[i].child;
922 LayoutUnit childSize = violations[i].childSize;
923 LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength);
924 availableFreeSpace -= childSize - preferredChildSize;
925 totalFlexGrow -= child.style().flexGrow();
926 totalWeightedFlexShrink -= child.style().flexShrink() * preferredChildSize;
927 inflexibleItems.set(&child, childSize);
931 // Returns true if we successfully ran the algorithm and sized the flex items.
932 bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, Vector<LayoutUnit>& childSizes, bool hasInfiniteLineLength)
935 LayoutUnit totalViolation = 0;
936 LayoutUnit usedFreeSpace = 0;
937 Vector<Violation> minViolations;
938 Vector<Violation> maxViolations;
939 for (size_t i = 0; i < children.size(); ++i) {
940 RenderBox& child = *children[i];
941 if (child.isOutOfFlowPositioned()) {
942 childSizes.append(0);
946 if (inflexibleItems.contains(&child))
947 childSizes.append(inflexibleItems.get(&child));
949 LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength);
950 LayoutUnit childSize = preferredChildSize;
951 double extraSpace = 0;
952 if (availableFreeSpace > 0 && totalFlexGrow > 0 && flexSign == PositiveFlexibility && std::isfinite(totalFlexGrow))
953 extraSpace = availableFreeSpace * child.style().flexGrow() / totalFlexGrow;
954 else if (availableFreeSpace < 0 && totalWeightedFlexShrink > 0 && flexSign == NegativeFlexibility && std::isfinite(totalWeightedFlexShrink))
955 extraSpace = availableFreeSpace * child.style().flexShrink() * preferredChildSize / totalWeightedFlexShrink;
956 if (std::isfinite(extraSpace))
957 childSize += roundedLayoutUnit(extraSpace);
959 LayoutUnit adjustedChildSize = adjustChildSizeForMinAndMax(child, childSize);
960 childSizes.append(adjustedChildSize);
961 usedFreeSpace += adjustedChildSize - preferredChildSize;
963 LayoutUnit violation = adjustedChildSize - childSize;
965 minViolations.append(Violation(child, adjustedChildSize));
966 else if (violation < 0)
967 maxViolations.append(Violation(child, adjustedChildSize));
968 totalViolation += violation;
973 freezeViolations(totalViolation < 0 ? maxViolations : minViolations, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, hasInfiniteLineLength);
975 availableFreeSpace -= usedFreeSpace;
977 return !totalViolation;
980 static LayoutUnit initialJustifyContentOffset(LayoutUnit availableFreeSpace, EJustifyContent justifyContent, unsigned numberOfChildren)
982 if (justifyContent == JustifyFlexEnd)
983 return availableFreeSpace;
984 if (justifyContent == JustifyCenter)
985 return availableFreeSpace / 2;
986 if (justifyContent == JustifySpaceAround) {
987 if (availableFreeSpace > 0 && numberOfChildren)
988 return availableFreeSpace / (2 * numberOfChildren);
990 return availableFreeSpace / 2;
995 static LayoutUnit justifyContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, EJustifyContent justifyContent, unsigned numberOfChildren)
997 if (availableFreeSpace > 0 && numberOfChildren > 1) {
998 if (justifyContent == JustifySpaceBetween)
999 return availableFreeSpace / (numberOfChildren - 1);
1000 if (justifyContent == JustifySpaceAround)
1001 return availableFreeSpace / numberOfChildren;
1006 void RenderFlexibleBox::setLogicalOverrideSize(RenderBox& child, LayoutUnit childPreferredSize)
1008 if (hasOrthogonalFlow(child))
1009 child.setOverrideLogicalContentHeight(childPreferredSize - child.borderAndPaddingLogicalHeight());
1011 child.setOverrideLogicalContentWidth(childPreferredSize - child.borderAndPaddingLogicalWidth());
1014 void RenderFlexibleBox::prepareChildForPositionedLayout(RenderBox& child, LayoutUnit mainAxisOffset, LayoutUnit crossAxisOffset, PositionedLayoutMode layoutMode)
1016 ASSERT(child.isOutOfFlowPositioned());
1017 child.containingBlock()->insertPositionedObject(child);
1018 RenderLayer* childLayer = child.layer();
1019 LayoutUnit inlinePosition = isColumnFlow() ? crossAxisOffset : mainAxisOffset;
1020 if (layoutMode == FlipForRowReverse && style().flexDirection() == FlowRowReverse)
1021 inlinePosition = mainAxisExtent() - mainAxisOffset;
1022 childLayer->setStaticInlinePosition(inlinePosition); // FIXME: Not right for regions.
1024 LayoutUnit staticBlockPosition = isColumnFlow() ? mainAxisOffset : crossAxisOffset;
1025 if (childLayer->staticBlockPosition() != staticBlockPosition) {
1026 childLayer->setStaticBlockPosition(staticBlockPosition);
1027 if (child.style().hasStaticBlockPosition(style().isHorizontalWritingMode()))
1028 child.setChildNeedsLayout(MarkOnlyThis);
1032 EAlignItems RenderFlexibleBox::alignmentForChild(RenderBox& child) const
1034 EAlignItems align = resolveAlignment(&style(), &child.style());
1036 if (align == AlignBaseline && hasOrthogonalFlow(child))
1037 align = AlignFlexStart;
1039 if (style().flexWrap() == FlexWrapReverse) {
1040 if (align == AlignFlexStart)
1041 align = AlignFlexEnd;
1042 else if (align == AlignFlexEnd)
1043 align = AlignFlexStart;
1049 size_t RenderFlexibleBox::numberOfInFlowPositionedChildren(const OrderedFlexItemList& children) const
1052 for (size_t i = 0; i < children.size(); ++i) {
1053 RenderBox* child = children[i];
1054 if (!child->isOutOfFlowPositioned())
1060 bool RenderFlexibleBox::needToStretchChild(RenderBox& child)
1062 if (alignmentForChild(child) != AlignStretch)
1065 Length crossAxisLength = isHorizontalFlow() ? child.style().height() : child.style().width();
1066 return crossAxisLength.isAuto();
1069 void RenderFlexibleBox::resetAutoMarginsAndLogicalTopInCrossAxis(RenderBox& child)
1071 if (hasAutoMarginsInCrossAxis(child))
1072 child.updateLogicalHeight();
1075 void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList& children, const Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, bool relayoutChildren, Vector<LineContext>& lineContexts)
1077 ASSERT(childSizes.size() == children.size());
1079 size_t numberOfChildrenForJustifyContent = numberOfInFlowPositionedChildren(children);
1080 LayoutUnit autoMarginOffset = autoMarginOffsetInMainAxis(children, availableFreeSpace);
1081 LayoutUnit mainAxisOffset = flowAwareBorderStart() + flowAwarePaddingStart();
1082 mainAxisOffset += initialJustifyContentOffset(availableFreeSpace, style().justifyContent(), numberOfChildrenForJustifyContent);
1083 if (style().flexDirection() == FlowRowReverse)
1084 mainAxisOffset += isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight();
1086 LayoutUnit totalMainExtent = mainAxisExtent();
1087 LayoutUnit maxAscent = 0, maxDescent = 0; // Used when align-items: baseline.
1088 LayoutUnit maxChildCrossAxisExtent = 0;
1089 size_t seenInFlowPositionedChildren = 0;
1090 bool shouldFlipMainAxis = !isColumnFlow() && !isLeftToRightFlow();
1091 for (size_t i = 0; i < children.size(); ++i) {
1092 RenderBox& child = *children[i];
1093 if (child.isOutOfFlowPositioned()) {
1094 prepareChildForPositionedLayout(child, mainAxisOffset, crossAxisOffset, FlipForRowReverse);
1098 LayoutUnit childPreferredSize = childSizes[i] + mainAxisBorderAndPaddingExtentForChild(child);
1099 setLogicalOverrideSize(child, childPreferredSize);
1100 // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905.
1101 if (needToStretchChild(child) || childPreferredSize != mainAxisExtentForChild(child))
1102 child.setChildNeedsLayout(MarkOnlyThis);
1104 // To avoid double applying margin changes in updateAutoMarginsInCrossAxis, we reset the margins here.
1105 resetAutoMarginsAndLogicalTopInCrossAxis(child);
1107 updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child);
1108 child.layoutIfNeeded();
1110 updateAutoMarginsInMainAxis(child, autoMarginOffset);
1112 LayoutUnit childCrossAxisMarginBoxExtent;
1113 if (alignmentForChild(child) == AlignBaseline && !hasAutoMarginsInCrossAxis(child)) {
1114 LayoutUnit ascent = marginBoxAscentForChild(child);
1115 LayoutUnit descent = (crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child)) - ascent;
1117 maxAscent = std::max(maxAscent, ascent);
1118 maxDescent = std::max(maxDescent, descent);
1120 childCrossAxisMarginBoxExtent = maxAscent + maxDescent;
1122 childCrossAxisMarginBoxExtent = crossAxisExtentForChild(child) + crossAxisMarginExtentForChild(child);
1123 if (!isColumnFlow())
1124 setLogicalHeight(std::max(logicalHeight(), crossAxisOffset + flowAwareBorderAfter() + flowAwarePaddingAfter() + childCrossAxisMarginBoxExtent + crossAxisScrollbarExtent()));
1125 maxChildCrossAxisExtent = std::max(maxChildCrossAxisExtent, childCrossAxisMarginBoxExtent);
1127 mainAxisOffset += flowAwareMarginStartForChild(child);
1129 LayoutUnit childMainExtent = mainAxisExtentForChild(child);
1130 LayoutPoint childLocation(shouldFlipMainAxis ? totalMainExtent - mainAxisOffset - childMainExtent : mainAxisOffset,
1131 crossAxisOffset + flowAwareMarginBeforeForChild(child));
1133 // FIXME: Supporting layout deltas.
1134 setFlowAwareLocationForChild(child, childLocation);
1135 mainAxisOffset += childMainExtent + flowAwareMarginEndForChild(child);
1137 ++seenInFlowPositionedChildren;
1138 if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent)
1139 mainAxisOffset += justifyContentSpaceBetweenChildren(availableFreeSpace, style().justifyContent(), numberOfChildrenForJustifyContent);
1143 setLogicalHeight(mainAxisOffset + flowAwareBorderEnd() + flowAwarePaddingEnd() + scrollbarLogicalHeight());
1145 if (style().flexDirection() == FlowColumnReverse) {
1146 // We have to do an extra pass for column-reverse to reposition the flex items since the start depends
1147 // on the height of the flexbox, which we only know after we've positioned all the flex items.
1148 updateLogicalHeight();
1149 layoutColumnReverse(children, crossAxisOffset, availableFreeSpace);
1152 if (m_numberOfInFlowChildrenOnFirstLine == -1)
1153 m_numberOfInFlowChildrenOnFirstLine = seenInFlowPositionedChildren;
1154 lineContexts.append(LineContext(crossAxisOffset, maxChildCrossAxisExtent, children.size(), maxAscent));
1155 crossAxisOffset += maxChildCrossAxisExtent;
1158 void RenderFlexibleBox::layoutColumnReverse(const OrderedFlexItemList& children, LayoutUnit crossAxisOffset, LayoutUnit availableFreeSpace)
1160 // This is similar to the logic in layoutAndPlaceChildren, except we place the children
1161 // starting from the end of the flexbox. We also don't need to layout anything since we're
1162 // just moving the children to a new position.
1163 size_t numberOfChildrenForJustifyContent = numberOfInFlowPositionedChildren(children);
1164 LayoutUnit mainAxisOffset = logicalHeight() - flowAwareBorderEnd() - flowAwarePaddingEnd();
1165 mainAxisOffset -= initialJustifyContentOffset(availableFreeSpace, style().justifyContent(), numberOfChildrenForJustifyContent);
1166 mainAxisOffset -= isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight();
1168 size_t seenInFlowPositionedChildren = 0;
1169 for (size_t i = 0; i < children.size(); ++i) {
1170 RenderBox& child = *children[i];
1171 if (child.isOutOfFlowPositioned()) {
1172 child.layer()->setStaticBlockPosition(mainAxisOffset);
1175 mainAxisOffset -= mainAxisExtentForChild(child) + flowAwareMarginEndForChild(child);
1177 setFlowAwareLocationForChild(child, LayoutPoint(mainAxisOffset, crossAxisOffset + flowAwareMarginBeforeForChild(child)));
1179 mainAxisOffset -= flowAwareMarginStartForChild(child);
1181 ++seenInFlowPositionedChildren;
1182 if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent)
1183 mainAxisOffset -= justifyContentSpaceBetweenChildren(availableFreeSpace, style().justifyContent(), numberOfChildrenForJustifyContent);
1187 static LayoutUnit initialAlignContentOffset(LayoutUnit availableFreeSpace, EAlignContent alignContent, unsigned numberOfLines)
1189 if (alignContent == AlignContentFlexEnd)
1190 return availableFreeSpace;
1191 if (alignContent == AlignContentCenter)
1192 return availableFreeSpace / 2;
1193 if (alignContent == AlignContentSpaceAround) {
1194 if (availableFreeSpace > 0 && numberOfLines)
1195 return availableFreeSpace / (2 * numberOfLines);
1196 if (availableFreeSpace < 0)
1197 return availableFreeSpace / 2;
1202 static LayoutUnit alignContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, EAlignContent alignContent, unsigned numberOfLines)
1204 if (availableFreeSpace > 0 && numberOfLines > 1) {
1205 if (alignContent == AlignContentSpaceBetween)
1206 return availableFreeSpace / (numberOfLines - 1);
1207 if (alignContent == AlignContentSpaceAround || alignContent == AlignContentStretch)
1208 return availableFreeSpace / numberOfLines;
1213 void RenderFlexibleBox::alignFlexLines(Vector<LineContext>& lineContexts)
1215 if (!isMultiline() || style().alignContent() == AlignContentFlexStart)
1218 LayoutUnit availableCrossAxisSpace = crossAxisContentExtent();
1219 for (size_t i = 0; i < lineContexts.size(); ++i)
1220 availableCrossAxisSpace -= lineContexts[i].crossAxisExtent;
1222 RenderBox* child = m_orderIterator.first();
1223 LayoutUnit lineOffset = initialAlignContentOffset(availableCrossAxisSpace, style().alignContent(), lineContexts.size());
1224 for (unsigned lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1225 lineContexts[lineNumber].crossAxisOffset += lineOffset;
1226 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next())
1227 adjustAlignmentForChild(*child, lineOffset);
1229 if (style().alignContent() == AlignContentStretch && availableCrossAxisSpace > 0)
1230 lineContexts[lineNumber].crossAxisExtent += availableCrossAxisSpace / static_cast<unsigned>(lineContexts.size());
1232 lineOffset += alignContentSpaceBetweenChildren(availableCrossAxisSpace, style().alignContent(), lineContexts.size());
1236 void RenderFlexibleBox::adjustAlignmentForChild(RenderBox& child, LayoutUnit delta)
1238 if (child.isOutOfFlowPositioned()) {
1239 LayoutUnit staticInlinePosition = child.layer()->staticInlinePosition();
1240 LayoutUnit staticBlockPosition = child.layer()->staticBlockPosition();
1241 LayoutUnit mainAxis = isColumnFlow() ? staticBlockPosition : staticInlinePosition;
1242 LayoutUnit crossAxis = isColumnFlow() ? staticInlinePosition : staticBlockPosition;
1244 prepareChildForPositionedLayout(child, mainAxis, crossAxis, NoFlipForRowReverse);
1248 setFlowAwareLocationForChild(child, flowAwareLocationForChild(child) + LayoutSize(0, delta));
1251 void RenderFlexibleBox::alignChildren(const Vector<LineContext>& lineContexts)
1253 // Keep track of the space between the baseline edge and the after edge of the box for each line.
1254 Vector<LayoutUnit> minMarginAfterBaselines;
1256 RenderBox* child = m_orderIterator.first();
1257 for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1258 LayoutUnit minMarginAfterBaseline = LayoutUnit::max();
1259 LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent;
1260 LayoutUnit maxAscent = lineContexts[lineNumber].maxAscent;
1262 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) {
1264 if (child->isOutOfFlowPositioned()) {
1265 if (style().flexWrap() == FlexWrapReverse)
1266 adjustAlignmentForChild(*child, lineCrossAxisExtent);
1270 if (updateAutoMarginsInCrossAxis(*child, std::max(LayoutUnit::fromPixel(0), availableAlignmentSpaceForChild(lineCrossAxisExtent, *child))))
1273 switch (alignmentForChild(*child)) {
1275 ASSERT_NOT_REACHED();
1277 case AlignStretch: {
1278 applyStretchAlignmentToChild(*child, lineCrossAxisExtent);
1279 // Since wrap-reverse flips cross start and cross end, strech children should be aligned with the cross end.
1280 if (style().flexWrap() == FlexWrapReverse)
1281 adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child));
1284 case AlignFlexStart:
1287 adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child));
1290 adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child) / 2);
1292 case AlignBaseline: {
1293 // 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.
1294 // https://bugs.webkit.org/show_bug.cgi?id=98076
1295 LayoutUnit ascent = marginBoxAscentForChild(*child);
1296 LayoutUnit startOffset = maxAscent - ascent;
1297 adjustAlignmentForChild(*child, startOffset);
1299 if (style().flexWrap() == FlexWrapReverse)
1300 minMarginAfterBaseline = std::min(minMarginAfterBaseline, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child) - startOffset);
1305 minMarginAfterBaselines.append(minMarginAfterBaseline);
1308 if (style().flexWrap() != FlexWrapReverse)
1311 // wrap-reverse flips the cross axis start and end. For baseline alignment, this means we
1312 // need to align the after edge of baseline elements with the after edge of the flex line.
1313 child = m_orderIterator.first();
1314 for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1315 LayoutUnit minMarginAfterBaseline = minMarginAfterBaselines[lineNumber];
1316 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) {
1318 if (alignmentForChild(*child) == AlignBaseline && !hasAutoMarginsInCrossAxis(*child) && minMarginAfterBaseline)
1319 adjustAlignmentForChild(*child, minMarginAfterBaseline);
1324 void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox& child, LayoutUnit lineCrossAxisExtent)
1326 if (!isColumnFlow() && child.style().logicalHeight().isAuto()) {
1327 // FIXME: If the child has orthogonal flow, then it already has an override height set, so use it.
1328 if (!hasOrthogonalFlow(child)) {
1329 LayoutUnit stretchedLogicalHeight = child.logicalHeight() + availableAlignmentSpaceForChild(lineCrossAxisExtent, child);
1330 LayoutUnit desiredLogicalHeight = child.constrainLogicalHeightByMinMax(stretchedLogicalHeight);
1332 // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905.
1333 if (desiredLogicalHeight != child.logicalHeight()) {
1334 child.setOverrideLogicalContentHeight(desiredLogicalHeight - child.borderAndPaddingLogicalHeight());
1335 child.setLogicalHeight(0);
1336 child.setChildNeedsLayout(MarkOnlyThis);
1340 } else if (isColumnFlow() && child.style().logicalWidth().isAuto()) {
1341 // FIXME: If the child doesn't have orthogonal flow, then it already has an override width set, so use it.
1342 if (hasOrthogonalFlow(child)) {
1343 LayoutUnit childWidth = std::max<LayoutUnit>(0, lineCrossAxisExtent - crossAxisMarginExtentForChild(child));
1344 childWidth = child.constrainLogicalWidthInRegionByMinMax(childWidth, childWidth, this);
1346 if (childWidth != child.logicalWidth()) {
1347 child.setOverrideLogicalContentWidth(childWidth - child.borderAndPaddingLogicalWidth());
1348 child.setChildNeedsLayout(MarkOnlyThis);
1355 void RenderFlexibleBox::flipForRightToLeftColumn()
1357 if (style().isLeftToRightDirection() || !isColumnFlow())
1360 LayoutUnit crossExtent = crossAxisExtent();
1361 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
1362 if (child->isOutOfFlowPositioned())
1364 LayoutPoint location = flowAwareLocationForChild(*child);
1365 location.setY(crossExtent - crossAxisExtentForChild(*child) - location.y());
1366 setFlowAwareLocationForChild(*child, location);
1370 void RenderFlexibleBox::flipForWrapReverse(const Vector<LineContext>& lineContexts, LayoutUnit crossAxisStartEdge)
1372 LayoutUnit contentExtent = crossAxisContentExtent();
1373 RenderBox* child = m_orderIterator.first();
1374 for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1375 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) {
1377 LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent;
1378 LayoutUnit originalOffset = lineContexts[lineNumber].crossAxisOffset - crossAxisStartEdge;
1379 LayoutUnit newOffset = contentExtent - originalOffset - lineCrossAxisExtent;
1380 adjustAlignmentForChild(*child, newOffset - originalOffset);
1385 bool RenderFlexibleBox::isTopLayoutOverflowAllowed() const
1387 bool hasTopOverflow = RenderBlock::isTopLayoutOverflowAllowed();
1388 if (hasTopOverflow || !style().isReverseFlexDirection())
1389 return hasTopOverflow;
1391 return !isHorizontalFlow();
1394 bool RenderFlexibleBox::isLeftLayoutOverflowAllowed() const
1396 bool hasLeftOverflow = RenderBlock::isLeftLayoutOverflowAllowed();
1397 if (hasLeftOverflow || !style().isReverseFlexDirection())
1398 return hasLeftOverflow;
1400 return isHorizontalFlow();