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