Use "= default" to denote default constructor or destructor
[WebKit-https.git] / Source / WebCore / rendering / RenderFlexibleBox.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "RenderFlexibleBox.h"
33
34 #include "FlexibleBoxAlgorithm.h"
35 #include "LayoutRepainter.h"
36 #include "RenderChildIterator.h"
37 #include "RenderLayer.h"
38 #include "RenderView.h"
39 #include "RuntimeEnabledFeatures.h"
40 #include <limits>
41 #include <wtf/MathExtras.h>
42
43 namespace WebCore {
44
45 struct RenderFlexibleBox::LineContext {
46     LineContext(LayoutUnit crossAxisOffset, LayoutUnit crossAxisExtent, LayoutUnit maxAscent, Vector<FlexItem>&& flexItems)
47         : crossAxisOffset(crossAxisOffset)
48         , crossAxisExtent(crossAxisExtent)
49         , maxAscent(maxAscent)
50         , flexItems(flexItems)
51     {
52     }
53     
54     LayoutUnit crossAxisOffset;
55     LayoutUnit crossAxisExtent;
56     LayoutUnit maxAscent;
57     Vector<FlexItem> flexItems;
58 };
59
60 RenderFlexibleBox::RenderFlexibleBox(Element& element, RenderStyle&& style)
61     : RenderBlock(element, WTFMove(style), 0)
62 {
63     setChildrenInline(false); // All of our children must be block-level.
64 }
65
66 RenderFlexibleBox::RenderFlexibleBox(Document& document, RenderStyle&& style)
67     : RenderBlock(document, WTFMove(style), 0)
68 {
69     setChildrenInline(false); // All of our children must be block-level.
70 }
71
72 RenderFlexibleBox::~RenderFlexibleBox() = default;
73
74 const char* RenderFlexibleBox::renderName() const
75 {
76     return "RenderFlexibleBox";
77 }
78
79 void RenderFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
80 {
81     LayoutUnit childMinWidth;
82     LayoutUnit childMaxWidth;
83     bool hadExcludedChildren = computePreferredWidthsForExcludedChildren(childMinWidth, childMaxWidth);
84
85     // FIXME: We're ignoring flex-basis here and we shouldn't. We can't start
86     // honoring it though until the flex shorthand stops setting it to 0. See
87     // https://bugs.webkit.org/show_bug.cgi?id=116117 and
88     // https://crbug.com/240765.
89     for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
90         if (child->isOutOfFlowPositioned() || child->isExcludedFromNormalLayout())
91             continue;
92         
93         LayoutUnit margin = marginIntrinsicLogicalWidthForChild(*child);
94         
95         LayoutUnit minPreferredLogicalWidth;
96         LayoutUnit maxPreferredLogicalWidth;
97         computeChildPreferredLogicalWidths(*child, minPreferredLogicalWidth, maxPreferredLogicalWidth);
98         
99         minPreferredLogicalWidth += margin;
100         maxPreferredLogicalWidth += margin;
101
102         if (!isColumnFlow()) {
103             maxLogicalWidth += maxPreferredLogicalWidth;
104             if (isMultiline()) {
105                 // For multiline, the min preferred width is if you put a break between
106                 // each item.
107                 minLogicalWidth = std::max(minLogicalWidth, minPreferredLogicalWidth);
108             } else
109                 minLogicalWidth += minPreferredLogicalWidth;
110         } else {
111             minLogicalWidth = std::max(minPreferredLogicalWidth, minLogicalWidth);
112             maxLogicalWidth = std::max(maxPreferredLogicalWidth, maxLogicalWidth);
113         }
114     }
115     
116     maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth);
117     
118     // Due to negative margins, it is possible that we calculated a negative
119     // intrinsic width. Make sure that we never return a negative width.
120     minLogicalWidth = std::max(LayoutUnit(), minLogicalWidth);
121     maxLogicalWidth = std::max(LayoutUnit(), maxLogicalWidth);
122     
123     if (hadExcludedChildren) {
124         minLogicalWidth = std::max(minLogicalWidth, childMinWidth);
125         maxLogicalWidth = std::max(maxLogicalWidth, childMaxWidth);
126     }
127
128     LayoutUnit scrollbarWidth(scrollbarLogicalWidth());
129     maxLogicalWidth += scrollbarWidth;
130     minLogicalWidth += scrollbarWidth;
131 }
132
133 void RenderFlexibleBox::computePreferredLogicalWidths()
134 {
135     ASSERT(preferredLogicalWidthsDirty());
136
137     m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0;
138
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());
143     else
144         computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
145
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()));
150     }
151
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()));
156     }
157
158     LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth();
159     m_minPreferredLogicalWidth += borderAndPadding;
160     m_maxPreferredLogicalWidth += borderAndPadding;
161
162     setPreferredLogicalWidthsDirty(false);
163 }
164
165 static int synthesizedBaselineFromContentBox(const RenderBox& box, LineDirectionMode direction)
166 {
167     return direction == HorizontalLine ? box.borderTop() + box.paddingTop() + box.contentHeight() : box.borderRight() + box.paddingRight() + box.contentWidth();
168 }
169
170 int RenderFlexibleBox::baselinePosition(FontBaseline, bool, LineDirectionMode direction, LinePositionMode) const
171 {
172     int baseline = firstLineBaseline().value_or(synthesizedBaselineFromContentBox(*this, direction));
173
174     int marginAscent = direction == HorizontalLine ? marginTop() : marginRight();
175     return baseline + marginAscent;
176 }
177
178 std::optional<int> RenderFlexibleBox::firstLineBaseline() const
179 {
180     if (isWritingModeRoot() || m_numberOfInFlowChildrenOnFirstLine <= 0)
181         return std::optional<int>();
182     RenderBox* baselineChild = nullptr;
183     int childNumber = 0;
184     for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
185         if (m_orderIterator.shouldSkipChild(*child))
186             continue;
187         if (alignmentForChild(*child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(*child)) {
188             baselineChild = child;
189             break;
190         }
191         if (!baselineChild)
192             baselineChild = child;
193
194         ++childNumber;
195         if (childNumber == m_numberOfInFlowChildrenOnFirstLine)
196             break;
197     }
198
199     if (!baselineChild)
200         return std::optional<int>();
201
202     if (!isColumnFlow() && hasOrthogonalFlow(*baselineChild))
203         return std::optional<int>(crossAxisExtentForChild(*baselineChild) + baselineChild->logicalTop());
204     if (isColumnFlow() && !hasOrthogonalFlow(*baselineChild))
205         return std::optional<int>(mainAxisExtentForChild(*baselineChild) + baselineChild->logicalTop());
206
207     std::optional<int> baseline = baselineChild->firstLineBaseline();
208     if (!baseline) {
209         // FIXME: We should pass |direction| into firstLineBoxBaseline and stop bailing out if we're a writing mode root.
210         // This would also fix some cases where the flexbox is orthogonal to its container.
211         LineDirectionMode direction = isHorizontalWritingMode() ? HorizontalLine : VerticalLine;
212         return std::optional<int>(synthesizedBaselineFromContentBox(*baselineChild, direction) + baselineChild->logicalTop());
213     }
214
215     return std::optional<int>(baseline.value() + baselineChild->logicalTop());
216 }
217
218 std::optional<int> RenderFlexibleBox::inlineBlockBaseline(LineDirectionMode direction) const
219 {
220     if (std::optional<int> baseline = firstLineBaseline())
221         return baseline;
222
223     int marginAscent = direction == HorizontalLine ? marginTop() : marginRight();
224     return synthesizedBaselineFromContentBox(*this, direction) + marginAscent;
225 }
226
227 static const StyleContentAlignmentData& contentAlignmentNormalBehavior()
228 {
229     // The justify-content property applies along the main axis, but since
230     // flexing in the main axis is controlled by flex, stretch behaves as
231     // flex-start (ignoring the specified fallback alignment, if any).
232     // https://drafts.csswg.org/css-align/#distribution-flex
233     static const StyleContentAlignmentData normalBehavior = { ContentPositionNormal, ContentDistributionStretch};
234     return normalBehavior;
235 }
236
237 void RenderFlexibleBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
238 {
239     RenderBlock::styleDidChange(diff, oldStyle);
240     if (!oldStyle || diff != StyleDifferenceLayout)
241         return;
242
243     if (oldStyle->resolvedAlignItems(selfAlignmentNormalBehavior()).position() == ItemPositionStretch) {
244         // Flex items that were previously stretching need to be relayed out so we
245         // can compute new available cross axis space. This is only necessary for
246         // stretching since other alignment values don't change the size of the
247         // box.
248         for (auto& child : childrenOfType<RenderBox>(*this)) {
249             ItemPosition previousAlignment = child.style().resolvedAlignSelf(oldStyle, selfAlignmentNormalBehavior()).position();
250             if (previousAlignment == ItemPositionStretch && previousAlignment != child.style().resolvedAlignSelf(&style(), selfAlignmentNormalBehavior()).position())
251                 child.setChildNeedsLayout(MarkOnlyThis);
252         }
253     }
254 }
255
256 void RenderFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit)
257 {
258     ASSERT(needsLayout());
259
260     if (!relayoutChildren && simplifiedLayout())
261         return;
262
263     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
264
265     m_relaidOutChildren.clear();
266     
267     bool oldInLayout = m_inLayout;
268     m_inLayout = true;
269     
270     if (recomputeLogicalWidth())
271         relayoutChildren = true;
272
273     LayoutUnit previousHeight = logicalHeight();
274     setLogicalHeight(borderAndPaddingLogicalHeight() + scrollbarLogicalHeight());
275
276     LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
277
278     preparePaginationBeforeBlockLayout(relayoutChildren);
279
280     m_numberOfInFlowChildrenOnFirstLine = -1;
281
282     beginUpdateScrollInfoAfterLayoutTransaction();
283
284     prepareOrderIteratorAndMargins();
285
286     // Fieldsets need to find their legend and position it inside the border of the object.
287     // The legend then gets skipped during normal layout. The same is true for ruby text.
288     // It doesn't get included in the normal layout process but is instead skipped.
289     layoutExcludedChildren(relayoutChildren);
290
291     ChildFrameRects oldChildRects;
292     appendChildFrameRects(oldChildRects);
293
294     layoutFlexItems(relayoutChildren);
295
296     endAndCommitUpdateScrollInfoAfterLayoutTransaction();
297
298     if (logicalHeight() != previousHeight)
299         relayoutChildren = true;
300
301     layoutPositionedObjects(relayoutChildren || isDocumentElementRenderer());
302
303     repaintChildrenDuringLayoutIfMoved(oldChildRects);
304     // FIXME: css3/flexbox/repaint-rtl-column.html seems to repaint more overflow than it needs to.
305     computeOverflow(clientLogicalBottomAfterRepositioning());
306     statePusher.pop();
307
308     updateLayerTransform();
309     
310     // We have to reset this, because changes to our ancestors' style can affect
311     // this value. Also, this needs to be before we call updateAfterLayout, as
312     // that function may re-enter this one.
313     m_hasDefiniteHeight = SizeDefiniteness::Unknown;
314
315     // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
316     // we overflow or not.
317     updateScrollInfoAfterLayout();
318
319     repainter.repaintAfterLayout();
320
321     clearNeedsLayout();
322     
323     m_inLayout = oldInLayout;
324 }
325
326 void RenderFlexibleBox::appendChildFrameRects(ChildFrameRects& childFrameRects)
327 {
328     for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
329         if (!child->isOutOfFlowPositioned())
330             childFrameRects.append(child->frameRect());
331     }
332 }
333
334 void RenderFlexibleBox::repaintChildrenDuringLayoutIfMoved(const ChildFrameRects& oldChildRects)
335 {
336     size_t childIndex = 0;
337     for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
338         if (child->isOutOfFlowPositioned())
339             continue;
340
341         // If the child moved, we have to repaint it as well as any floating/positioned
342         // descendants. An exception is if we need a layout. In this case, we know we're going to
343         // repaint ourselves (and the child) anyway.
344         if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
345             child->repaintDuringLayoutIfMoved(oldChildRects[childIndex]);
346         ++childIndex;
347     }
348     ASSERT(childIndex == oldChildRects.size());
349 }
350
351 void RenderFlexibleBox::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
352 {
353     for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) {
354         if (!paintChild(*child, paintInfo, paintOffset, paintInfoForChild, usePrintRect, PaintAsInlineBlock))
355             return;
356     }
357 }
358
359 void RenderFlexibleBox::repositionLogicalHeightDependentFlexItems(Vector<LineContext>& lineContexts)
360 {
361     LayoutUnit crossAxisStartEdge = lineContexts.isEmpty() ? LayoutUnit() : lineContexts[0].crossAxisOffset;
362     alignFlexLines(lineContexts);
363     
364     alignChildren(lineContexts);
365     
366     if (style().flexWrap() == FlexWrapReverse)
367         flipForWrapReverse(lineContexts, crossAxisStartEdge);
368     
369     // direction:rtl + flex-direction:column means the cross-axis direction is
370     // flipped.
371     flipForRightToLeftColumn(lineContexts);
372 }
373
374 LayoutUnit RenderFlexibleBox::clientLogicalBottomAfterRepositioning()
375 {
376     LayoutUnit maxChildLogicalBottom;
377     for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
378         if (child->isOutOfFlowPositioned())
379             continue;
380         LayoutUnit childLogicalBottom = logicalTopForChild(*child) + logicalHeightForChild(*child) + marginAfterForChild(*child);
381         maxChildLogicalBottom = std::max(maxChildLogicalBottom, childLogicalBottom);
382     }
383     return std::max(clientLogicalBottom(), maxChildLogicalBottom + paddingAfter());
384 }
385
386 bool RenderFlexibleBox::hasOrthogonalFlow(const RenderBox& child) const
387 {
388     return isHorizontalFlow() != child.isHorizontalWritingMode();
389 }
390
391 bool RenderFlexibleBox::isColumnFlow() const
392 {
393     return style().isColumnFlexDirection();
394 }
395
396 bool RenderFlexibleBox::isHorizontalFlow() const
397 {
398     if (isHorizontalWritingMode())
399         return !isColumnFlow();
400     return isColumnFlow();
401 }
402
403 bool RenderFlexibleBox::isLeftToRightFlow() const
404 {
405     if (isColumnFlow())
406         return style().writingMode() == TopToBottomWritingMode || style().writingMode() == LeftToRightWritingMode;
407     return style().isLeftToRightDirection() ^ (style().flexDirection() == FlowRowReverse);
408 }
409
410 bool RenderFlexibleBox::isMultiline() const
411 {
412     return style().flexWrap() != FlexNoWrap;
413 }
414
415 Length RenderFlexibleBox::flexBasisForChild(const RenderBox& child) const
416 {
417     Length flexLength = child.style().flexBasis();
418     if (flexLength.isAuto())
419         flexLength = isHorizontalFlow() ? child.style().width() : child.style().height();
420     return flexLength;
421 }
422
423 LayoutUnit RenderFlexibleBox::crossAxisExtentForChild(const RenderBox& child) const
424 {
425     return isHorizontalFlow() ? child.height() : child.width();
426 }
427
428 LayoutUnit RenderFlexibleBox::cachedChildIntrinsicContentLogicalHeight(const RenderBox& child) const
429 {
430     if (child.isRenderReplaced())
431         return downcast<RenderReplaced>(child).intrinsicLogicalHeight();
432     
433     if (m_intrinsicContentLogicalHeights.contains(&child))
434         return m_intrinsicContentLogicalHeights.get(&child);
435     
436     return child.contentLogicalHeight();
437 }
438
439 void RenderFlexibleBox::setCachedChildIntrinsicContentLogicalHeight(const RenderBox& child, LayoutUnit height)
440 {
441     if (child.isRenderReplaced())
442         return; // Replaced elements know their intrinsic height already, so save space by not caching.
443     m_intrinsicContentLogicalHeights.set(&child, height);
444 }
445
446 void RenderFlexibleBox::clearCachedChildIntrinsicContentLogicalHeight(const RenderBox& child)
447 {
448     if (child.isRenderReplaced())
449         return; // Replaced elements know their intrinsic height already, so nothing to do.
450     m_intrinsicContentLogicalHeights.remove(&child);
451 }
452
453 LayoutUnit RenderFlexibleBox::childIntrinsicLogicalHeight(const RenderBox& child) const
454 {
455     // This should only be called if the logical height is the cross size
456     ASSERT(!hasOrthogonalFlow(child));
457     if (needToStretchChildLogicalHeight(child)) {
458         LayoutUnit childContentHeight = cachedChildIntrinsicContentLogicalHeight(child);
459         LayoutUnit childLogicalHeight = childContentHeight + child.scrollbarLogicalHeight() + child.borderAndPaddingLogicalHeight();
460         return child.constrainLogicalHeightByMinMax(childLogicalHeight, childContentHeight);
461     }
462     return child.logicalHeight();
463 }
464
465 LayoutUnit RenderFlexibleBox::childIntrinsicLogicalWidth(const RenderBox& child) const
466 {
467     // This should only be called if the logical width is the cross size
468     ASSERT(hasOrthogonalFlow(child));
469     // If our height is auto, make sure that our returned height is unaffected by
470     // earlier layouts by returning the max preferred logical width
471     if (!crossAxisLengthIsDefinite(child, child.style().logicalWidth()))
472         return child.maxPreferredLogicalWidth();
473     return child.logicalWidth();
474 }
475
476 LayoutUnit RenderFlexibleBox::crossAxisIntrinsicExtentForChild(const RenderBox& child) const
477 {
478     return hasOrthogonalFlow(child) ? childIntrinsicLogicalWidth(child) : childIntrinsicLogicalHeight(child);
479 }
480
481 LayoutUnit RenderFlexibleBox::mainAxisExtentForChild(const RenderBox& child) const
482 {
483     return isHorizontalFlow() ? child.size().width() : child.size().height();
484 }
485
486 LayoutUnit RenderFlexibleBox::mainAxisContentExtentForChildIncludingScrollbar(const RenderBox& child) const
487 {
488     return isHorizontalFlow() ? child.contentWidth() + child.verticalScrollbarWidth() : child.contentHeight() + child.horizontalScrollbarHeight();
489 }
490
491 LayoutUnit RenderFlexibleBox::crossAxisExtent() const
492 {
493     return isHorizontalFlow() ? size().height() : size().width();
494 }
495
496 LayoutUnit RenderFlexibleBox::mainAxisExtent() const
497 {
498     return isHorizontalFlow() ? size().width() : size().height();
499 }
500
501 LayoutUnit RenderFlexibleBox::crossAxisContentExtent() const
502 {
503     return isHorizontalFlow() ? contentHeight() : contentWidth();
504 }
505
506 LayoutUnit RenderFlexibleBox::mainAxisContentExtent(LayoutUnit contentLogicalHeight)
507 {
508     if (isColumnFlow()) {
509         LayoutUnit borderPaddingAndScrollbar = borderAndPaddingLogicalHeight() + scrollbarLogicalHeight();
510         LayoutUnit borderBoxLogicalHeight = contentLogicalHeight + borderPaddingAndScrollbar;
511         auto computedValues = computeLogicalHeight(borderBoxLogicalHeight, logicalTop());
512         if (computedValues.m_extent == LayoutUnit::max())
513             return computedValues.m_extent;
514         return std::max(LayoutUnit(), computedValues.m_extent - borderPaddingAndScrollbar);
515     }
516     return contentLogicalWidth();
517 }
518
519 std::optional<LayoutUnit> RenderFlexibleBox::computeMainAxisExtentForChild(const RenderBox& child, SizeType sizeType, const Length& size)
520 {
521     // If we have a horizontal flow, that means the main size is the width.
522     // That's the logical width for horizontal writing modes, and the logical
523     // height in vertical writing modes. For a vertical flow, main size is the
524     // height, so it's the inverse. So we need the logical width if we have a
525     // horizontal flow and horizontal writing mode, or vertical flow and vertical
526     // writing mode. Otherwise we need the logical height.
527     if (isHorizontalFlow() != child.style().isHorizontalWritingMode()) {
528         // We don't have to check for "auto" here - computeContentLogicalHeight
529         // will just return a null Optional for that case anyway. It's safe to access
530         // scrollbarLogicalHeight here because ComputeNextFlexLine will have
531         // already forced layout on the child. We previously did a layout out the child
532         // if necessary (see ComputeNextFlexLine and the call to
533         // childHasIntrinsicMainAxisSize) so we can be sure that the two height
534         // calls here will return up-to-date data.
535         std::optional<LayoutUnit> height = child.computeContentLogicalHeight(sizeType, size, cachedChildIntrinsicContentLogicalHeight(child));
536         if (!height)
537             return height;
538         return height.value() + child.scrollbarLogicalHeight();
539     }
540
541     // computeLogicalWidth always re-computes the intrinsic widths. However, when
542     // our logical width is auto, we can just use our cached value. So let's do
543     // that here. (Compare code in LayoutBlock::computePreferredLogicalWidths)
544     LayoutUnit borderAndPadding = child.borderAndPaddingLogicalWidth();
545     if (child.style().logicalWidth().isAuto() && !child.hasAspectRatio()) {
546         if (size.type() == MinContent)
547             return child.minPreferredLogicalWidth() - borderAndPadding;
548         if (size.type() == MaxContent)
549             return child.maxPreferredLogicalWidth() - borderAndPadding;
550     }
551     
552     // FIXME: Figure out how this should work for regions and pass in the appropriate values.
553     RenderFragmentContainer* fragment = nullptr;
554     return child.computeLogicalWidthInFragmentUsing(sizeType, size, contentLogicalWidth(), *this, fragment) - borderAndPadding;
555 }
556
557     
558 WritingMode RenderFlexibleBox::transformedWritingMode() const
559 {
560     WritingMode mode = style().writingMode();
561     if (!isColumnFlow())
562         return mode;
563     
564     switch (mode) {
565     case TopToBottomWritingMode:
566     case BottomToTopWritingMode:
567         return style().isLeftToRightDirection() ? LeftToRightWritingMode : RightToLeftWritingMode;
568     case LeftToRightWritingMode:
569     case RightToLeftWritingMode:
570         return style().isLeftToRightDirection() ? TopToBottomWritingMode : BottomToTopWritingMode;
571     }
572     ASSERT_NOT_REACHED();
573     return TopToBottomWritingMode;
574 }
575
576 LayoutUnit RenderFlexibleBox::flowAwareBorderStart() const
577 {
578     if (isHorizontalFlow())
579         return isLeftToRightFlow() ? borderLeft() : borderRight();
580     return isLeftToRightFlow() ? borderTop() : borderBottom();
581 }
582
583 LayoutUnit RenderFlexibleBox::flowAwareBorderEnd() const
584 {
585     if (isHorizontalFlow())
586         return isLeftToRightFlow() ? borderRight() : borderLeft();
587     return isLeftToRightFlow() ? borderBottom() : borderTop();
588 }
589
590 LayoutUnit RenderFlexibleBox::flowAwareBorderBefore() const
591 {
592     switch (transformedWritingMode()) {
593     case TopToBottomWritingMode:
594         return borderTop();
595     case BottomToTopWritingMode:
596         return borderBottom();
597     case LeftToRightWritingMode:
598         return borderLeft();
599     case RightToLeftWritingMode:
600         return borderRight();
601     }
602     ASSERT_NOT_REACHED();
603     return borderTop();
604 }
605
606 LayoutUnit RenderFlexibleBox::flowAwareBorderAfter() const
607 {
608     switch (transformedWritingMode()) {
609     case TopToBottomWritingMode:
610         return borderBottom();
611     case BottomToTopWritingMode:
612         return borderTop();
613     case LeftToRightWritingMode:
614         return borderRight();
615     case RightToLeftWritingMode:
616         return borderLeft();
617     }
618     ASSERT_NOT_REACHED();
619     return borderTop();
620 }
621
622 LayoutUnit RenderFlexibleBox::flowAwarePaddingStart() const
623 {
624     if (isHorizontalFlow())
625         return isLeftToRightFlow() ? paddingLeft() : paddingRight();
626     return isLeftToRightFlow() ? paddingTop() : paddingBottom();
627 }
628
629 LayoutUnit RenderFlexibleBox::flowAwarePaddingEnd() const
630 {
631     if (isHorizontalFlow())
632         return isLeftToRightFlow() ? paddingRight() : paddingLeft();
633     return isLeftToRightFlow() ? paddingBottom() : paddingTop();
634 }
635
636 LayoutUnit RenderFlexibleBox::flowAwarePaddingBefore() const
637 {
638     switch (transformedWritingMode()) {
639     case TopToBottomWritingMode:
640         return paddingTop();
641     case BottomToTopWritingMode:
642         return paddingBottom();
643     case LeftToRightWritingMode:
644         return paddingLeft();
645     case RightToLeftWritingMode:
646         return paddingRight();
647     }
648     ASSERT_NOT_REACHED();
649     return paddingTop();
650 }
651
652 LayoutUnit RenderFlexibleBox::flowAwarePaddingAfter() const
653 {
654     switch (transformedWritingMode()) {
655     case TopToBottomWritingMode:
656         return paddingBottom();
657     case BottomToTopWritingMode:
658         return paddingTop();
659     case LeftToRightWritingMode:
660         return paddingRight();
661     case RightToLeftWritingMode:
662         return paddingLeft();
663     }
664     ASSERT_NOT_REACHED();
665     return paddingTop();
666 }
667
668 LayoutUnit RenderFlexibleBox::flowAwareMarginStartForChild(const RenderBox& child) const
669 {
670     if (isHorizontalFlow())
671         return isLeftToRightFlow() ? child.marginLeft() : child.marginRight();
672     return isLeftToRightFlow() ? child.marginTop() : child.marginBottom();
673 }
674
675 LayoutUnit RenderFlexibleBox::flowAwareMarginEndForChild(const RenderBox& child) const
676 {
677     if (isHorizontalFlow())
678         return isLeftToRightFlow() ? child.marginRight() : child.marginLeft();
679     return isLeftToRightFlow() ? child.marginBottom() : child.marginTop();
680 }
681
682 LayoutUnit RenderFlexibleBox::flowAwareMarginBeforeForChild(const RenderBox& child) const
683 {
684     switch (transformedWritingMode()) {
685     case TopToBottomWritingMode:
686         return child.marginTop();
687     case BottomToTopWritingMode:
688         return child.marginBottom();
689     case LeftToRightWritingMode:
690         return child.marginLeft();
691     case RightToLeftWritingMode:
692         return child.marginRight();
693     }
694     ASSERT_NOT_REACHED();
695     return marginTop();
696 }
697
698 LayoutUnit RenderFlexibleBox::crossAxisMarginExtentForChild(const RenderBox& child) const
699 {
700     return isHorizontalFlow() ? child.verticalMarginExtent() : child.horizontalMarginExtent();
701 }
702
703 LayoutUnit RenderFlexibleBox::crossAxisScrollbarExtent() const
704 {
705     return isHorizontalFlow() ? horizontalScrollbarHeight() : verticalScrollbarWidth();
706 }
707
708 LayoutPoint RenderFlexibleBox::flowAwareLocationForChild(const RenderBox& child) const
709 {
710     return isHorizontalFlow() ? child.location() : child.location().transposedPoint();
711 }
712
713 bool RenderFlexibleBox::useChildAspectRatio(const RenderBox& child) const
714 {
715     if (!child.hasAspectRatio())
716         return false;
717     if (!child.intrinsicSize().height()) {
718         // We can't compute a ratio in this case.
719         return false;
720     }
721     Length crossSize;
722     if (isHorizontalFlow())
723         crossSize = child.style().height();
724     else
725         crossSize = child.style().width();
726     return crossAxisLengthIsDefinite(child, crossSize);
727 }
728
729     
730 LayoutUnit RenderFlexibleBox::computeMainSizeFromAspectRatioUsing(const RenderBox& child, Length crossSizeLength) const
731 {
732     ASSERT(child.hasAspectRatio());
733     ASSERT(child.intrinsicSize().height());
734     
735     std::optional<LayoutUnit> crossSize;
736     if (crossSizeLength.isFixed())
737         crossSize = LayoutUnit(crossSizeLength.value());
738     else {
739         ASSERT(crossSizeLength.isPercentOrCalculated());
740         crossSize = hasOrthogonalFlow(child) ? adjustBorderBoxLogicalWidthForBoxSizing(valueForLength(crossSizeLength, contentWidth())) : child.computePercentageLogicalHeight(crossSizeLength);
741         if (!crossSize)
742             return LayoutUnit();
743     }
744     
745     const LayoutSize& childIntrinsicSize = child.intrinsicSize();
746     double ratio = childIntrinsicSize.width().toFloat() /
747     childIntrinsicSize.height().toFloat();
748     if (isHorizontalFlow())
749         return LayoutUnit(crossSize.value() * ratio);
750     return LayoutUnit(crossSize.value() / ratio);
751 }
752
753 void RenderFlexibleBox::setFlowAwareLocationForChild(RenderBox& child, const LayoutPoint& location)
754 {
755     if (isHorizontalFlow())
756         child.setLocation(location);
757     else
758         child.setLocation(location.transposedPoint());
759 }
760     
761 bool RenderFlexibleBox::mainAxisLengthIsDefinite(const RenderBox& child, const Length& flexBasis) const
762 {
763     if (flexBasis.isAuto())
764         return false;
765     if (flexBasis.isPercentOrCalculated()) {
766         if (!isColumnFlow() || m_hasDefiniteHeight == SizeDefiniteness::Definite)
767             return true;
768         if (m_hasDefiniteHeight == SizeDefiniteness::Indefinite)
769             return false;
770         bool definite = child.computePercentageLogicalHeight(flexBasis) != std::nullopt;
771         if (m_inLayout) {
772             // We can reach this code even while we're not laying ourselves out, such
773             // as from mainSizeForPercentageResolution.
774             m_hasDefiniteHeight = definite ? SizeDefiniteness::Definite : SizeDefiniteness::Indefinite;
775         }
776         return definite;
777     }
778     return true;
779 }
780
781 bool RenderFlexibleBox::crossAxisLengthIsDefinite(const RenderBox& child, const Length& length) const
782 {
783     if (length.isAuto())
784         return false;
785     if (length.isPercentOrCalculated()) {
786         if (hasOrthogonalFlow(child) || m_hasDefiniteHeight == SizeDefiniteness::Definite)
787             return true;
788         if (m_hasDefiniteHeight == SizeDefiniteness::Indefinite)
789             return false;
790         bool definite = bool(child.computePercentageLogicalHeight(length));
791         m_hasDefiniteHeight = definite ? SizeDefiniteness::Definite : SizeDefiniteness::Indefinite;
792         return definite;
793     }
794     // FIXME: Eventually we should support other types of sizes here.
795     // Requires updating computeMainSizeFromAspectRatioUsing.
796     return length.isFixed();
797 }
798
799 void RenderFlexibleBox::cacheChildMainSize(const RenderBox& child)
800 {
801     ASSERT(!child.needsLayout());
802     LayoutUnit mainSize;
803     if (hasOrthogonalFlow(child))
804         mainSize = child.logicalHeight();
805     else
806         mainSize = child.maxPreferredLogicalWidth();
807     m_intrinsicSizeAlongMainAxis.set(&child, mainSize);
808     m_relaidOutChildren.add(&child);
809 }
810
811 void RenderFlexibleBox::clearCachedMainSizeForChild(const RenderBox& child)
812 {
813     m_intrinsicSizeAlongMainAxis.remove(&child);
814 }
815
816     
817 LayoutUnit RenderFlexibleBox::computeInnerFlexBaseSizeForChild(RenderBox& child, LayoutUnit mainAxisBorderAndPadding, bool relayoutChildren)
818 {
819     child.clearOverrideSize();
820     
821     Length flexBasis = flexBasisForChild(child);
822     if (mainAxisLengthIsDefinite(child, flexBasis))
823         return std::max(LayoutUnit(), computeMainAxisExtentForChild(child, MainOrPreferredSize, flexBasis).value());
824
825     // The flex basis is indefinite (=auto), so we need to compute the actual
826     // width of the child. For the logical width axis we just use the preferred
827     // width; for the height we need to lay out the child.
828     LayoutUnit mainAxisExtent;
829     if (hasOrthogonalFlow(child)) {
830         updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child);
831         if (child.needsLayout() || relayoutChildren || !m_intrinsicSizeAlongMainAxis.contains(&child)) {
832             if (!child.needsLayout())
833                 child.setChildNeedsLayout(MarkOnlyThis);
834             child.layoutIfNeeded();
835             cacheChildMainSize(child);
836         }
837         mainAxisExtent = m_intrinsicSizeAlongMainAxis.get(&child);
838     } else {
839         // We don't need to add scrollbarLogicalWidth here because the preferred
840         // width includes the scrollbar, even for overflow: auto.
841         mainAxisExtent = child.maxPreferredLogicalWidth();
842     }
843     return mainAxisExtent - mainAxisBorderAndPadding;
844 }
845
846 void RenderFlexibleBox::layoutFlexItems(bool relayoutChildren)
847 {
848     Vector<LineContext> lineContexts;
849     LayoutUnit sumFlexBaseSize;
850     double totalFlexGrow;
851     double totalFlexShrink;
852     double totalWeightedFlexShrink;
853     LayoutUnit sumHypotheticalMainSize;
854     
855     // Set up our master list of flex items. All of the rest of the algorithm
856     // should work off this list of a subset.
857     // TODO(cbiesinger): That second part is not yet true.
858     Vector<FlexItem> allItems;
859     m_orderIterator.first();
860     for (RenderBox* child = m_orderIterator.currentChild(); child; child = m_orderIterator.next()) {
861         if (m_orderIterator.shouldSkipChild(*child)) {
862             // Out-of-flow children are not flex items, so we skip them here.
863             if (child->isOutOfFlowPositioned())
864                 prepareChildForPositionedLayout(*child);
865             continue;
866         }
867         allItems.append(constructFlexItem(*child, relayoutChildren));
868     }
869     
870     const LayoutUnit lineBreakLength = mainAxisContentExtent(LayoutUnit::max());
871     FlexLayoutAlgorithm flexAlgorithm(style(), lineBreakLength, allItems);
872     LayoutUnit crossAxisOffset = flowAwareBorderBefore() + flowAwarePaddingBefore();
873     Vector<FlexItem> lineItems;
874     size_t nextIndex = 0;
875     while (flexAlgorithm.computeNextFlexLine(nextIndex, lineItems, sumFlexBaseSize, totalFlexGrow, totalFlexShrink, totalWeightedFlexShrink, sumHypotheticalMainSize)) {
876         LayoutUnit containerMainInnerSize = mainAxisContentExtent(sumHypotheticalMainSize);
877         // availableFreeSpace is the initial amount of free space in this flexbox.
878         // remainingFreeSpace starts out at the same value but as we place and lay
879         // out flex items we subtract from it. Note that both values can be
880         // negative.
881         LayoutUnit remainingFreeSpace = containerMainInnerSize - sumFlexBaseSize;
882         FlexSign flexSign = (sumHypotheticalMainSize < containerMainInnerSize) ? PositiveFlexibility : NegativeFlexibility;
883         freezeInflexibleItems(flexSign, lineItems, remainingFreeSpace, totalFlexGrow, totalFlexShrink, totalWeightedFlexShrink);
884         // The initial free space gets calculated after freezing inflexible items.
885         // https://drafts.csswg.org/css-flexbox/#resolve-flexible-lengths step 3
886         const LayoutUnit initialFreeSpace = remainingFreeSpace;
887         while (!resolveFlexibleLengths(flexSign, lineItems, initialFreeSpace, remainingFreeSpace, totalFlexGrow, totalFlexShrink, totalWeightedFlexShrink)) {
888             ASSERT(totalFlexGrow >= 0);
889             ASSERT(totalWeightedFlexShrink >= 0);
890         }
891         
892         // Recalculate the remaining free space. The adjustment for flex factors
893         // between 0..1 means we can't just use remainingFreeSpace here.
894         remainingFreeSpace = containerMainInnerSize;
895         for (size_t i = 0; i < lineItems.size(); ++i) {
896             FlexItem& flexItem = lineItems[i];
897             ASSERT(!flexItem.box.isOutOfFlowPositioned());
898             remainingFreeSpace -= flexItem.flexedMarginBoxSize();
899         }
900         // This will std::move lineItems into a newly-created LineContext.
901         layoutAndPlaceChildren(crossAxisOffset, lineItems, remainingFreeSpace, relayoutChildren, lineContexts);
902     }
903
904     if (hasLineIfEmpty()) {
905         // Even if computeNextFlexLine returns true, the flexbox might not have
906         // a line because all our children might be out of flow positioned.
907         // Instead of just checking if we have a line, make sure the flexbox
908         // has at least a line's worth of height to cover this case.
909         LayoutUnit minHeight = borderAndPaddingLogicalHeight() + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes) + scrollbarLogicalHeight();
910         if (size().height() < minHeight)
911             setLogicalHeight(minHeight);
912     }
913     
914     updateLogicalHeight();
915     repositionLogicalHeightDependentFlexItems(lineContexts);
916 }
917
918 LayoutUnit RenderFlexibleBox::autoMarginOffsetInMainAxis(const Vector<FlexItem>& children, LayoutUnit& availableFreeSpace)
919 {
920     if (availableFreeSpace <= LayoutUnit())
921         return LayoutUnit();
922     
923     int numberOfAutoMargins = 0;
924     bool isHorizontal = isHorizontalFlow();
925     for (size_t i = 0; i < children.size(); ++i) {
926         const auto& child = children[i].box;
927         ASSERT(!child.isOutOfFlowPositioned());
928         if (isHorizontal) {
929             if (child.style().marginLeft().isAuto())
930                 ++numberOfAutoMargins;
931             if (child.style().marginRight().isAuto())
932                 ++numberOfAutoMargins;
933         } else {
934             if (child.style().marginTop().isAuto())
935                 ++numberOfAutoMargins;
936             if (child.style().marginBottom().isAuto())
937                 ++numberOfAutoMargins;
938         }
939     }
940     if (!numberOfAutoMargins)
941         return LayoutUnit();
942     
943     LayoutUnit sizeOfAutoMargin = availableFreeSpace / numberOfAutoMargins;
944     availableFreeSpace = LayoutUnit();
945     return sizeOfAutoMargin;
946 }
947
948 void RenderFlexibleBox::updateAutoMarginsInMainAxis(RenderBox& child, LayoutUnit autoMarginOffset)
949 {
950     ASSERT(autoMarginOffset >= LayoutUnit());
951     
952     if (isHorizontalFlow()) {
953         if (child.style().marginLeft().isAuto())
954             child.setMarginLeft(autoMarginOffset);
955         if (child.style().marginRight().isAuto())
956             child.setMarginRight(autoMarginOffset);
957     } else {
958         if (child.style().marginTop().isAuto())
959             child.setMarginTop(autoMarginOffset);
960         if (child.style().marginBottom().isAuto())
961             child.setMarginBottom(autoMarginOffset);
962     }
963 }
964
965 bool RenderFlexibleBox::hasAutoMarginsInCrossAxis(const RenderBox& child) const
966 {
967     if (isHorizontalFlow())
968         return child.style().marginTop().isAuto() || child.style().marginBottom().isAuto();
969     return child.style().marginLeft().isAuto() || child.style().marginRight().isAuto();
970 }
971
972 LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChild(LayoutUnit lineCrossAxisExtent, const RenderBox& child)
973 {
974     ASSERT(!child.isOutOfFlowPositioned());
975     LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child);
976     return lineCrossAxisExtent - childCrossExtent;
977 }
978
979 bool RenderFlexibleBox::updateAutoMarginsInCrossAxis(RenderBox& child, LayoutUnit availableAlignmentSpace)
980 {
981     ASSERT(!child.isOutOfFlowPositioned());
982     ASSERT(availableAlignmentSpace >= LayoutUnit());
983     
984     bool isHorizontal = isHorizontalFlow();
985     Length topOrLeft = isHorizontal ? child.style().marginTop() : child.style().marginLeft();
986     Length bottomOrRight = isHorizontal ? child.style().marginBottom() : child.style().marginRight();
987     if (topOrLeft.isAuto() && bottomOrRight.isAuto()) {
988         adjustAlignmentForChild(child, availableAlignmentSpace / 2);
989         if (isHorizontal) {
990             child.setMarginTop(availableAlignmentSpace / 2);
991             child.setMarginBottom(availableAlignmentSpace / 2);
992         } else {
993             child.setMarginLeft(availableAlignmentSpace / 2);
994             child.setMarginRight(availableAlignmentSpace / 2);
995         }
996         return true;
997     }
998     bool shouldAdjustTopOrLeft = true;
999     if (isColumnFlow() && !child.style().isLeftToRightDirection()) {
1000         // For column flows, only make this adjustment if topOrLeft corresponds to
1001         // the "before" margin, so that flipForRightToLeftColumn will do the right
1002         // thing.
1003         shouldAdjustTopOrLeft = false;
1004     }
1005     if (!isColumnFlow() && child.style().isFlippedBlocksWritingMode()) {
1006         // If we are a flipped writing mode, we need to adjust the opposite side.
1007         // This is only needed for row flows because this only affects the
1008         // block-direction axis.
1009         shouldAdjustTopOrLeft = false;
1010     }
1011     
1012     if (topOrLeft.isAuto()) {
1013         if (shouldAdjustTopOrLeft)
1014             adjustAlignmentForChild(child, availableAlignmentSpace);
1015         
1016         if (isHorizontal)
1017             child.setMarginTop(availableAlignmentSpace);
1018         else
1019             child.setMarginLeft(availableAlignmentSpace);
1020         return true;
1021     }
1022
1023     if (bottomOrRight.isAuto()) {
1024         if (!shouldAdjustTopOrLeft)
1025             adjustAlignmentForChild(child, availableAlignmentSpace);
1026         
1027         if (isHorizontal)
1028             child.setMarginBottom(availableAlignmentSpace);
1029         else
1030             child.setMarginRight(availableAlignmentSpace);
1031         return true;
1032     }
1033     return false;
1034 }
1035
1036 LayoutUnit RenderFlexibleBox::marginBoxAscentForChild(const RenderBox& child)
1037 {
1038     LayoutUnit ascent = child.firstLineBaseline().value_or(crossAxisExtentForChild(child));
1039     return ascent + flowAwareMarginBeforeForChild(child);
1040 }
1041
1042 LayoutUnit RenderFlexibleBox::computeChildMarginValue(Length margin)
1043 {
1044     // When resolving the margins, we use the content size for resolving percent and calc (for percents in calc expressions) margins.
1045     // Fortunately, percent margins are always computed with respect to the block's width, even for margin-top and margin-bottom.
1046     LayoutUnit availableSize = contentLogicalWidth();
1047     return minimumValueForLength(margin, availableSize);
1048 }
1049
1050 void RenderFlexibleBox::prepareOrderIteratorAndMargins()
1051 {
1052     OrderIteratorPopulator populator(m_orderIterator);
1053
1054     for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
1055         if (!populator.collectChild(*child))
1056             continue;
1057
1058         // Before running the flex algorithm, 'auto' has a margin of 0.
1059         // Also, if we're not auto sizing, we don't do a layout that computes the start/end margins.
1060         if (isHorizontalFlow()) {
1061             child->setMarginLeft(computeChildMarginValue(child->style().marginLeft()));
1062             child->setMarginRight(computeChildMarginValue(child->style().marginRight()));
1063         } else {
1064             child->setMarginTop(computeChildMarginValue(child->style().marginTop()));
1065             child->setMarginBottom(computeChildMarginValue(child->style().marginBottom()));
1066         }
1067     }
1068 }
1069
1070 LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(const RenderBox& child, LayoutUnit childSize)
1071 {
1072     Length max = isHorizontalFlow() ? child.style().maxWidth() : child.style().maxHeight();
1073     std::optional<LayoutUnit> maxExtent = std::nullopt;
1074     if (max.isSpecifiedOrIntrinsic()) {
1075         maxExtent = computeMainAxisExtentForChild(child, MaxSize, max);
1076         childSize = std::min(childSize, maxExtent.value_or(childSize));
1077     }
1078
1079     Length min = isHorizontalFlow() ? child.style().minWidth() : child.style().minHeight();
1080     if (min.isSpecifiedOrIntrinsic())
1081         return std::max(childSize, std::max(LayoutUnit(), computeMainAxisExtentForChild(child, MinSize, min).value_or(childSize)));
1082     
1083     if (!isFlexibleBoxImpl() && min.isAuto() && mainAxisOverflowForChild(child) == OVISIBLE && !(isColumnFlow() && is<RenderFlexibleBox>(child))) {
1084         // FIXME: For now, we do not handle min-height: auto for nested
1085         // column flexboxes. We need to implement
1086         // https://drafts.csswg.org/css-flexbox/#intrinsic-sizes before that
1087         // produces reasonable results. Tracking bug: https://crbug.com/581553
1088         // css-flexbox section 4.5
1089         LayoutUnit contentSize = computeMainAxisExtentForChild(child, MinSize, Length(MinContent)).value();
1090         ASSERT(contentSize >= 0);
1091         if (child.hasAspectRatio() && child.intrinsicSize().height() > 0)
1092             contentSize = adjustChildSizeForAspectRatioCrossAxisMinAndMax(child, contentSize);
1093         contentSize = std::min(contentSize, maxExtent.value_or(contentSize));
1094         
1095         Length mainSize = isHorizontalFlow() ? child.style().width() : child.style().height();
1096         if (mainAxisLengthIsDefinite(child, mainSize)) {
1097             LayoutUnit resolvedMainSize = computeMainAxisExtentForChild(child, MainOrPreferredSize, mainSize).value();
1098             ASSERT(resolvedMainSize >= 0);
1099             LayoutUnit specifiedSize = std::min(resolvedMainSize, maxExtent.value_or(resolvedMainSize));
1100             return std::max(childSize, std::min(specifiedSize, contentSize));
1101         }
1102
1103         if (useChildAspectRatio(child)) {
1104             Length crossSizeLength = isHorizontalFlow() ? child.style().height() : child.style().width();
1105             std::optional<LayoutUnit> transferredSize = computeMainSizeFromAspectRatioUsing(child, crossSizeLength);
1106             if (transferredSize) {
1107                 transferredSize = adjustChildSizeForAspectRatioCrossAxisMinAndMax(child, transferredSize.value());
1108                 return std::max(childSize, std::min(transferredSize.value(), contentSize));
1109             }
1110         }
1111         
1112         return std::max(childSize, contentSize);
1113     }
1114
1115     return std::max(LayoutUnit(), childSize);
1116 }
1117     
1118 std::optional<LayoutUnit> RenderFlexibleBox::crossSizeForPercentageResolution(const RenderBox& child)
1119 {
1120     if (alignmentForChild(child) != ItemPositionStretch)
1121         return std::nullopt;
1122
1123     // Here we implement https://drafts.csswg.org/css-flexbox/#algo-stretch
1124     if (hasOrthogonalFlow(child) && child.hasOverrideLogicalContentWidth())
1125         return child.overrideLogicalContentWidth();
1126     if (!hasOrthogonalFlow(child) && child.hasOverrideLogicalContentHeight())
1127         return child.overrideLogicalContentHeight();
1128     
1129     // We don't currently implement the optimization from
1130     // https://drafts.csswg.org/css-flexbox/#definite-sizes case 1. While that
1131     // could speed up a specialized case, it requires determining if we have a
1132     // definite size, which itself is not cheap. We can consider implementing it
1133     // at a later time. (The correctness is ensured by redoing layout in
1134     // applyStretchAlignmentToChild)
1135     return std::nullopt;
1136 }
1137
1138 std::optional<LayoutUnit> RenderFlexibleBox::mainSizeForPercentageResolution(const RenderBox& child)
1139 {
1140     // This function implements section 9.8. Definite and Indefinite Sizes, case
1141     // 2) of the flexbox spec.
1142     // We need to check for the flexbox to have a definite main size, and for the
1143     // flex item to have a definite flex basis.
1144     const Length& flexBasis = flexBasisForChild(child);
1145     if (!mainAxisLengthIsDefinite(child, flexBasis))
1146         return std::nullopt;
1147     if (!flexBasis.isPercentOrCalculated()) {
1148         // If flex basis had a percentage, our size is guaranteed to be definite or
1149         // the flex item's size could not be definite. Otherwise, we make up a
1150         // percentage to check whether we have a definite size.
1151         if (!mainAxisLengthIsDefinite(child, Length(0, Percent)))
1152             return std::nullopt;
1153     }
1154     
1155     if (hasOrthogonalFlow(child))
1156         return child.hasOverrideLogicalContentHeight() ? std::optional<LayoutUnit>(child.overrideLogicalContentHeight()) : std::nullopt;
1157     return child.hasOverrideLogicalContentWidth() ? std::optional<LayoutUnit>(child.overrideLogicalContentWidth()) : std::nullopt;
1158 }
1159
1160 std::optional<LayoutUnit> RenderFlexibleBox::childLogicalHeightForPercentageResolution(const RenderBox& child)
1161 {
1162     if (!hasOrthogonalFlow(child))
1163         return crossSizeForPercentageResolution(child);
1164     return mainSizeForPercentageResolution(child);
1165 }
1166
1167 LayoutUnit RenderFlexibleBox::adjustChildSizeForAspectRatioCrossAxisMinAndMax(const RenderBox& child, LayoutUnit childSize)
1168 {
1169     Length crossMin = isHorizontalFlow() ? child.style().minHeight() : child.style().minWidth();
1170     Length crossMax = isHorizontalFlow() ? child.style().maxHeight() : child.style().maxWidth();
1171     
1172     if (crossAxisLengthIsDefinite(child, crossMax)) {
1173         LayoutUnit maxValue = computeMainSizeFromAspectRatioUsing(child, crossMax);
1174         childSize = std::min(maxValue, childSize);
1175     }
1176     
1177     if (crossAxisLengthIsDefinite(child, crossMin)) {
1178         LayoutUnit minValue = computeMainSizeFromAspectRatioUsing(child, crossMin);
1179         childSize = std::max(minValue, childSize);
1180     }
1181     
1182     return childSize;
1183 }
1184
1185 FlexItem RenderFlexibleBox::constructFlexItem(RenderBox& child, bool relayoutChildren)
1186 {
1187     // If this condition is true, then computeMainAxisExtentForChild will call
1188     // child.intrinsicContentLogicalHeight() and
1189     // child.scrollbarLogicalHeight(), so if the child has intrinsic
1190     // min/max/preferred size, run layout on it now to make sure its logical
1191     // height and scroll bars are up to date.
1192     if (childHasIntrinsicMainAxisSize(child) && child.needsLayout()) {
1193         child.clearOverrideSize();
1194         child.setChildNeedsLayout(MarkOnlyThis);
1195         child.layoutIfNeeded();
1196         cacheChildMainSize(child);
1197         relayoutChildren = false;
1198     }
1199     
1200     LayoutUnit borderAndPadding = isHorizontalFlow() ? child.horizontalBorderAndPaddingExtent() : child.verticalBorderAndPaddingExtent();
1201     LayoutUnit childInnerFlexBaseSize = computeInnerFlexBaseSizeForChild(child, borderAndPadding, relayoutChildren);
1202     LayoutUnit childMinMaxAppliedMainAxisExtent = adjustChildSizeForMinAndMax(child, childInnerFlexBaseSize);
1203     LayoutUnit margin = isHorizontalFlow() ? child.horizontalMarginExtent() : child.verticalMarginExtent();
1204     return FlexItem(child, childInnerFlexBaseSize, childMinMaxAppliedMainAxisExtent, borderAndPadding, margin);
1205 }
1206     
1207 void RenderFlexibleBox::freezeViolations(Vector<FlexItem*>& violations, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalFlexShrink, double& totalWeightedFlexShrink)
1208 {
1209     for (size_t i = 0; i < violations.size(); ++i) {
1210         ASSERT(!violations[i]->frozen);
1211         const auto& child = violations[i]->box;
1212         LayoutUnit childSize = violations[i]->flexedContentSize;
1213         availableFreeSpace -= childSize - violations[i]->flexBaseContentSize;
1214         totalFlexGrow -= child.style().flexGrow();
1215         totalFlexShrink -= child.style().flexShrink();
1216         totalWeightedFlexShrink -= child.style().flexShrink() * violations[i]->flexBaseContentSize;
1217         // totalWeightedFlexShrink can be negative when we exceed the precision of
1218         // a double when we initially calcuate totalWeightedFlexShrink. We then
1219         // subtract each child's weighted flex shrink with full precision, now
1220         // leading to a negative result. See
1221         // css3/flexbox/large-flex-shrink-assert.html
1222         totalWeightedFlexShrink = std::max(totalWeightedFlexShrink, 0.0);
1223         violations[i]->frozen = true;
1224     }
1225 }
1226     
1227 void RenderFlexibleBox::freezeInflexibleItems(FlexSign flexSign, Vector<FlexItem>& children, LayoutUnit& remainingFreeSpace, double& totalFlexGrow, double& totalFlexShrink, double& totalWeightedFlexShrink)
1228 {
1229     // Per https://drafts.csswg.org/css-flexbox/#resolve-flexible-lengths step 2,
1230     // we freeze all items with a flex factor of 0 as well as those with a min/max
1231     // size violation.
1232     Vector<FlexItem*> newInflexibleItems;
1233     for (size_t i = 0; i < children.size(); ++i) {
1234         FlexItem& flexItem = children[i];
1235         const auto& child = flexItem.box;
1236         ASSERT(!flexItem.box.isOutOfFlowPositioned());
1237         ASSERT(!flexItem.frozen);
1238         float flexFactor = (flexSign == PositiveFlexibility) ? child.style().flexGrow() : child.style().flexShrink();
1239         if (!flexFactor || (flexSign == PositiveFlexibility && flexItem.flexBaseContentSize > flexItem.hypotheticalMainContentSize) || (flexSign == NegativeFlexibility && flexItem.flexBaseContentSize < flexItem.hypotheticalMainContentSize)) {
1240             flexItem.flexedContentSize = flexItem.hypotheticalMainContentSize;
1241             newInflexibleItems.append(&flexItem);
1242         }
1243     }
1244     freezeViolations(newInflexibleItems, remainingFreeSpace, totalFlexGrow, totalFlexShrink, totalWeightedFlexShrink);
1245 }
1246
1247 // Returns true if we successfully ran the algorithm and sized the flex items.
1248 bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, Vector<FlexItem>& children, LayoutUnit initialFreeSpace, LayoutUnit& remainingFreeSpace, double& totalFlexGrow, double& totalFlexShrink, double& totalWeightedFlexShrink)
1249 {
1250     LayoutUnit totalViolation;
1251     LayoutUnit usedFreeSpace;
1252     Vector<FlexItem*> minViolations;
1253     Vector<FlexItem*> maxViolations;
1254
1255     double sumFlexFactors = (flexSign == PositiveFlexibility) ? totalFlexGrow : totalFlexShrink;
1256     if (sumFlexFactors > 0 && sumFlexFactors < 1) {
1257         LayoutUnit fractional(initialFreeSpace * sumFlexFactors);
1258         if (fractional.abs() < remainingFreeSpace.abs())
1259             remainingFreeSpace = fractional;
1260     }
1261
1262     for (size_t i = 0; i < children.size(); ++i) {
1263         FlexItem& flexItem = children[i];
1264         const auto& child = flexItem.box;
1265         
1266         // This check also covers out-of-flow children.
1267         if (flexItem.frozen)
1268             continue;
1269         
1270         LayoutUnit childSize = flexItem.flexBaseContentSize;
1271         double extraSpace = 0;
1272         if (remainingFreeSpace > 0 && totalFlexGrow > 0 && flexSign == PositiveFlexibility && std::isfinite(totalFlexGrow))
1273             extraSpace = remainingFreeSpace * child.style().flexGrow() / totalFlexGrow;
1274         else if (remainingFreeSpace < 0 && totalWeightedFlexShrink > 0 && flexSign == NegativeFlexibility && std::isfinite(totalWeightedFlexShrink) && child.style().flexShrink())
1275             extraSpace = remainingFreeSpace * child.style().flexShrink() * flexItem.flexBaseContentSize / totalWeightedFlexShrink;
1276         if (std::isfinite(extraSpace))
1277             childSize += LayoutUnit::fromFloatRound(extraSpace);
1278         
1279         LayoutUnit adjustedChildSize = adjustChildSizeForMinAndMax(child, childSize);
1280         ASSERT(adjustedChildSize >= 0);
1281         flexItem.flexedContentSize = adjustedChildSize;
1282         usedFreeSpace += adjustedChildSize - flexItem.flexBaseContentSize;
1283         
1284         LayoutUnit violation = adjustedChildSize - childSize;
1285         if (violation > 0)
1286             minViolations.append(&flexItem);
1287         else if (violation < 0)
1288             maxViolations.append(&flexItem);
1289         totalViolation += violation;
1290     }
1291     
1292     if (totalViolation)
1293         freezeViolations(totalViolation < 0 ? maxViolations : minViolations, remainingFreeSpace, totalFlexGrow, totalFlexShrink, totalWeightedFlexShrink);
1294     else
1295         remainingFreeSpace -= usedFreeSpace;
1296     
1297     return !totalViolation;
1298 }
1299
1300 static LayoutUnit initialJustifyContentOffset(LayoutUnit availableFreeSpace, ContentPosition justifyContent, ContentDistributionType justifyContentDistribution, unsigned numberOfChildren)
1301 {
1302     if (justifyContent == ContentPositionFlexEnd)
1303         return availableFreeSpace;
1304     if (justifyContent == ContentPositionCenter)
1305         return availableFreeSpace / 2;
1306     if (justifyContentDistribution == ContentDistributionSpaceAround) {
1307         if (availableFreeSpace > 0 && numberOfChildren)
1308             return availableFreeSpace / (2 * numberOfChildren);
1309         else
1310             return availableFreeSpace / 2;
1311     }
1312     if (justifyContentDistribution == ContentDistributionSpaceEvenly) {
1313         if (availableFreeSpace > 0 && numberOfChildren)
1314             return availableFreeSpace / (numberOfChildren + 1);
1315         // Fallback to 'center'
1316         return availableFreeSpace / 2;
1317     }
1318     return 0;
1319 }
1320
1321 static LayoutUnit justifyContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, ContentDistributionType justifyContentDistribution, unsigned numberOfChildren)
1322 {
1323     if (availableFreeSpace > 0 && numberOfChildren > 1) {
1324         if (justifyContentDistribution == ContentDistributionSpaceBetween)
1325             return availableFreeSpace / (numberOfChildren - 1);
1326         if (justifyContentDistribution == ContentDistributionSpaceAround)
1327             return availableFreeSpace / numberOfChildren;
1328         if (justifyContentDistribution == ContentDistributionSpaceEvenly)
1329             return availableFreeSpace / (numberOfChildren + 1);
1330     }
1331     return 0;
1332 }
1333
1334
1335 static LayoutUnit alignmentOffset(LayoutUnit availableFreeSpace, ItemPosition position, LayoutUnit ascent, LayoutUnit maxAscent, bool isWrapReverse)
1336 {
1337     switch (position) {
1338     case ItemPositionAuto:
1339     case ItemPositionNormal:
1340         ASSERT_NOT_REACHED();
1341         break;
1342     case ItemPositionStretch:
1343         // Actual stretching must be handled by the caller. Since wrap-reverse
1344         // flips cross start and cross end, stretch children should be aligned
1345         // with the cross end. This matters because applyStretchAlignment
1346         // doesn't always stretch or stretch fully (explicit cross size given, or
1347         // stretching constrained by max-height/max-width). For flex-start and
1348         // flex-end this is handled by alignmentForChild().
1349         if (isWrapReverse)
1350             return availableFreeSpace;
1351         break;
1352     case ItemPositionFlexStart:
1353         break;
1354     case ItemPositionFlexEnd:
1355         return availableFreeSpace;
1356     case ItemPositionCenter:
1357         return availableFreeSpace / 2;
1358     case ItemPositionBaseline:
1359         // FIXME: If we get here in columns, we want the use the descent, except
1360         // we currently can't get the ascent/descent of orthogonal children.
1361         // https://bugs.webkit.org/show_bug.cgi?id=98076
1362         return maxAscent - ascent;
1363     case ItemPositionLastBaseline:
1364     case ItemPositionSelfStart:
1365     case ItemPositionSelfEnd:
1366     case ItemPositionStart:
1367     case ItemPositionEnd:
1368     case ItemPositionLeft:
1369     case ItemPositionRight:
1370         // FIXME: Implement these. The extended grammar
1371         // is not enabled by default so we shouldn't hit this codepath.
1372         // The new grammar is only used when Grid Layout feature is enabled.
1373         ASSERT(RuntimeEnabledFeatures::sharedFeatures().isCSSGridLayoutEnabled());
1374         break;
1375     }
1376     return 0;
1377 }
1378
1379 void RenderFlexibleBox::setOverrideMainAxisContentSizeForChild(RenderBox& child, LayoutUnit childPreferredSize)
1380 {
1381     if (hasOrthogonalFlow(child))
1382         child.setOverrideLogicalContentHeight(childPreferredSize);
1383     else
1384         child.setOverrideLogicalContentWidth(childPreferredSize);
1385 }
1386
1387 LayoutUnit RenderFlexibleBox::staticMainAxisPositionForPositionedChild(const RenderBox& child)
1388 {
1389     const LayoutUnit availableSpace = mainAxisContentExtent(contentLogicalHeight()) - mainAxisExtentForChild(child);
1390
1391     ContentPosition position = style().resolvedJustifyContentPosition(contentAlignmentNormalBehavior());
1392     ContentDistributionType distribution = style().resolvedJustifyContentDistribution(contentAlignmentNormalBehavior());
1393     LayoutUnit offset = initialJustifyContentOffset(availableSpace, position, distribution, 1);
1394     if (style().flexDirection() == FlowRowReverse || style().flexDirection() == FlowColumnReverse)
1395         offset = availableSpace - offset;
1396     return offset;
1397 }
1398
1399 LayoutUnit RenderFlexibleBox::staticCrossAxisPositionForPositionedChild(const RenderBox& child)
1400 {
1401     LayoutUnit availableSpace = crossAxisContentExtent() - crossAxisExtentForChild(child);
1402     return alignmentOffset(availableSpace, alignmentForChild(child), LayoutUnit(), LayoutUnit(), style().flexWrap() == FlexWrapReverse);
1403 }
1404
1405 LayoutUnit RenderFlexibleBox::staticInlinePositionForPositionedChild(const RenderBox& child)
1406 {
1407     return startOffsetForContent() + (isColumnFlow() ? staticCrossAxisPositionForPositionedChild(child) : staticMainAxisPositionForPositionedChild(child));
1408 }
1409
1410 LayoutUnit RenderFlexibleBox::staticBlockPositionForPositionedChild(const RenderBox& child)
1411 {
1412     return borderAndPaddingBefore() + (isColumnFlow() ? staticMainAxisPositionForPositionedChild(child) : staticCrossAxisPositionForPositionedChild(child));
1413 }
1414
1415 bool RenderFlexibleBox::setStaticPositionForPositionedLayout(const RenderBox& child)
1416 {
1417     bool positionChanged = false;
1418     auto* childLayer = child.layer();
1419     if (child.style().hasStaticInlinePosition(style().isHorizontalWritingMode())) {
1420         LayoutUnit inlinePosition = staticInlinePositionForPositionedChild(child);
1421         if (childLayer->staticInlinePosition() != inlinePosition) {
1422             childLayer->setStaticInlinePosition(inlinePosition);
1423             positionChanged = true;
1424         }
1425     }
1426     if (child.style().hasStaticBlockPosition(style().isHorizontalWritingMode())) {
1427         LayoutUnit blockPosition = staticBlockPositionForPositionedChild(child);
1428         if (childLayer->staticBlockPosition() != blockPosition) {
1429             childLayer->setStaticBlockPosition(blockPosition);
1430             positionChanged = true;
1431         }
1432     }
1433     return positionChanged;
1434 }
1435
1436 void RenderFlexibleBox::prepareChildForPositionedLayout(RenderBox& child)
1437 {
1438     ASSERT(child.isOutOfFlowPositioned());
1439     child.containingBlock()->insertPositionedObject(child);
1440     auto* childLayer = child.layer();
1441     LayoutUnit staticInlinePosition = flowAwareBorderStart() + flowAwarePaddingStart();
1442     if (childLayer->staticInlinePosition() != staticInlinePosition) {
1443         childLayer->setStaticInlinePosition(staticInlinePosition);
1444         if (child.style().hasStaticInlinePosition(style().isHorizontalWritingMode()))
1445             child.setChildNeedsLayout(MarkOnlyThis);
1446     }
1447
1448     LayoutUnit staticBlockPosition = flowAwareBorderBefore() + flowAwarePaddingBefore();
1449     if (childLayer->staticBlockPosition() != staticBlockPosition) {
1450         childLayer->setStaticBlockPosition(staticBlockPosition);
1451         if (child.style().hasStaticBlockPosition(style().isHorizontalWritingMode()))
1452             child.setChildNeedsLayout(MarkOnlyThis);
1453     }
1454 }
1455
1456 ItemPosition RenderFlexibleBox::alignmentForChild(const RenderBox& child) const
1457 {
1458     ItemPosition align = child.style().resolvedAlignSelf(&style(), selfAlignmentNormalBehavior()).position();
1459     ASSERT(align != ItemPositionAuto && align != ItemPositionNormal);
1460
1461     if (align == ItemPositionBaseline && hasOrthogonalFlow(child))
1462         align = ItemPositionFlexStart;
1463
1464     if (style().flexWrap() == FlexWrapReverse) {
1465         if (align == ItemPositionFlexStart)
1466             align = ItemPositionFlexEnd;
1467         else if (align == ItemPositionFlexEnd)
1468             align = ItemPositionFlexStart;
1469     }
1470
1471     return align;
1472 }
1473
1474 void RenderFlexibleBox::resetAutoMarginsAndLogicalTopInCrossAxis(RenderBox& child)
1475 {
1476     if (hasAutoMarginsInCrossAxis(child)) {
1477         child.updateLogicalHeight();
1478         if (isHorizontalFlow()) {
1479             if (child.style().marginTop().isAuto())
1480                 child.setMarginTop(LayoutUnit());
1481             if (child.style().marginBottom().isAuto())
1482                 child.setMarginBottom(LayoutUnit());
1483         } else {
1484             if (child.style().marginLeft().isAuto())
1485                 child.setMarginLeft(LayoutUnit());
1486             if (child.style().marginRight().isAuto())
1487                 child.setMarginRight(LayoutUnit());
1488         }
1489     }
1490 }
1491
1492 bool RenderFlexibleBox::needToStretchChildLogicalHeight(const RenderBox& child) const
1493 {
1494     // This function is a little bit magical. It relies on the fact that blocks
1495     // intrinsically "stretch" themselves in their inline axis, i.e. a <div> has
1496     // an implicit width: 100%. So the child will automatically stretch if our
1497     // cross axis is the child's inline axis. That's the case if:
1498     // - We are horizontal and the child is in vertical writing mode
1499     // - We are vertical and the child is in horizontal writing mode
1500     // Otherwise, we need to stretch if the cross axis size is auto.
1501     if (alignmentForChild(child) != ItemPositionStretch)
1502         return false;
1503
1504     if (isHorizontalFlow() != child.style().isHorizontalWritingMode())
1505         return false;
1506
1507     return child.style().logicalHeight().isAuto();
1508 }
1509
1510 bool RenderFlexibleBox::childHasIntrinsicMainAxisSize(const RenderBox& child) const
1511 {
1512     bool result = false;
1513     if (isHorizontalFlow() != child.style().isHorizontalWritingMode()) {
1514         Length childFlexBasis = flexBasisForChild(child);
1515         Length childMinSize = isHorizontalFlow() ? child.style().minWidth() : child.style().minHeight();
1516         Length childMaxSize = isHorizontalFlow() ? child.style().maxWidth() : child.style().maxHeight();
1517         if (childFlexBasis.isIntrinsic() || childMinSize.isIntrinsicOrAuto() || childMaxSize.isIntrinsic())
1518             result = true;
1519     }
1520     return result;
1521 }
1522
1523 EOverflow RenderFlexibleBox::mainAxisOverflowForChild(const RenderBox& child) const
1524 {
1525     if (isHorizontalFlow())
1526         return child.style().overflowX();
1527     return child.style().overflowY();
1528 }
1529
1530 EOverflow RenderFlexibleBox::crossAxisOverflowForChild(const RenderBox& child) const
1531 {
1532     if (isHorizontalFlow())
1533         return child.style().overflowY();
1534     return child.style().overflowX();
1535 }
1536
1537 void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, Vector<FlexItem>& children, LayoutUnit availableFreeSpace, bool relayoutChildren, Vector<LineContext>& lineContexts)
1538 {
1539     ContentPosition position = style().resolvedJustifyContentPosition(contentAlignmentNormalBehavior());
1540     ContentDistributionType distribution = style().resolvedJustifyContentDistribution(contentAlignmentNormalBehavior());
1541
1542     LayoutUnit autoMarginOffset = autoMarginOffsetInMainAxis(children, availableFreeSpace);
1543     LayoutUnit mainAxisOffset = flowAwareBorderStart() + flowAwarePaddingStart();
1544     mainAxisOffset += initialJustifyContentOffset(availableFreeSpace, position, distribution, children.size());
1545     if (style().flexDirection() == FlowRowReverse)
1546         mainAxisOffset += isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight();
1547
1548     LayoutUnit totalMainExtent = mainAxisExtent();
1549     LayoutUnit maxAscent, maxDescent; // Used when align-items: baseline.
1550     LayoutUnit maxChildCrossAxisExtent;
1551     bool shouldFlipMainAxis = !isColumnFlow() && !isLeftToRightFlow();
1552     for (size_t i = 0; i < children.size(); ++i) {
1553         const auto& flexItem = children[i];
1554         auto& child = flexItem.box;
1555
1556         ASSERT(!flexItem.box.isOutOfFlowPositioned());
1557
1558         setOverrideMainAxisContentSizeForChild(child, flexItem.flexedContentSize);
1559         // The flexed content size and the override size include the scrollbar
1560         // width, so we need to compare to the size including the scrollbar.
1561         // TODO(cbiesinger): Should it include the scrollbar?
1562         if (flexItem.flexedContentSize != mainAxisContentExtentForChildIncludingScrollbar(child))
1563             child.setChildNeedsLayout(MarkOnlyThis);
1564         else {
1565             // To avoid double applying margin changes in
1566             // updateAutoMarginsInCrossAxis, we reset the margins here.
1567             resetAutoMarginsAndLogicalTopInCrossAxis(child);
1568         }
1569         // We may have already forced relayout for orthogonal flowing children in
1570         // computeInnerFlexBaseSizeForChild.
1571         bool forceChildRelayout = relayoutChildren && !m_relaidOutChildren.contains(&child);
1572         if (child.isRenderBlock() && downcast<RenderBlock>(child).hasPercentHeightDescendants()) {
1573             // Have to force another relayout even though the child is sized
1574             // correctly, because its descendants are not sized correctly yet. Our
1575             // previous layout of the child was done without an override height set.
1576             // So, redo it here.
1577             forceChildRelayout = true;
1578         }
1579         updateBlockChildDirtyBitsBeforeLayout(forceChildRelayout, child);
1580         if (!child.needsLayout())
1581             child.markForPaginationRelayoutIfNeeded();
1582         if (child.needsLayout())
1583             m_relaidOutChildren.add(&child);
1584         child.layoutIfNeeded();
1585
1586         updateAutoMarginsInMainAxis(child, autoMarginOffset);
1587
1588         LayoutUnit childCrossAxisMarginBoxExtent;
1589         if (alignmentForChild(child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(child)) {
1590             LayoutUnit ascent = marginBoxAscentForChild(child);
1591             LayoutUnit descent = (crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child)) - ascent;
1592
1593             maxAscent = std::max(maxAscent, ascent);
1594             maxDescent = std::max(maxDescent, descent);
1595
1596             // FIXME: Take scrollbar into account
1597             childCrossAxisMarginBoxExtent = maxAscent + maxDescent;
1598         } else
1599             childCrossAxisMarginBoxExtent = crossAxisIntrinsicExtentForChild(child) + crossAxisMarginExtentForChild(child);
1600
1601         if (!isColumnFlow())
1602             setLogicalHeight(std::max(logicalHeight(), crossAxisOffset + flowAwareBorderAfter() + flowAwarePaddingAfter() + childCrossAxisMarginBoxExtent + crossAxisScrollbarExtent()));
1603         maxChildCrossAxisExtent = std::max(maxChildCrossAxisExtent, childCrossAxisMarginBoxExtent);
1604
1605         mainAxisOffset += flowAwareMarginStartForChild(child);
1606
1607         LayoutUnit childMainExtent = mainAxisExtentForChild(child);
1608         // In an RTL column situation, this will apply the margin-right/margin-end
1609         // on the left. This will be fixed later in flipForRightToLeftColumn.
1610         LayoutPoint childLocation(shouldFlipMainAxis ? totalMainExtent - mainAxisOffset - childMainExtent : mainAxisOffset, crossAxisOffset + flowAwareMarginBeforeForChild(child));
1611         setFlowAwareLocationForChild(child, childLocation);
1612         mainAxisOffset += childMainExtent + flowAwareMarginEndForChild(child);
1613
1614         if (i != children.size() - 1) {
1615             // The last item does not get extra space added.
1616             mainAxisOffset += justifyContentSpaceBetweenChildren(availableFreeSpace, distribution, children.size());
1617         }
1618
1619         // FIXME: Deal with pagination.
1620     }
1621
1622     if (isColumnFlow())
1623         setLogicalHeight(std::max(logicalHeight(), mainAxisOffset + flowAwareBorderEnd() + flowAwarePaddingEnd() + scrollbarLogicalHeight()));
1624
1625     if (style().flexDirection() == FlowColumnReverse) {
1626         // We have to do an extra pass for column-reverse to reposition the flex
1627         // items since the start depends on the height of the flexbox, which we
1628         // only know after we've positioned all the flex items.
1629         updateLogicalHeight();
1630         layoutColumnReverse(children, crossAxisOffset, availableFreeSpace);
1631     }
1632
1633     if (m_numberOfInFlowChildrenOnFirstLine == -1)
1634         m_numberOfInFlowChildrenOnFirstLine = children.size();
1635     lineContexts.append(LineContext(crossAxisOffset, maxChildCrossAxisExtent, maxAscent, WTFMove(children)));
1636     crossAxisOffset += maxChildCrossAxisExtent;
1637 }
1638
1639 void RenderFlexibleBox::layoutColumnReverse(const Vector<FlexItem>& children, LayoutUnit crossAxisOffset, LayoutUnit availableFreeSpace)
1640 {
1641     ContentPosition position = style().resolvedJustifyContentPosition(contentAlignmentNormalBehavior());
1642     ContentDistributionType distribution = style().resolvedJustifyContentDistribution(contentAlignmentNormalBehavior());
1643     
1644     // This is similar to the logic in layoutAndPlaceChildren, except we place
1645     // the children starting from the end of the flexbox. We also don't need to
1646     // layout anything since we're just moving the children to a new position.
1647     LayoutUnit mainAxisOffset = logicalHeight() - flowAwareBorderEnd() - flowAwarePaddingEnd();
1648     mainAxisOffset -= initialJustifyContentOffset(availableFreeSpace, position, distribution, children.size());
1649     mainAxisOffset -= isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight();
1650     
1651     for (size_t i = 0; i < children.size(); ++i) {
1652         auto& child = children[i].box;
1653         ASSERT(!child.isOutOfFlowPositioned());
1654         mainAxisOffset -= mainAxisExtentForChild(child) + flowAwareMarginEndForChild(child);
1655         setFlowAwareLocationForChild(child, LayoutPoint(mainAxisOffset, crossAxisOffset + flowAwareMarginBeforeForChild(child)));
1656         mainAxisOffset -= flowAwareMarginStartForChild(child);
1657         mainAxisOffset -= justifyContentSpaceBetweenChildren(availableFreeSpace, distribution, children.size());
1658     }
1659 }
1660     
1661 static LayoutUnit initialAlignContentOffset(LayoutUnit availableFreeSpace, ContentPosition alignContent, ContentDistributionType alignContentDistribution, unsigned numberOfLines)
1662 {
1663     if (numberOfLines <= 1)
1664         return LayoutUnit();
1665     if (alignContent == ContentPositionFlexEnd)
1666         return availableFreeSpace;
1667     if (alignContent == ContentPositionCenter)
1668         return availableFreeSpace / 2;
1669     if (alignContentDistribution == ContentDistributionSpaceAround) {
1670         if (availableFreeSpace > 0 && numberOfLines)
1671             return availableFreeSpace / (2 * numberOfLines);
1672         if (availableFreeSpace < 0)
1673             return availableFreeSpace / 2;
1674     }
1675     if (alignContentDistribution == ContentDistributionSpaceEvenly) {
1676         if (availableFreeSpace > 0)
1677             return availableFreeSpace / (numberOfLines + 1);
1678         // Fallback to 'center'
1679         return availableFreeSpace / 2;
1680     }
1681     return LayoutUnit();
1682 }
1683
1684 static LayoutUnit alignContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, ContentDistributionType alignContentDistribution, unsigned numberOfLines)
1685 {
1686     if (availableFreeSpace > 0 && numberOfLines > 1) {
1687         if (alignContentDistribution == ContentDistributionSpaceBetween)
1688             return availableFreeSpace / (numberOfLines - 1);
1689         if (alignContentDistribution == ContentDistributionSpaceAround || alignContentDistribution == ContentDistributionStretch)
1690             return availableFreeSpace / numberOfLines;
1691         if (alignContentDistribution == ContentDistributionSpaceEvenly)
1692             return availableFreeSpace / (numberOfLines + 1);
1693     }
1694     return LayoutUnit();
1695 }
1696     
1697 void RenderFlexibleBox::alignFlexLines(Vector<LineContext>& lineContexts)
1698 {
1699     ContentPosition position = style().resolvedAlignContentPosition(contentAlignmentNormalBehavior());
1700     ContentDistributionType distribution = style().resolvedAlignContentDistribution(contentAlignmentNormalBehavior());
1701     
1702     // If we have a single line flexbox or a multiline line flexbox with only one
1703     // flex line, the line height is all the available space. For
1704     // flex-direction: row, this means we need to use the height, so we do this
1705     // after calling updateLogicalHeight.
1706     if (lineContexts.size() == 1) {
1707         lineContexts[0].crossAxisExtent = crossAxisContentExtent();
1708         return;
1709     }
1710     
1711     if (position == ContentPositionFlexStart)
1712         return;
1713     
1714     LayoutUnit availableCrossAxisSpace = crossAxisContentExtent();
1715     for (size_t i = 0; i < lineContexts.size(); ++i)
1716         availableCrossAxisSpace -= lineContexts[i].crossAxisExtent;
1717     
1718     LayoutUnit lineOffset = initialAlignContentOffset(availableCrossAxisSpace, position, distribution, lineContexts.size());
1719     for (unsigned lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1720         LineContext& lineContext = lineContexts[lineNumber];
1721         lineContext.crossAxisOffset += lineOffset;
1722         for (size_t childNumber = 0; childNumber < lineContext.flexItems.size(); ++childNumber) {
1723             FlexItem& flexItem = lineContext.flexItems[childNumber];
1724             adjustAlignmentForChild(flexItem.box, lineOffset);
1725         }
1726         
1727         if (distribution == ContentDistributionStretch && availableCrossAxisSpace > 0)
1728             lineContexts[lineNumber].crossAxisExtent += availableCrossAxisSpace / static_cast<unsigned>(lineContexts.size());
1729         
1730         lineOffset += alignContentSpaceBetweenChildren(availableCrossAxisSpace, distribution, lineContexts.size());
1731     }
1732 }
1733     
1734 void RenderFlexibleBox::adjustAlignmentForChild(RenderBox& child, LayoutUnit delta)
1735 {
1736     ASSERT(!child.isOutOfFlowPositioned());
1737     setFlowAwareLocationForChild(child, flowAwareLocationForChild(child) + LayoutSize(LayoutUnit(), delta));
1738 }
1739     
1740 void RenderFlexibleBox::alignChildren(const Vector<LineContext>& lineContexts)
1741 {
1742     // Keep track of the space between the baseline edge and the after edge of
1743     // the box for each line.
1744     Vector<LayoutUnit> minMarginAfterBaselines;
1745     
1746     for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1747         const LineContext& lineContext = lineContexts[lineNumber];
1748         
1749         LayoutUnit minMarginAfterBaseline = LayoutUnit::max();
1750         LayoutUnit lineCrossAxisExtent = lineContext.crossAxisExtent;
1751         LayoutUnit maxAscent = lineContext.maxAscent;
1752         
1753         for (size_t childNumber = 0; childNumber < lineContext.flexItems.size(); ++childNumber) {
1754             const auto& flexItem = lineContext.flexItems[childNumber];
1755             ASSERT(!flexItem.box.isOutOfFlowPositioned());
1756             
1757             if (updateAutoMarginsInCrossAxis(flexItem.box, std::max(LayoutUnit(), availableAlignmentSpaceForChild(lineCrossAxisExtent, flexItem.box))))
1758                 continue;
1759             
1760             ItemPosition position = alignmentForChild(flexItem.box);
1761             if (position == ItemPositionStretch)
1762                 applyStretchAlignmentToChild(flexItem.box, lineCrossAxisExtent);
1763             LayoutUnit availableSpace =
1764             availableAlignmentSpaceForChild(lineCrossAxisExtent, flexItem.box);
1765             LayoutUnit offset = alignmentOffset(availableSpace, position, marginBoxAscentForChild(flexItem.box), maxAscent, style().flexWrap() == FlexWrapReverse);
1766             adjustAlignmentForChild(flexItem.box, offset);
1767             if (position == ItemPositionBaseline && style().flexWrap() == FlexWrapReverse)
1768                 minMarginAfterBaseline = std::min(minMarginAfterBaseline, availableAlignmentSpaceForChild(lineCrossAxisExtent, flexItem.box) - offset);
1769         }
1770
1771         minMarginAfterBaselines.append(minMarginAfterBaseline);
1772     }
1773     
1774     if (style().flexWrap() != FlexWrapReverse)
1775         return;
1776     
1777     // wrap-reverse flips the cross axis start and end. For baseline alignment,
1778     // this means we need to align the after edge of baseline elements with the
1779     // after edge of the flex line.
1780     for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1781         const LineContext& lineContext = lineContexts[lineNumber];
1782         LayoutUnit minMarginAfterBaseline = minMarginAfterBaselines[lineNumber];
1783         for (size_t childNumber = 0; childNumber < lineContext.flexItems.size(); ++childNumber) {
1784             const auto& flexItem = lineContext.flexItems[childNumber];
1785             if (alignmentForChild(flexItem.box) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(flexItem.box) && minMarginAfterBaseline)
1786                 adjustAlignmentForChild(flexItem.box, minMarginAfterBaseline);
1787         }
1788     }
1789 }
1790
1791 void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox& child, LayoutUnit lineCrossAxisExtent)
1792 {
1793     if (!hasOrthogonalFlow(child) && child.style().logicalHeight().isAuto()) {
1794         LayoutUnit stretchedLogicalHeight = std::max(child.borderAndPaddingLogicalHeight(),
1795         lineCrossAxisExtent - crossAxisMarginExtentForChild(child));
1796         ASSERT(!child.needsLayout());
1797         LayoutUnit desiredLogicalHeight = child.constrainLogicalHeightByMinMax(stretchedLogicalHeight, cachedChildIntrinsicContentLogicalHeight(child));
1798         
1799         // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905.
1800         bool childNeedsRelayout = desiredLogicalHeight != child.logicalHeight();
1801         if (child.isRenderBlock() && downcast<RenderBlock>(child).hasPercentHeightDescendants() && m_relaidOutChildren.contains(&child)) {
1802             // Have to force another relayout even though the child is sized
1803             // correctly, because its descendants are not sized correctly yet. Our
1804             // previous layout of the child was done without an override height set.
1805             // So, redo it here.
1806             childNeedsRelayout = true;
1807         }
1808         if (childNeedsRelayout || !child.hasOverrideLogicalContentHeight())
1809             child.setOverrideLogicalContentHeight(desiredLogicalHeight - child.borderAndPaddingLogicalHeight());
1810         if (childNeedsRelayout) {
1811             child.setLogicalHeight(LayoutUnit());
1812             // We cache the child's intrinsic content logical height to avoid it being
1813             // reset to the stretched height.
1814             // FIXME: This is fragile. RendertBoxes should be smart enough to
1815             // determine their intrinsic content logical height correctly even when
1816             // there's an overrideHeight.
1817             LayoutUnit childIntrinsicContentLogicalHeight = cachedChildIntrinsicContentLogicalHeight(child);
1818             child.setChildNeedsLayout(MarkOnlyThis);
1819             
1820             // Don't use layoutChildIfNeeded to avoid setting cross axis cached size twice.
1821             child.layoutIfNeeded();
1822
1823             setCachedChildIntrinsicContentLogicalHeight(child, childIntrinsicContentLogicalHeight);
1824         }
1825     } else if (hasOrthogonalFlow(child) && child.style().logicalWidth().isAuto()) {
1826         LayoutUnit childWidth = std::max(LayoutUnit(), lineCrossAxisExtent - crossAxisMarginExtentForChild(child));
1827         childWidth = child.constrainLogicalWidthInFragmentByMinMax(childWidth, crossAxisContentExtent(), *this, nullptr);
1828         
1829         if (childWidth != child.logicalWidth()) {
1830             child.setOverrideLogicalContentWidth(childWidth - child.borderAndPaddingLogicalWidth());
1831             child.setChildNeedsLayout(MarkOnlyThis);
1832             child.layoutIfNeeded();
1833         }
1834     }
1835 }
1836
1837 void RenderFlexibleBox::flipForRightToLeftColumn(const Vector<LineContext>& lineContexts)
1838 {
1839     if (style().isLeftToRightDirection() || !isColumnFlow())
1840         return;
1841     
1842     LayoutUnit crossExtent = crossAxisExtent();
1843     for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1844         const LineContext& lineContext = lineContexts[lineNumber];
1845         for (size_t childNumber = 0; childNumber < lineContext.flexItems.size(); ++childNumber) {
1846             const auto& flexItem = lineContext.flexItems[childNumber];
1847             ASSERT(!flexItem.box.isOutOfFlowPositioned());
1848             
1849             LayoutPoint location = flowAwareLocationForChild(flexItem.box);
1850             // For vertical flows, setFlowAwareLocationForChild will transpose x and
1851             // y, so using the y axis for a column cross axis extent is correct.
1852             location.setY(crossExtent - crossAxisExtentForChild(flexItem.box) - location.y());
1853             if (!isHorizontalWritingMode())
1854                 location.move(LayoutSize(0, -horizontalScrollbarHeight()));
1855             setFlowAwareLocationForChild(flexItem.box, location);
1856         }
1857     }
1858 }
1859
1860 void RenderFlexibleBox::flipForWrapReverse(const Vector<LineContext>& lineContexts, LayoutUnit crossAxisStartEdge)
1861 {
1862     LayoutUnit contentExtent = crossAxisContentExtent();
1863     for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) {
1864         const LineContext& lineContext = lineContexts[lineNumber];
1865         for (size_t childNumber = 0; childNumber < lineContext.flexItems.size(); ++childNumber) {
1866             const auto& flexItem = lineContext.flexItems[childNumber];
1867             LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent;
1868             LayoutUnit originalOffset = lineContexts[lineNumber].crossAxisOffset - crossAxisStartEdge;
1869             LayoutUnit newOffset = contentExtent - originalOffset - lineCrossAxisExtent;
1870             adjustAlignmentForChild(flexItem.box, newOffset - originalOffset);
1871         }
1872     }
1873 }
1874
1875 bool RenderFlexibleBox::isTopLayoutOverflowAllowed() const
1876 {
1877     bool hasTopOverflow = RenderBlock::isTopLayoutOverflowAllowed();
1878     if (hasTopOverflow || !style().isReverseFlexDirection())
1879         return hasTopOverflow;
1880     
1881     return !isHorizontalFlow();
1882 }
1883
1884 bool RenderFlexibleBox::isLeftLayoutOverflowAllowed() const
1885 {
1886     bool hasLeftOverflow = RenderBlock::isLeftLayoutOverflowAllowed();
1887     if (hasLeftOverflow || !style().isReverseFlexDirection())
1888         return hasLeftOverflow;
1889     
1890     return isHorizontalFlow();
1891 }
1892
1893 }