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 "core/rendering/RenderFlexibleBox.h"
34 #include "core/frame/UseCounter.h"
35 #include "core/paint/BlockPainter.h"
36 #include "core/rendering/RenderLayer.h"
37 #include "core/rendering/RenderView.h"
38 #include "core/rendering/TextAutosizer.h"
39 #include "platform/LengthFunctions.h"
40 #include "wtf/MathExtras.h"
45 struct RenderFlexibleBox::LineContext {
46 LineContext(LayoutUnit crossAxisOffset, LayoutUnit crossAxisExtent, size_t numberOfChildren, LayoutUnit maxAscent)
47 : crossAxisOffset(crossAxisOffset)
48 , crossAxisExtent(crossAxisExtent)
49 , numberOfChildren(numberOfChildren)
50 , maxAscent(maxAscent)
54 LayoutUnit crossAxisOffset;
55 LayoutUnit crossAxisExtent;
56 size_t numberOfChildren;
60 struct RenderFlexibleBox::Violation {
61 Violation(RenderBox* child, LayoutUnit childSize)
63 , childSize(childSize)
72 RenderFlexibleBox::RenderFlexibleBox(Element* element)
73 : RenderBlock(element)
74 , m_orderIterator(this)
75 , m_numberOfInFlowChildrenOnFirstLine(-1)
77 ASSERT(!childrenInline());
80 RenderFlexibleBox::~RenderFlexibleBox()
84 RenderFlexibleBox* RenderFlexibleBox::createAnonymous(Document* document)
86 RenderFlexibleBox* renderer = new RenderFlexibleBox(0);
87 renderer->setDocumentForAnonymous(document);
91 const char* RenderFlexibleBox::renderName() const
93 return "RenderFlexibleBox";
96 void RenderFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
98 // FIXME: We're ignoring flex-basis here and we shouldn't. We can't start honoring it though until
99 // the flex shorthand stops setting it to 0.
100 // See https://bugs.webkit.org/show_bug.cgi?id=116117 and http://crbug.com/240765.
101 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
102 if (child->isOutOfFlowPositioned())
105 LayoutUnit margin = marginIntrinsicLogicalWidthForChild(child);
106 bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode();
107 LayoutUnit minPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->minPreferredLogicalWidth();
108 LayoutUnit maxPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->maxPreferredLogicalWidth();
109 minPreferredLogicalWidth += margin;
110 maxPreferredLogicalWidth += margin;
111 if (!isColumnFlow()) {
112 maxLogicalWidth += maxPreferredLogicalWidth;
114 // For multiline, the min preferred width is if you put a break between each item.
115 minLogicalWidth = std::max(minLogicalWidth, minPreferredLogicalWidth);
117 minLogicalWidth += minPreferredLogicalWidth;
119 minLogicalWidth = std::max(minPreferredLogicalWidth, minLogicalWidth);
120 maxLogicalWidth = std::max(maxPreferredLogicalWidth, maxLogicalWidth);
124 maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth);
126 LayoutUnit scrollbarWidth = instrinsicScrollbarLogicalWidth();
127 maxLogicalWidth += scrollbarWidth;
128 minLogicalWidth += scrollbarWidth;
131 static int synthesizedBaselineFromContentBox(const RenderBox& box, LineDirectionMode direction)
133 return direction == HorizontalLine ? box.borderTop() + box.paddingTop() + box.contentHeight() : box.borderRight() + box.paddingRight() + box.contentWidth();
136 int RenderFlexibleBox::baselinePosition(FontBaseline, bool, LineDirectionMode direction, LinePositionMode mode) const
138 ASSERT(mode == PositionOnContainingLine);
139 int baseline = firstLineBoxBaseline();
141 baseline = synthesizedBaselineFromContentBox(*this, direction);
143 return beforeMarginInLineDirection(direction) + baseline;
146 int RenderFlexibleBox::firstLineBoxBaseline() const
148 if (isWritingModeRoot() || m_numberOfInFlowChildrenOnFirstLine <= 0)
150 RenderBox* baselineChild = 0;
152 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
153 if (child->isOutOfFlowPositioned())
155 if (alignmentForChild(*child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(*child)) {
156 baselineChild = child;
160 baselineChild = child;
163 if (childNumber == m_numberOfInFlowChildrenOnFirstLine)
170 if (!isColumnFlow() && hasOrthogonalFlow(*baselineChild))
171 return crossAxisExtentForChild(*baselineChild) + baselineChild->logicalTop();
172 if (isColumnFlow() && !hasOrthogonalFlow(*baselineChild))
173 return mainAxisExtentForChild(*baselineChild) + baselineChild->logicalTop();
175 int baseline = baselineChild->firstLineBoxBaseline();
176 if (baseline == -1) {
177 // FIXME: We should pass |direction| into firstLineBoxBaseline and stop bailing out if we're a writing mode root.
178 // This would also fix some cases where the flexbox is orthogonal to its container.
179 LineDirectionMode direction = isHorizontalWritingMode() ? HorizontalLine : VerticalLine;
180 return synthesizedBaselineFromContentBox(*baselineChild, direction) + baselineChild->logicalTop();
183 return baseline + baselineChild->logicalTop();
186 int RenderFlexibleBox::inlineBlockBaseline(LineDirectionMode direction) const
188 int baseline = firstLineBoxBaseline();
192 int marginAscent = direction == HorizontalLine ? marginTop() : marginRight();
193 return synthesizedBaselineFromContentBox(*this, direction) + marginAscent;
196 static ItemPosition resolveAlignment(const RenderStyle* parentStyle, const RenderStyle* childStyle)
198 ItemPosition align = childStyle->alignSelf();
199 if (align == ItemPositionAuto)
200 align = (parentStyle->alignItems() == ItemPositionAuto) ? ItemPositionStretch : parentStyle->alignItems();
204 void RenderFlexibleBox::removeChild(RenderObject* child)
206 RenderBlock::removeChild(child);
207 m_intrinsicSizeAlongMainAxis.remove(child);
210 void RenderFlexibleBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
212 RenderBlock::styleDidChange(diff, oldStyle);
214 if (oldStyle && oldStyle->alignItems() == ItemPositionStretch && diff.needsFullLayout()) {
215 // Flex items that were previously stretching need to be relayed out so we can compute new available cross axis space.
216 // This is only necessary for stretching since other alignment values don't change the size of the box.
217 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
218 ItemPosition previousAlignment = resolveAlignment(oldStyle, child->style());
219 if (previousAlignment == ItemPositionStretch && previousAlignment != resolveAlignment(style(), child->style()))
220 child->setChildNeedsLayout(MarkOnlyThis);
225 void RenderFlexibleBox::layoutBlock(bool relayoutChildren)
227 ASSERT(needsLayout());
229 if (!relayoutChildren && simplifiedLayout())
232 if (updateLogicalWidthAndColumnWidth())
233 relayoutChildren = true;
235 LayoutUnit previousHeight = logicalHeight();
236 setLogicalHeight(borderAndPaddingLogicalHeight() + scrollbarLogicalHeight());
239 TextAutosizer::LayoutScope textAutosizerLayoutScope(this);
240 LayoutState state(*this, locationOffset());
242 m_numberOfInFlowChildrenOnFirstLine = -1;
244 RenderBlock::startDelayUpdateScrollInfo();
246 prepareOrderIteratorAndMargins();
248 ChildFrameRects oldChildRects;
249 appendChildFrameRects(oldChildRects);
251 layoutFlexItems(relayoutChildren);
253 RenderBlock::finishDelayUpdateScrollInfo();
255 if (logicalHeight() != previousHeight)
256 relayoutChildren = true;
258 layoutPositionedObjects(relayoutChildren || isDocumentElement());
260 // FIXME: css3/flexbox/repaint-rtl-column.html seems to issue paint invalidations for more overflow than it needs to.
261 computeOverflow(clientLogicalBottomAfterRepositioning());
264 updateLayerTransformAfterLayout();
266 // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
267 // we overflow or not.
268 updateScrollInfoAfterLayout();
273 void RenderFlexibleBox::appendChildFrameRects(ChildFrameRects& childFrameRects)
275 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
276 if (!child->isOutOfFlowPositioned())
277 childFrameRects.append(child->frameRect());
281 void RenderFlexibleBox::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
283 BlockPainter::paintChildrenOfFlexibleBox(*this, paintInfo, paintOffset);
286 void RenderFlexibleBox::repositionLogicalHeightDependentFlexItems(Vector<LineContext>& lineContexts)
288 LayoutUnit crossAxisStartEdge = lineContexts.isEmpty() ? LayoutUnit() : lineContexts[0].crossAxisOffset;
289 alignFlexLines(lineContexts);
291 alignChildren(lineContexts);
293 if (style()->flexWrap() == FlexWrapReverse)
294 flipForWrapReverse(lineContexts, crossAxisStartEdge);
296 // direction:rtl + flex-direction:column means the cross-axis direction is flipped.
297 flipForRightToLeftColumn();
300 LayoutUnit RenderFlexibleBox::clientLogicalBottomAfterRepositioning()
302 LayoutUnit maxChildLogicalBottom = 0;
303 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
304 if (child->isOutOfFlowPositioned())
306 LayoutUnit childLogicalBottom = logicalTopForChild(child) + logicalHeightForChild(child) + marginAfterForChild(child);
307 maxChildLogicalBottom = std::max(maxChildLogicalBottom, childLogicalBottom);
309 return std::max(clientLogicalBottom(), maxChildLogicalBottom + paddingAfter());
312 bool RenderFlexibleBox::hasOrthogonalFlow(RenderBox& child) const
314 // FIXME: If the child is a flexbox, then we need to check isHorizontalFlow.
315 return isHorizontalFlow() != child.isHorizontalWritingMode();
318 bool RenderFlexibleBox::isColumnFlow() const
320 return style()->isColumnFlexDirection();
323 bool RenderFlexibleBox::isHorizontalFlow() const
325 if (isHorizontalWritingMode())
326 return !isColumnFlow();
327 return isColumnFlow();
330 bool RenderFlexibleBox::isLeftToRightFlow() const
333 return style()->writingMode() == TopToBottomWritingMode || style()->writingMode() == LeftToRightWritingMode;
334 return style()->isLeftToRightDirection() ^ (style()->flexDirection() == FlowRowReverse);
337 bool RenderFlexibleBox::isMultiline() const
339 return style()->flexWrap() != FlexNoWrap;
342 Length RenderFlexibleBox::flexBasisForChild(RenderBox& child) const
344 Length flexLength = child.style()->flexBasis();
345 if (flexLength.isAuto())
346 flexLength = isHorizontalFlow() ? child.style()->width() : child.style()->height();
350 LayoutUnit RenderFlexibleBox::crossAxisExtentForChild(RenderBox& child) const
352 return isHorizontalFlow() ? child.height() : child.width();
355 static inline LayoutUnit constrainedChildIntrinsicContentLogicalHeight(RenderBox& child)
357 LayoutUnit childIntrinsicContentLogicalHeight = child.intrinsicContentLogicalHeight();
358 return child.constrainLogicalHeightByMinMax(childIntrinsicContentLogicalHeight + child.borderAndPaddingLogicalHeight(), childIntrinsicContentLogicalHeight);
361 LayoutUnit RenderFlexibleBox::childIntrinsicHeight(RenderBox& child) const
363 if (child.isHorizontalWritingMode() && needToStretchChildLogicalHeight(child))
364 return constrainedChildIntrinsicContentLogicalHeight(child);
365 return child.height();
368 LayoutUnit RenderFlexibleBox::childIntrinsicWidth(RenderBox& child) const
370 if (!child.isHorizontalWritingMode() && needToStretchChildLogicalHeight(child))
371 return constrainedChildIntrinsicContentLogicalHeight(child);
372 return child.width();
375 LayoutUnit RenderFlexibleBox::crossAxisIntrinsicExtentForChild(RenderBox& child) const
377 return isHorizontalFlow() ? childIntrinsicHeight(child) : childIntrinsicWidth(child);
380 LayoutUnit RenderFlexibleBox::mainAxisExtentForChild(RenderBox& child) const
382 return isHorizontalFlow() ? child.width() : child.height();
385 LayoutUnit RenderFlexibleBox::crossAxisExtent() const
387 return isHorizontalFlow() ? height() : width();
390 LayoutUnit RenderFlexibleBox::mainAxisExtent() const
392 return isHorizontalFlow() ? width() : height();
395 LayoutUnit RenderFlexibleBox::crossAxisContentExtent() const
397 return isHorizontalFlow() ? contentHeight() : contentWidth();
400 LayoutUnit RenderFlexibleBox::mainAxisContentExtent(LayoutUnit contentLogicalHeight)
402 if (isColumnFlow()) {
403 LogicalExtentComputedValues computedValues;
404 LayoutUnit borderPaddingAndScrollbar = borderAndPaddingLogicalHeight() + scrollbarLogicalHeight();
405 LayoutUnit borderBoxLogicalHeight = contentLogicalHeight + borderPaddingAndScrollbar;
406 computeLogicalHeight(borderBoxLogicalHeight, logicalTop(), computedValues);
407 if (computedValues.m_extent == LayoutUnit::max())
408 return computedValues.m_extent;
409 return std::max(LayoutUnit(0), computedValues.m_extent - borderPaddingAndScrollbar);
411 return contentLogicalWidth();
414 LayoutUnit RenderFlexibleBox::computeMainAxisExtentForChild(RenderBox& child, SizeType sizeType, const Length& size)
416 // FIXME: This is wrong for orthogonal flows. It should use the flexbox's writing-mode, not the child's in order
417 // to figure out the logical height/width.
418 if (isColumnFlow()) {
419 // We don't have to check for "auto" here - computeContentLogicalHeight will just return -1 for that case anyway.
420 if (size.isIntrinsic())
421 child.layoutIfNeeded();
422 return child.computeContentLogicalHeight(size, child.logicalHeight() - child.borderAndPaddingLogicalHeight()) + child.scrollbarLogicalHeight();
424 return child.computeLogicalWidthUsing(sizeType, size, contentLogicalWidth(), this) - child.borderAndPaddingLogicalWidth();
427 WritingMode RenderFlexibleBox::transformedWritingMode() const
429 WritingMode mode = style()->writingMode();
434 case TopToBottomWritingMode:
435 case BottomToTopWritingMode:
436 return style()->isLeftToRightDirection() ? LeftToRightWritingMode : RightToLeftWritingMode;
437 case LeftToRightWritingMode:
438 case RightToLeftWritingMode:
439 return style()->isLeftToRightDirection() ? TopToBottomWritingMode : BottomToTopWritingMode;
441 ASSERT_NOT_REACHED();
442 return TopToBottomWritingMode;
445 LayoutUnit RenderFlexibleBox::flowAwareBorderStart() const
447 if (isHorizontalFlow())
448 return isLeftToRightFlow() ? borderLeft() : borderRight();
449 return isLeftToRightFlow() ? borderTop() : borderBottom();
452 LayoutUnit RenderFlexibleBox::flowAwareBorderEnd() const
454 if (isHorizontalFlow())
455 return isLeftToRightFlow() ? borderRight() : borderLeft();
456 return isLeftToRightFlow() ? borderBottom() : borderTop();
459 LayoutUnit RenderFlexibleBox::flowAwareBorderBefore() const
461 switch (transformedWritingMode()) {
462 case TopToBottomWritingMode:
464 case BottomToTopWritingMode:
465 return borderBottom();
466 case LeftToRightWritingMode:
468 case RightToLeftWritingMode:
469 return borderRight();
471 ASSERT_NOT_REACHED();
475 LayoutUnit RenderFlexibleBox::flowAwareBorderAfter() const
477 switch (transformedWritingMode()) {
478 case TopToBottomWritingMode:
479 return borderBottom();
480 case BottomToTopWritingMode:
482 case LeftToRightWritingMode:
483 return borderRight();
484 case RightToLeftWritingMode:
487 ASSERT_NOT_REACHED();
491 LayoutUnit RenderFlexibleBox::flowAwarePaddingStart() const
493 if (isHorizontalFlow())
494 return isLeftToRightFlow() ? paddingLeft() : paddingRight();
495 return isLeftToRightFlow() ? paddingTop() : paddingBottom();
498 LayoutUnit RenderFlexibleBox::flowAwarePaddingEnd() const
500 if (isHorizontalFlow())
501 return isLeftToRightFlow() ? paddingRight() : paddingLeft();
502 return isLeftToRightFlow() ? paddingBottom() : paddingTop();
505 LayoutUnit RenderFlexibleBox::flowAwarePaddingBefore() const
507 switch (transformedWritingMode()) {
508 case TopToBottomWritingMode:
510 case BottomToTopWritingMode:
511 return paddingBottom();
512 case LeftToRightWritingMode:
513 return paddingLeft();
514 case RightToLeftWritingMode:
515 return paddingRight();
517 ASSERT_NOT_REACHED();
521 LayoutUnit RenderFlexibleBox::flowAwarePaddingAfter() const
523 switch (transformedWritingMode()) {
524 case TopToBottomWritingMode:
525 return paddingBottom();
526 case BottomToTopWritingMode:
528 case LeftToRightWritingMode:
529 return paddingRight();
530 case RightToLeftWritingMode:
531 return paddingLeft();
533 ASSERT_NOT_REACHED();
537 LayoutUnit RenderFlexibleBox::flowAwareMarginStartForChild(RenderBox& child) const
539 if (isHorizontalFlow())
540 return isLeftToRightFlow() ? child.marginLeft() : child.marginRight();
541 return isLeftToRightFlow() ? child.marginTop() : child.marginBottom();
544 LayoutUnit RenderFlexibleBox::flowAwareMarginEndForChild(RenderBox& child) const
546 if (isHorizontalFlow())
547 return isLeftToRightFlow() ? child.marginRight() : child.marginLeft();
548 return isLeftToRightFlow() ? child.marginBottom() : child.marginTop();
551 LayoutUnit RenderFlexibleBox::flowAwareMarginBeforeForChild(RenderBox& child) const
553 switch (transformedWritingMode()) {
554 case TopToBottomWritingMode:
555 return child.marginTop();
556 case BottomToTopWritingMode:
557 return child.marginBottom();
558 case LeftToRightWritingMode:
559 return child.marginLeft();
560 case RightToLeftWritingMode:
561 return child.marginRight();
563 ASSERT_NOT_REACHED();
567 LayoutUnit RenderFlexibleBox::crossAxisMarginExtentForChild(RenderBox& child) const
569 return isHorizontalFlow() ? child.marginHeight() : child.marginWidth();
572 LayoutUnit RenderFlexibleBox::crossAxisScrollbarExtent() const
574 return isHorizontalFlow() ? horizontalScrollbarHeight() : verticalScrollbarWidth();
577 LayoutUnit RenderFlexibleBox::crossAxisScrollbarExtentForChild(RenderBox& child) const
579 return isHorizontalFlow() ? child.horizontalScrollbarHeight() : child.verticalScrollbarWidth();
582 LayoutPoint RenderFlexibleBox::flowAwareLocationForChild(RenderBox& child) const
584 return isHorizontalFlow() ? child.location() : child.location().transposedPoint();
587 void RenderFlexibleBox::setFlowAwareLocationForChild(RenderBox& child, const LayoutPoint& location)
589 if (isHorizontalFlow())
590 child.setLocation(location);
592 child.setLocation(location.transposedPoint());
595 LayoutUnit RenderFlexibleBox::mainAxisBorderAndPaddingExtentForChild(RenderBox& child) const
597 return isHorizontalFlow() ? child.borderAndPaddingWidth() : child.borderAndPaddingHeight();
600 static inline bool preferredMainAxisExtentDependsOnLayout(const Length& flexBasis, bool hasInfiniteLineLength)
602 return flexBasis.isAuto() || (flexBasis.isPercent() && hasInfiniteLineLength);
605 bool RenderFlexibleBox::childPreferredMainAxisContentExtentRequiresLayout(RenderBox& child, bool hasInfiniteLineLength) const
607 return preferredMainAxisExtentDependsOnLayout(flexBasisForChild(child), hasInfiniteLineLength) && hasOrthogonalFlow(child);
610 LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox& child, bool hasInfiniteLineLength, bool relayoutChildren)
612 clearLogicalOverrideSize(child);
614 if (child.style()->hasAspectRatio() || child.isImage() || child.isVideo() || child.isCanvas())
615 UseCounter::count(document(), UseCounter::AspectRatioFlexItem);
617 Length flexBasis = flexBasisForChild(child);
618 if (preferredMainAxisExtentDependsOnLayout(flexBasis, hasInfiniteLineLength)) {
619 LayoutUnit mainAxisExtent;
620 if (hasOrthogonalFlow(child)) {
621 if (child.needsLayout() || relayoutChildren) {
622 m_intrinsicSizeAlongMainAxis.remove(&child);
623 child.forceChildLayout();
624 m_intrinsicSizeAlongMainAxis.set(&child, child.logicalHeight());
626 ASSERT(m_intrinsicSizeAlongMainAxis.contains(&child));
627 mainAxisExtent = m_intrinsicSizeAlongMainAxis.get(&child);
629 mainAxisExtent = child.maxPreferredLogicalWidth();
631 ASSERT(mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child) >= 0);
632 return mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child);
634 return std::max(LayoutUnit(0), computeMainAxisExtentForChild(child, MainOrPreferredSize, flexBasis));
637 void RenderFlexibleBox::layoutFlexItems(bool relayoutChildren)
639 Vector<LineContext> lineContexts;
640 OrderedFlexItemList orderedChildren;
641 LayoutUnit sumFlexBaseSize;
642 double totalFlexGrow;
643 double totalWeightedFlexShrink;
644 LayoutUnit sumHypotheticalMainSize;
646 Vector<LayoutUnit, 16> childSizes;
648 m_orderIterator.first();
649 LayoutUnit crossAxisOffset = flowAwareBorderBefore() + flowAwarePaddingBefore();
650 bool hasInfiniteLineLength = false;
651 while (computeNextFlexLine(orderedChildren, sumFlexBaseSize, totalFlexGrow, totalWeightedFlexShrink, sumHypotheticalMainSize, hasInfiniteLineLength, relayoutChildren)) {
652 LayoutUnit containerMainInnerSize = mainAxisContentExtent(sumHypotheticalMainSize);
653 LayoutUnit availableFreeSpace = containerMainInnerSize - sumFlexBaseSize;
654 FlexSign flexSign = (sumHypotheticalMainSize < containerMainInnerSize) ? PositiveFlexibility : NegativeFlexibility;
655 InflexibleFlexItemSize inflexibleItems;
656 childSizes.reserveCapacity(orderedChildren.size());
657 while (!resolveFlexibleLengths(flexSign, orderedChildren, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, childSizes, hasInfiniteLineLength)) {
658 ASSERT(totalFlexGrow >= 0 && totalWeightedFlexShrink >= 0);
659 ASSERT(inflexibleItems.size() > 0);
662 layoutAndPlaceChildren(crossAxisOffset, orderedChildren, childSizes, availableFreeSpace, relayoutChildren, lineContexts, hasInfiniteLineLength);
664 if (hasLineIfEmpty()) {
665 // Even if computeNextFlexLine returns true, the flexbox might not have
666 // a line because all our children might be out of flow positioned.
667 // Instead of just checking if we have a line, make sure the flexbox
668 // has at least a line's worth of height to cover this case.
669 LayoutUnit minHeight = borderAndPaddingLogicalHeight()
670 + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)
671 + scrollbarLogicalHeight();
672 if (height() < minHeight)
673 setLogicalHeight(minHeight);
676 updateLogicalHeight();
677 repositionLogicalHeightDependentFlexItems(lineContexts);
680 LayoutUnit RenderFlexibleBox::autoMarginOffsetInMainAxis(const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace)
682 if (availableFreeSpace <= 0)
685 int numberOfAutoMargins = 0;
686 bool isHorizontal = isHorizontalFlow();
687 for (size_t i = 0; i < children.size(); ++i) {
688 RenderBox* child = children[i];
689 if (child->isOutOfFlowPositioned())
692 if (child->style()->marginLeft().isAuto())
693 ++numberOfAutoMargins;
694 if (child->style()->marginRight().isAuto())
695 ++numberOfAutoMargins;
697 if (child->style()->marginTop().isAuto())
698 ++numberOfAutoMargins;
699 if (child->style()->marginBottom().isAuto())
700 ++numberOfAutoMargins;
703 if (!numberOfAutoMargins)
706 LayoutUnit sizeOfAutoMargin = availableFreeSpace / numberOfAutoMargins;
707 availableFreeSpace = 0;
708 return sizeOfAutoMargin;
711 void RenderFlexibleBox::updateAutoMarginsInMainAxis(RenderBox& child, LayoutUnit autoMarginOffset)
713 ASSERT(autoMarginOffset >= 0);
715 if (isHorizontalFlow()) {
716 if (child.style()->marginLeft().isAuto())
717 child.setMarginLeft(autoMarginOffset);
718 if (child.style()->marginRight().isAuto())
719 child.setMarginRight(autoMarginOffset);
721 if (child.style()->marginTop().isAuto())
722 child.setMarginTop(autoMarginOffset);
723 if (child.style()->marginBottom().isAuto())
724 child.setMarginBottom(autoMarginOffset);
728 bool RenderFlexibleBox::hasAutoMarginsInCrossAxis(RenderBox& child) const
730 if (isHorizontalFlow())
731 return child.style()->marginTop().isAuto() || child.style()->marginBottom().isAuto();
732 return child.style()->marginLeft().isAuto() || child.style()->marginRight().isAuto();
735 LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChild(LayoutUnit lineCrossAxisExtent, RenderBox& child)
737 ASSERT(!child.isOutOfFlowPositioned());
738 LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child);
739 return lineCrossAxisExtent - childCrossExtent;
742 LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChildBeforeStretching(LayoutUnit lineCrossAxisExtent, RenderBox& child)
744 ASSERT(!child.isOutOfFlowPositioned());
745 LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + crossAxisIntrinsicExtentForChild(child);
746 return lineCrossAxisExtent - childCrossExtent;
749 bool RenderFlexibleBox::updateAutoMarginsInCrossAxis(RenderBox& child, LayoutUnit availableAlignmentSpace)
751 ASSERT(!child.isOutOfFlowPositioned());
752 ASSERT(availableAlignmentSpace >= 0);
754 bool isHorizontal = isHorizontalFlow();
755 Length topOrLeft = isHorizontal ? child.style()->marginTop() : child.style()->marginLeft();
756 Length bottomOrRight = isHorizontal ? child.style()->marginBottom() : child.style()->marginRight();
757 if (topOrLeft.isAuto() && bottomOrRight.isAuto()) {
758 adjustAlignmentForChild(child, availableAlignmentSpace / 2);
760 child.setMarginTop(availableAlignmentSpace / 2);
761 child.setMarginBottom(availableAlignmentSpace / 2);
763 child.setMarginLeft(availableAlignmentSpace / 2);
764 child.setMarginRight(availableAlignmentSpace / 2);
768 bool shouldAdjustTopOrLeft = true;
769 if (isColumnFlow() && !child.style()->isLeftToRightDirection()) {
770 // For column flows, only make this adjustment if topOrLeft corresponds to the "before" margin,
771 // so that flipForRightToLeftColumn will do the right thing.
772 shouldAdjustTopOrLeft = false;
774 if (!isColumnFlow() && child.style()->isFlippedBlocksWritingMode()) {
775 // If we are a flipped writing mode, we need to adjust the opposite side. This is only needed
776 // for row flows because this only affects the block-direction axis.
777 shouldAdjustTopOrLeft = false;
780 if (topOrLeft.isAuto()) {
781 if (shouldAdjustTopOrLeft)
782 adjustAlignmentForChild(child, availableAlignmentSpace);
785 child.setMarginTop(availableAlignmentSpace);
787 child.setMarginLeft(availableAlignmentSpace);
790 if (bottomOrRight.isAuto()) {
791 if (!shouldAdjustTopOrLeft)
792 adjustAlignmentForChild(child, availableAlignmentSpace);
795 child.setMarginBottom(availableAlignmentSpace);
797 child.setMarginRight(availableAlignmentSpace);
803 LayoutUnit RenderFlexibleBox::marginBoxAscentForChild(RenderBox& child)
805 LayoutUnit ascent = child.firstLineBoxBaseline();
807 ascent = crossAxisExtentForChild(child);
808 return ascent + flowAwareMarginBeforeForChild(child);
811 LayoutUnit RenderFlexibleBox::computeChildMarginValue(Length margin)
813 // When resolving the margins, we use the content size for resolving percent and calc (for percents in calc expressions) margins.
814 // Fortunately, percent margins are always computed with respect to the block's width, even for margin-top and margin-bottom.
815 LayoutUnit availableSize = contentLogicalWidth();
816 return minimumValueForLength(margin, availableSize);
819 void RenderFlexibleBox::prepareOrderIteratorAndMargins()
821 OrderIteratorPopulator populator(m_orderIterator);
823 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
824 populator.collectChild(child);
826 if (child->isOutOfFlowPositioned())
829 // Before running the flex algorithm, 'auto' has a margin of 0.
830 // Also, if we're not auto sizing, we don't do a layout that computes the start/end margins.
831 if (isHorizontalFlow()) {
832 child->setMarginLeft(computeChildMarginValue(child->style()->marginLeft()));
833 child->setMarginRight(computeChildMarginValue(child->style()->marginRight()));
835 child->setMarginTop(computeChildMarginValue(child->style()->marginTop()));
836 child->setMarginBottom(computeChildMarginValue(child->style()->marginBottom()));
841 LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(RenderBox& child, LayoutUnit childSize)
843 Length max = isHorizontalFlow() ? child.style()->maxWidth() : child.style()->maxHeight();
844 if (max.isSpecifiedOrIntrinsic()) {
845 LayoutUnit maxExtent = computeMainAxisExtentForChild(child, MaxSize, max);
846 if (maxExtent != -1 && childSize > maxExtent)
847 childSize = maxExtent;
850 Length min = isHorizontalFlow() ? child.style()->minWidth() : child.style()->minHeight();
851 LayoutUnit minExtent = 0;
852 if (min.isSpecifiedOrIntrinsic())
853 minExtent = computeMainAxisExtentForChild(child, MinSize, min);
854 return std::max(childSize, minExtent);
857 bool RenderFlexibleBox::computeNextFlexLine(OrderedFlexItemList& orderedChildren, LayoutUnit& sumFlexBaseSize, double& totalFlexGrow, double& totalWeightedFlexShrink, LayoutUnit& sumHypotheticalMainSize, bool& hasInfiniteLineLength, bool relayoutChildren)
859 orderedChildren.clear();
861 totalFlexGrow = totalWeightedFlexShrink = 0;
862 sumHypotheticalMainSize = 0;
864 if (!m_orderIterator.currentChild())
867 LayoutUnit lineBreakLength = mainAxisContentExtent(LayoutUnit::max());
868 hasInfiniteLineLength = lineBreakLength == LayoutUnit::max();
870 bool lineHasInFlowItem = false;
872 for (RenderBox* child = m_orderIterator.currentChild(); child; child = m_orderIterator.next()) {
873 if (child->isOutOfFlowPositioned()) {
874 orderedChildren.append(child);
878 LayoutUnit childMainAxisExtent = preferredMainAxisContentExtentForChild(*child, hasInfiniteLineLength, relayoutChildren);
879 LayoutUnit childMainAxisMarginBorderPadding = mainAxisBorderAndPaddingExtentForChild(*child)
880 + (isHorizontalFlow() ? child->marginWidth() : child->marginHeight());
881 LayoutUnit childFlexBaseSize = childMainAxisExtent + childMainAxisMarginBorderPadding;
883 LayoutUnit childMinMaxAppliedMainAxisExtent = adjustChildSizeForMinAndMax(*child, childMainAxisExtent);
884 LayoutUnit childHypotheticalMainSize = childMinMaxAppliedMainAxisExtent + childMainAxisMarginBorderPadding;
886 if (isMultiline() && sumHypotheticalMainSize + childHypotheticalMainSize > lineBreakLength && lineHasInFlowItem)
888 orderedChildren.append(child);
889 lineHasInFlowItem = true;
890 sumFlexBaseSize += childFlexBaseSize;
891 totalFlexGrow += child->style()->flexGrow();
892 totalWeightedFlexShrink += child->style()->flexShrink() * childMainAxisExtent;
893 sumHypotheticalMainSize += childHypotheticalMainSize;
898 void RenderFlexibleBox::freezeViolations(const Vector<Violation>& violations, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, bool hasInfiniteLineLength)
900 for (size_t i = 0; i < violations.size(); ++i) {
901 RenderBox* child = violations[i].child;
902 LayoutUnit childSize = violations[i].childSize;
903 LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(*child, hasInfiniteLineLength);
904 availableFreeSpace -= childSize - preferredChildSize;
905 totalFlexGrow -= child->style()->flexGrow();
906 totalWeightedFlexShrink -= child->style()->flexShrink() * preferredChildSize;
907 inflexibleItems.set(child, childSize);
911 // Returns true if we successfully ran the algorithm and sized the flex items.
912 bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, Vector<LayoutUnit, 16>& childSizes, bool hasInfiniteLineLength)
914 childSizes.resize(0);
915 LayoutUnit totalViolation = 0;
916 LayoutUnit usedFreeSpace = 0;
917 Vector<Violation> minViolations;
918 Vector<Violation> maxViolations;
919 for (size_t i = 0; i < children.size(); ++i) {
920 RenderBox* child = children[i];
921 if (child->isOutOfFlowPositioned()) {
922 childSizes.append(0);
926 if (inflexibleItems.contains(child))
927 childSizes.append(inflexibleItems.get(child));
929 LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(*child, hasInfiniteLineLength);
930 LayoutUnit childSize = preferredChildSize;
931 double extraSpace = 0;
932 if (availableFreeSpace > 0 && totalFlexGrow > 0 && flexSign == PositiveFlexibility && std::isfinite(totalFlexGrow))
933 extraSpace = availableFreeSpace * child->style()->flexGrow() / totalFlexGrow;
934 else if (availableFreeSpace < 0 && totalWeightedFlexShrink > 0 && flexSign == NegativeFlexibility && std::isfinite(totalWeightedFlexShrink))
935 extraSpace = availableFreeSpace * child->style()->flexShrink() * preferredChildSize / totalWeightedFlexShrink;
936 if (std::isfinite(extraSpace))
937 childSize += LayoutUnit::fromFloatRound(extraSpace);
939 LayoutUnit adjustedChildSize = adjustChildSizeForMinAndMax(*child, childSize);
940 childSizes.append(adjustedChildSize);
941 usedFreeSpace += adjustedChildSize - preferredChildSize;
943 LayoutUnit violation = adjustedChildSize - childSize;
945 minViolations.append(Violation(child, adjustedChildSize));
946 else if (violation < 0)
947 maxViolations.append(Violation(child, adjustedChildSize));
948 totalViolation += violation;
953 freezeViolations(totalViolation < 0 ? maxViolations : minViolations, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, hasInfiniteLineLength);
955 availableFreeSpace -= usedFreeSpace;
957 return !totalViolation;
960 static LayoutUnit initialJustifyContentOffset(LayoutUnit availableFreeSpace, EJustifyContent justifyContent, unsigned numberOfChildren)
962 if (justifyContent == JustifyFlexEnd)
963 return availableFreeSpace;
964 if (justifyContent == JustifyCenter)
965 return availableFreeSpace / 2;
966 if (justifyContent == JustifySpaceAround) {
967 if (availableFreeSpace > 0 && numberOfChildren)
968 return availableFreeSpace / (2 * numberOfChildren);
970 return availableFreeSpace / 2;
975 static LayoutUnit justifyContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, EJustifyContent justifyContent, unsigned numberOfChildren)
977 if (availableFreeSpace > 0 && numberOfChildren > 1) {
978 if (justifyContent == JustifySpaceBetween)
979 return availableFreeSpace / (numberOfChildren - 1);
980 if (justifyContent == JustifySpaceAround)
981 return availableFreeSpace / numberOfChildren;
986 void RenderFlexibleBox::setLogicalOverrideSize(RenderBox& child, LayoutUnit childPreferredSize)
988 if (hasOrthogonalFlow(child))
989 child.setOverrideLogicalContentHeight(childPreferredSize - child.borderAndPaddingLogicalHeight());
991 child.setOverrideLogicalContentWidth(childPreferredSize - child.borderAndPaddingLogicalWidth());
994 void RenderFlexibleBox::clearLogicalOverrideSize(RenderBox& child)
996 if (hasOrthogonalFlow(child))
997 child.clearOverrideLogicalContentHeight();
999 child.clearOverrideLogicalContentWidth();
1002 void RenderFlexibleBox::prepareChildForPositionedLayout(RenderBox& child, LayoutUnit mainAxisOffset, LayoutUnit crossAxisOffset, PositionedLayoutMode layoutMode)
1004 ASSERT(child.isOutOfFlowPositioned());
1005 child.containingBlock()->insertPositionedObject(&child);
1006 RenderLayer* childLayer = child.layer();
1007 LayoutUnit inlinePosition = isColumnFlow() ? crossAxisOffset : mainAxisOffset;
1008 if (layoutMode == FlipForRowReverse && style()->flexDirection() == FlowRowReverse)
1009 inlinePosition = mainAxisExtent() - mainAxisOffset;
1010 childLayer->setStaticInlinePosition(inlinePosition);
1012 LayoutUnit staticBlockPosition = isColumnFlow() ? mainAxisOffset : crossAxisOffset;
1013 if (childLayer->staticBlockPosition() != staticBlockPosition) {
1014 childLayer->setStaticBlockPosition(staticBlockPosition);
1015 if (child.style()->hasStaticBlockPosition(style()->isHorizontalWritingMode()))
1016 child.setChildNeedsLayout(MarkOnlyThis);
1020 ItemPosition RenderFlexibleBox::alignmentForChild(RenderBox& child) const
1022 ItemPosition align = resolveAlignment(style(), child.style());
1024 if (align == ItemPositionBaseline && hasOrthogonalFlow(child))
1025 align = ItemPositionFlexStart;
1027 if (style()->flexWrap() == FlexWrapReverse) {
1028 if (align == ItemPositionFlexStart)
1029 align = ItemPositionFlexEnd;
1030 else if (align == ItemPositionFlexEnd)
1031 align = ItemPositionFlexStart;
1037 size_t RenderFlexibleBox::numberOfInFlowPositionedChildren(const OrderedFlexItemList& children) const
1040 for (size_t i = 0; i < children.size(); ++i) {
1041 RenderBox* child = children[i];
1042 if (!child->isOutOfFlowPositioned())
1048 void RenderFlexibleBox::resetAutoMarginsAndLogicalTopInCrossAxis(RenderBox& child)
1050 if (hasAutoMarginsInCrossAxis(child)) {
1051 child.updateLogicalHeight();
1052 if (isHorizontalFlow()) {
1053 if (child.style()->marginTop().isAuto())
1054 child.setMarginTop(0);
1055 if (child.style()->marginBottom().isAuto())
1056 child.setMarginBottom(0);
1058 if (child.style()->marginLeft().isAuto())
1059 child.setMarginLeft(0);
1060 if (child.style()->marginRight().isAuto())
1061 child.setMarginRight(0);
1066 bool RenderFlexibleBox::needToStretchChildLogicalHeight(RenderBox& child) const
1068 if (alignmentForChild(child) != ItemPositionStretch)
1071 return isHorizontalFlow() && child.style()->height().isAuto();
1074 void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList& children, const Vector<LayoutUnit, 16>& childSizes, LayoutUnit availableFreeSpace, bool relayoutChildren, Vector<LineContext>& lineContexts, bool hasInfiniteLineLength)
1076 ASSERT(childSizes.size() == children.size());
1078 size_t numberOfChildrenForJustifyContent = numberOfInFlowPositionedChildren(children);
1079 LayoutUnit autoMarginOffset = autoMarginOffsetInMainAxis(children, availableFreeSpace);
1080 LayoutUnit mainAxisOffset = flowAwareBorderStart() + flowAwarePaddingStart();
1081 mainAxisOffset += initialJustifyContentOffset(availableFreeSpace, style()->justifyContent(), numberOfChildrenForJustifyContent);
1082 if (style()->flexDirection() == FlowRowReverse)
1083 mainAxisOffset += isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight();
1085 LayoutUnit totalMainExtent = mainAxisExtent();
1086 LayoutUnit maxAscent = 0, maxDescent = 0; // Used when align-items: baseline.
1087 LayoutUnit maxChildCrossAxisExtent = 0;
1088 size_t seenInFlowPositionedChildren = 0;
1089 bool shouldFlipMainAxis = !isColumnFlow() && !isLeftToRightFlow();
1090 for (size_t i = 0; i < children.size(); ++i) {
1091 RenderBox* child = children[i];
1093 if (child->isOutOfFlowPositioned()) {
1094 prepareChildForPositionedLayout(*child, mainAxisOffset, crossAxisOffset, FlipForRowReverse);
1098 // FIXME Investigate if this can be removed based on other flags. crbug.com/370010
1099 child->setMayNeedPaintInvalidation(true);
1101 LayoutUnit childPreferredSize = childSizes[i] + mainAxisBorderAndPaddingExtentForChild(*child);
1102 setLogicalOverrideSize(*child, childPreferredSize);
1103 if (childPreferredSize != mainAxisExtentForChild(*child)) {
1104 child->setChildNeedsLayout(MarkOnlyThis);
1106 // To avoid double applying margin changes in updateAutoMarginsInCrossAxis, we reset the margins here.
1107 resetAutoMarginsAndLogicalTopInCrossAxis(*child);
1109 // We may have already forced relayout for orthogonal flowing children in preferredMainAxisContentExtentForChild.
1110 bool forceChildRelayout = relayoutChildren && !childPreferredMainAxisContentExtentRequiresLayout(*child, hasInfiniteLineLength);
1111 updateBlockChildDirtyBitsBeforeLayout(forceChildRelayout, child);
1112 child->layoutIfNeeded();
1114 updateAutoMarginsInMainAxis(*child, autoMarginOffset);
1116 LayoutUnit childCrossAxisMarginBoxExtent;
1117 if (alignmentForChild(*child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(*child)) {
1118 LayoutUnit ascent = marginBoxAscentForChild(*child);
1119 LayoutUnit descent = (crossAxisMarginExtentForChild(*child) + crossAxisExtentForChild(*child)) - ascent;
1121 maxAscent = std::max(maxAscent, ascent);
1122 maxDescent = std::max(maxDescent, descent);
1124 childCrossAxisMarginBoxExtent = maxAscent + maxDescent;
1126 childCrossAxisMarginBoxExtent = crossAxisIntrinsicExtentForChild(*child) + crossAxisMarginExtentForChild(*child) + crossAxisScrollbarExtentForChild(*child);
1128 if (!isColumnFlow())
1129 setLogicalHeight(std::max(logicalHeight(), crossAxisOffset + flowAwareBorderAfter() + flowAwarePaddingAfter() + childCrossAxisMarginBoxExtent + crossAxisScrollbarExtent()));
1130 maxChildCrossAxisExtent = std::max(maxChildCrossAxisExtent, childCrossAxisMarginBoxExtent);
1132 mainAxisOffset += flowAwareMarginStartForChild(*child);
1134 LayoutUnit childMainExtent = mainAxisExtentForChild(*child);
1135 // In an RTL column situation, this will apply the margin-right/margin-end on the left.
1136 // This will be fixed later in flipForRightToLeftColumn.
1137 LayoutPoint childLocation(shouldFlipMainAxis ? totalMainExtent - mainAxisOffset - childMainExtent : mainAxisOffset,
1138 crossAxisOffset + flowAwareMarginBeforeForChild(*child));
1140 // FIXME: Supporting layout deltas.
1141 setFlowAwareLocationForChild(*child, childLocation);
1142 mainAxisOffset += childMainExtent + flowAwareMarginEndForChild(*child);
1144 ++seenInFlowPositionedChildren;
1145 if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent)
1146 mainAxisOffset += justifyContentSpaceBetweenChildren(availableFreeSpace, style()->justifyContent(), numberOfChildrenForJustifyContent);
1150 setLogicalHeight(mainAxisOffset + flowAwareBorderEnd() + flowAwarePaddingEnd() + scrollbarLogicalHeight());
1152 if (style()->flexDirection() == FlowColumnReverse) {
1153 // We have to do an extra pass for column-reverse to reposition the flex items since the start depends
1154 // on the height of the flexbox, which we only know after we've positioned all the flex items.
1155 updateLogicalHeight();
1156 layoutColumnReverse(children, crossAxisOffset, availableFreeSpace);
1159 if (m_numberOfInFlowChildrenOnFirstLine == -1)
1160 m_numberOfInFlowChildrenOnFirstLine = seenInFlowPositionedChildren;
1161 lineContexts.append(LineContext(crossAxisOffset, maxChildCrossAxisExtent, children.size(), maxAscent));
1162 crossAxisOffset += maxChildCrossAxisExtent;
1165 void RenderFlexibleBox::layoutColumnReverse(const OrderedFlexItemList& children, LayoutUnit crossAxisOffset, LayoutUnit availableFreeSpace)
1167 // This is similar to the logic in layoutAndPlaceChildren, except we place the children
1168 // starting from the end of the flexbox. We also don't need to layout anything since we're
1169 // just moving the children to a new position.
1170 size_t numberOfChildrenForJustifyContent = numberOfInFlowPositionedChildren(children);
1171 LayoutUnit mainAxisOffset = logicalHeight() - flowAwareBorderEnd() - flowAwarePaddingEnd();
1172 mainAxisOffset -= initialJustifyContentOffset(availableFreeSpace, style()->justifyContent(), numberOfChildrenForJustifyContent);
1173 mainAxisOffset -= isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight();
1175 size_t seenInFlowPositionedChildren = 0;
1176 for (size_t i = 0; i < children.size(); ++i) {
1177 RenderBox* child = children[i];
1179 if (child->isOutOfFlowPositioned()) {
1180 child->layer()->setStaticBlockPosition(mainAxisOffset);
1183 mainAxisOffset -= mainAxisExtentForChild(*child) + flowAwareMarginEndForChild(*child);
1185 setFlowAwareLocationForChild(*child, LayoutPoint(mainAxisOffset, crossAxisOffset + flowAwareMarginBeforeForChild(*child)));
1187 mainAxisOffset -= flowAwareMarginStartForChild(*child);
1189 ++seenInFlowPositionedChildren;
1190 if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent)
1191 mainAxisOffset -= justifyContentSpaceBetweenChildren(availableFreeSpace, style()->justifyContent(), numberOfChildrenForJustifyContent);
1195 static LayoutUnit initialAlignContentOffset(LayoutUnit availableFreeSpace, EAlignContent alignContent, unsigned numberOfLines)
1197 if (numberOfLines <= 1)
1199 if (alignContent == AlignContentFlexEnd)
1200 return availableFreeSpace;
1201 if (alignContent == AlignContentCenter)
1202 return availableFreeSpace / 2;
1203 if (alignContent == AlignContentSpaceAround) {
1204 if (availableFreeSpace > 0 && numberOfLines)
1205 return availableFreeSpace / (2 * numberOfLines);
1206 if (availableFreeSpace < 0)
1207 return availableFreeSpace / 2;
1212 static LayoutUnit alignContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, EAlignContent alignContent, unsigned numberOfLines)
1214 if (availableFreeSpace > 0 && numberOfLines > 1) {
1215 if (alignContent == AlignContentSpaceBetween)
1216 return availableFreeSpace / (numberOfLines - 1);
1217 if (alignContent == AlignContentSpaceAround || alignContent == AlignContentStretch)
1218 return availableFreeSpace / numberOfLines;
1223 void RenderFlexibleBox::alignFlexLines(Vector<LineContext>& lineContexts)
1225 // If we have a single line flexbox or a multiline line flexbox with only one flex line,
1226 // the line height is all the available space.
1227 // For flex-direction: row, this means we need to use the height, so we do this after calling updateLogicalHeight.
1228 if (lineContexts.size() == 1) {
1229 lineContexts[0].crossAxisExtent = crossAxisContentExtent();
1233 if (style()->alignContent() == AlignContentFlexStart)
1236 LayoutUnit availableCrossAxisSpace = crossAxisContentExtent();
1237 for (size_t i = 0; i < lineContexts.size(); ++i)
1238 availableCrossAxisSpace -= lineContexts[i].crossAxisExtent;
1240 RenderBox* child = m_orderIterator.first();
1241 LayoutUnit lineOffset = initialAlignContentOffset(availableCrossAxisSpace, style()->alignContent(), lineContexts.size());
1242 for (unsigned lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1243 lineContexts[lineNumber].crossAxisOffset += lineOffset;
1244 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next())
1245 adjustAlignmentForChild(*child, lineOffset);
1247 if (style()->alignContent() == AlignContentStretch && availableCrossAxisSpace > 0)
1248 lineContexts[lineNumber].crossAxisExtent += availableCrossAxisSpace / static_cast<unsigned>(lineContexts.size());
1250 lineOffset += alignContentSpaceBetweenChildren(availableCrossAxisSpace, style()->alignContent(), lineContexts.size());
1254 void RenderFlexibleBox::adjustAlignmentForChild(RenderBox& child, LayoutUnit delta)
1256 if (child.isOutOfFlowPositioned()) {
1257 LayoutUnit staticInlinePosition = child.layer()->staticInlinePosition();
1258 LayoutUnit staticBlockPosition = child.layer()->staticBlockPosition();
1259 LayoutUnit mainAxis = isColumnFlow() ? staticBlockPosition : staticInlinePosition;
1260 LayoutUnit crossAxis = isColumnFlow() ? staticInlinePosition : staticBlockPosition;
1262 prepareChildForPositionedLayout(child, mainAxis, crossAxis, NoFlipForRowReverse);
1266 setFlowAwareLocationForChild(child, flowAwareLocationForChild(child) + LayoutSize(0, delta));
1269 void RenderFlexibleBox::alignChildren(const Vector<LineContext>& lineContexts)
1271 // Keep track of the space between the baseline edge and the after edge of the box for each line.
1272 Vector<LayoutUnit> minMarginAfterBaselines;
1274 RenderBox* child = m_orderIterator.first();
1275 for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1276 LayoutUnit minMarginAfterBaseline = LayoutUnit::max();
1277 LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent;
1278 LayoutUnit maxAscent = lineContexts[lineNumber].maxAscent;
1280 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) {
1282 if (child->isOutOfFlowPositioned()) {
1283 if (style()->flexWrap() == FlexWrapReverse)
1284 adjustAlignmentForChild(*child, lineCrossAxisExtent);
1288 if (updateAutoMarginsInCrossAxis(*child, std::max(LayoutUnit(0), availableAlignmentSpaceForChild(lineCrossAxisExtent, *child))))
1291 switch (alignmentForChild(*child)) {
1292 case ItemPositionAuto:
1293 ASSERT_NOT_REACHED();
1295 case ItemPositionStretch: {
1296 applyStretchAlignmentToChild(*child, lineCrossAxisExtent);
1297 // Since wrap-reverse flips cross start and cross end, strech children should be aligned with the cross end.
1298 if (style()->flexWrap() == FlexWrapReverse)
1299 adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child));
1302 case ItemPositionFlexStart:
1304 case ItemPositionFlexEnd:
1305 adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child));
1307 case ItemPositionCenter:
1308 adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child) / 2);
1310 case ItemPositionBaseline: {
1311 // 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.
1312 // https://bugs.webkit.org/show_bug.cgi?id=98076
1313 LayoutUnit ascent = marginBoxAscentForChild(*child);
1314 LayoutUnit startOffset = maxAscent - ascent;
1315 adjustAlignmentForChild(*child, startOffset);
1317 if (style()->flexWrap() == FlexWrapReverse)
1318 minMarginAfterBaseline = std::min(minMarginAfterBaseline, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child) - startOffset);
1321 case ItemPositionLastBaseline:
1322 case ItemPositionSelfStart:
1323 case ItemPositionSelfEnd:
1324 case ItemPositionStart:
1325 case ItemPositionEnd:
1326 case ItemPositionLeft:
1327 case ItemPositionRight:
1328 // FIXME: File a bug about implementing that. The extended grammar
1329 // is not enabled by default so we shouldn't hit this codepath.
1330 ASSERT_NOT_REACHED();
1334 minMarginAfterBaselines.append(minMarginAfterBaseline);
1337 if (style()->flexWrap() != FlexWrapReverse)
1340 // wrap-reverse flips the cross axis start and end. For baseline alignment, this means we
1341 // need to align the after edge of baseline elements with the after edge of the flex line.
1342 child = m_orderIterator.first();
1343 for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1344 LayoutUnit minMarginAfterBaseline = minMarginAfterBaselines[lineNumber];
1345 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) {
1347 if (alignmentForChild(*child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(*child) && minMarginAfterBaseline)
1348 adjustAlignmentForChild(*child, minMarginAfterBaseline);
1353 void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox& child, LayoutUnit lineCrossAxisExtent)
1355 if (!isColumnFlow() && child.style()->logicalHeight().isAuto()) {
1356 // FIXME: If the child has orthogonal flow, then it already has an override height set, so use it.
1357 if (!hasOrthogonalFlow(child)) {
1358 LayoutUnit heightBeforeStretching = needToStretchChildLogicalHeight(child) ? constrainedChildIntrinsicContentLogicalHeight(child) : child.logicalHeight();
1359 LayoutUnit stretchedLogicalHeight = heightBeforeStretching + availableAlignmentSpaceForChildBeforeStretching(lineCrossAxisExtent, child);
1360 ASSERT(!child.needsLayout());
1361 LayoutUnit desiredLogicalHeight = child.constrainLogicalHeightByMinMax(stretchedLogicalHeight, heightBeforeStretching - child.borderAndPaddingLogicalHeight());
1362 LayoutUnit desiredLogicalContentHeight = desiredLogicalHeight - child.borderAndPaddingLogicalHeight();
1364 // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905.
1365 if (desiredLogicalHeight != child.logicalHeight() || !child.hasOverrideHeight() || desiredLogicalContentHeight != child.overrideLogicalContentHeight()) {
1366 child.setOverrideLogicalContentHeight(desiredLogicalContentHeight);
1367 child.setLogicalHeight(0);
1368 child.forceChildLayout();
1371 } else if (isColumnFlow() && child.style()->logicalWidth().isAuto()) {
1372 // FIXME: If the child doesn't have orthogonal flow, then it already has an override width set, so use it.
1373 if (hasOrthogonalFlow(child)) {
1374 LayoutUnit childWidth = std::max<LayoutUnit>(0, lineCrossAxisExtent - crossAxisMarginExtentForChild(child));
1375 childWidth = child.constrainLogicalWidthByMinMax(childWidth, childWidth, this);
1377 if (childWidth != child.logicalWidth()) {
1378 child.setOverrideLogicalContentWidth(childWidth - child.borderAndPaddingLogicalWidth());
1379 child.forceChildLayout();
1385 void RenderFlexibleBox::flipForRightToLeftColumn()
1387 if (style()->isLeftToRightDirection() || !isColumnFlow())
1390 LayoutUnit crossExtent = crossAxisExtent();
1391 for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
1392 if (child->isOutOfFlowPositioned())
1394 LayoutPoint location = flowAwareLocationForChild(*child);
1395 // For vertical flows, setFlowAwareLocationForChild will transpose x and y,
1396 // so using the y axis for a column cross axis extent is correct.
1397 location.setY(crossExtent - crossAxisExtentForChild(*child) - location.y());
1398 setFlowAwareLocationForChild(*child, location);
1402 void RenderFlexibleBox::flipForWrapReverse(const Vector<LineContext>& lineContexts, LayoutUnit crossAxisStartEdge)
1404 LayoutUnit contentExtent = crossAxisContentExtent();
1405 RenderBox* child = m_orderIterator.first();
1406 for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1407 for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) {
1409 LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent;
1410 LayoutUnit originalOffset = lineContexts[lineNumber].crossAxisOffset - crossAxisStartEdge;
1411 LayoutUnit newOffset = contentExtent - originalOffset - lineCrossAxisExtent;
1412 adjustAlignmentForChild(*child, newOffset - originalOffset);