COMMIT_MESSAGE

 1[cleanup] Replace RenderFlexibleBox's per-layout phase bools with a single monotonic FlexLayoutState
 2https://bugs.webkit.org/show_bug.cgi?id=320044
 3
 4Reviewed by NOBODY (OOPS!).
 5
 6RenderFlexibleBox tracked where it was in the flex layout algorithm with five
 7independent booleans -- m_inLayout, m_afterMainAxisItemSizing,
 8m_afterCrossAxisItemSizing, m_inPostFlexUpdateScrollbarLayout and
 9m_isComputingFlexBaseSizes -- each flipped on and off by a SetForScope object that
 10FlexFormattingContext reached through the container (scopedComputingFlexBaseSizes,
 11scopedAfterMainAxisItemSizing, scopedAfterCrossAxisItemSizing). The
 12percentage-resolution cascade in canUseFlexItemForPercentageResolution then had to
 13test them in a hand-maintained priority order.
 14
 15Replace all five with a single FlexLayoutState that holds one monotonically
 16advancing Phase enum. m_flexLayoutState is a std::optional the container owns,
 17engaged via SetForScope only while a layout is running (std::nullopt otherwise, so
 18there is no separate "in layout" bool). FlexFormattingContext::layout() advances
 19the phase at the entry of each pipeline step it runs, and readers ask either "at or
 20past phase X" (phase() >= X) or "in phase X" (phase() == X):
 21
 22 PreparingFlexItems -> ComputingFlexBaseSizes -> CollectingLines ->
 23 ResolvingFlexibleLengths -> MainAxisItemSizing -> CrossSizing ->
 24 MainAxisAlignment -> CrossAxisItemSizing -> CrossAxisAlignment ->
 25 PostFlexScrollbarLayout
 26
 27This is behavior-preserving. The state is only consulted while a flex item is
 28being laid out -- flex base size computation, the main-axis item layout pass, the
 29cross-axis stretch relayout, and the post-flex scrollbar relayout -- and in each
 30of those windows the phase resolves to the same answer the old scoped bool gave.
 31The phase does linger into the surrounding no-layout regions (advancing is
 32one-way), but no read observes it there, so the coarser lifetime is not
 33observable.
 34
 35Because setPhase only advances, the main-axis item layout is split so the phase is
 36owned in exactly one place: layoutFlexItems() is the MainAxisItemSizing pipeline
 37step and the sole phase setter, while the reusable per-item loop
 38layoutFlexItemsWithMainSizes() sets no phase. The multiline-column re-run in
 39distributeMainAxisFreeSpaceForMultilineColumnIfNeeded() calls the latter, so it
 40relayouts within whatever phase main alignment left it in rather than trying (and
 41failing, since advancing is one-way) to rewind to MainAxisItemSizing.
 42
 43* Source/WebCore/Headers.cmake:
 44* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
 45* Source/WebCore/layout/formattingContexts/flex/FlexLayoutState.h: Added.
 46(WebCore::FlexLayoutState::phase const):
 47(WebCore::FlexLayoutState::setPhase):
 48* Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.h:
 49* Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.cpp:
 50(WebCore::FlexFormattingContext::layout):
 51(WebCore::FlexFormattingContext::computeFlexBaseAndHypotheticalMainSizes):
 52(WebCore::FlexFormattingContext::computeFlexLines):
 53(WebCore::FlexFormattingContext::computeMainSizeForFlexItems):
 54(WebCore::FlexFormattingContext::layoutFlexItems):
 55(WebCore::FlexFormattingContext::layoutFlexItemsWithMainSizes):
 56(WebCore::FlexFormattingContext::hypotheticalCrossSizeForFlexItems):
 57(WebCore::FlexFormattingContext::distributeMainAxisFreeSpaceForMultilineColumnIfNeeded):
 58(WebCore::FlexFormattingContext::handleMainAxisAlignment):
 59(WebCore::FlexFormattingContext::computeCrossSizeForFlexItems):
 60(WebCore::FlexFormattingContext::handleCrossAxisAlignmentForFlexItems):
 61(WebCore::FlexFormattingContext::flexBaseSizeForFlexItem):
 62(WebCore::FlexFormattingContext::applyStretchAlignmentToFlexItem):
 63(WebCore::FlexFormattingContext::flexLayoutState const):
 64* Source/WebCore/rendering/RenderFlexibleBox.h:
 65(WebCore::RenderFlexibleBox::isComputingFlexBaseSizes const):
 66(WebCore::RenderFlexibleBox::isInCrossAxisStretchLayout const):
 67(WebCore::RenderFlexibleBox::flexLayoutState):
 68(WebCore::RenderFlexibleBox::scopedComputingFlexBaseSizes): Deleted.
 69(WebCore::RenderFlexibleBox::scopedAfterMainAxisItemSizing): Deleted.
 70(WebCore::RenderFlexibleBox::scopedAfterCrossAxisItemSizing): Deleted.
 71* Source/WebCore/rendering/RenderFlexibleBox.cpp:
 72(WebCore::RenderFlexibleBox::layoutBlock):
 73(WebCore::RenderFlexibleBox::canUseFlexItemForPercentageResolution):
 74(WebCore::RenderFlexibleBox::layoutFlexItemWithMainSize):
 75(WebCore::RenderFlexibleBox::canComputePercentageFlexBasis):

Source/WebCore/Headers.cmake

@@set(WebCore_PRIVATE_FRAMEWORK_HEADERS
17651765
17661766 layout/formattingContexts/flex/FlexFormattingContext.h
17671767 layout/formattingContexts/flex/FlexFormattingUtils.h
 1768 layout/formattingContexts/flex/FlexLayoutState.h
17681769
17691770 layout/formattingContexts/grid/AxisConstraint.h
17701771 layout/formattingContexts/grid/GridAreaLines.h

Source/WebCore/WebCore.xcodeproj/project.pbxproj

74997499 FE0D84E910484348001A179E /* WebEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = FE0D84E810484348001A179E /* WebEvent.h */; settings = {ATTRIBUTES = (Private, ); }; };
75007500 FE1A2B3C4D5E6F0011112303 /* FlexFormattingContext.h in Headers */ = {isa = PBXBuildFile; fileRef = FE1A2B3C4D5E6F0011112302 /* FlexFormattingContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
75017501 FE1A2B3C4D5E6F0011112403 /* FlexFormattingUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FE1A2B3C4D5E6F0011112402 /* FlexFormattingUtils.h */; settings = {ATTRIBUTES = (Private, ); }; };
 7502 FE1A2B3C4D5E6F0011112503 /* FlexLayoutState.h in Headers */ = {isa = PBXBuildFile; fileRef = FE1A2B3C4D5E6F0011112502 /* FlexLayoutState.h */; settings = {ATTRIBUTES = (Private, ); }; };
75027503 FE36FD1516C7826500F887C1 /* ChangeVersionData.h in Headers */ = {isa = PBXBuildFile; fileRef = FE36FD1116C7826400F887C1 /* ChangeVersionData.h */; };
75037504 FE36FD1716C7826500F887C1 /* SQLTransactionStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = FE36FD1316C7826400F887C1 /* SQLTransactionStateMachine.h */; };
75047505 FE36FD1816C7826500F887C1 /* SQLTransactionState.h in Headers */ = {isa = PBXBuildFile; fileRef = FE36FD1416C7826400F887C1 /* SQLTransactionState.h */; };

2393123932 FE1A2B3C4D5E6F0011112302 /* FlexFormattingContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlexFormattingContext.h; sourceTree = "<group>"; };
2393223933 FE1A2B3C4D5E6F0011112401 /* FlexFormattingUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FlexFormattingUtils.cpp; sourceTree = "<group>"; };
2393323934 FE1A2B3C4D5E6F0011112402 /* FlexFormattingUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlexFormattingUtils.h; sourceTree = "<group>"; };
 23935 FE1A2B3C4D5E6F0011112502 /* FlexLayoutState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlexLayoutState.h; sourceTree = "<group>"; };
2393423936 FE36FD1116C7826400F887C1 /* ChangeVersionData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChangeVersionData.h; sourceTree = "<group>"; };
2393523937 FE36FD1216C7826400F887C1 /* SQLTransactionStateMachine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SQLTransactionStateMachine.cpp; sourceTree = "<group>"; };
2393623938 FE36FD1316C7826400F887C1 /* SQLTransactionStateMachine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLTransactionStateMachine.h; sourceTree = "<group>"; };

3157031572 FE1A2B3C4D5E6F0011112302 /* FlexFormattingContext.h */,
3157131573 FE1A2B3C4D5E6F0011112401 /* FlexFormattingUtils.cpp */,
3157231574 FE1A2B3C4D5E6F0011112402 /* FlexFormattingUtils.h */,
 31575 FE1A2B3C4D5E6F0011112502 /* FlexLayoutState.h */,
3157331576 );
3157431577 path = flex;
3157531578 sourceTree = "<group>";

4548645489 A8CFF04F0A154F09000A4234 /* FixedTableLayout.h in Headers */,
4548745490 FE1A2B3C4D5E6F0011112303 /* FlexFormattingContext.h in Headers */,
4548845491 FE1A2B3C4D5E6F0011112403 /* FlexFormattingUtils.h in Headers */,
 45492 FE1A2B3C4D5E6F0011112503 /* FlexLayoutState.h in Headers */,
4548945493 6FFDC442212EFF1700A9CA91 /* FloatAvoider.h in Headers */,
4549045494 BC073BAA0C399B1F000F5979 /* FloatConversion.h in Headers */,
4549145495 115CFA86208B9066001E6991 /* FloatingContext.h in Headers */,

Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.cpp

2727#include "FlexFormattingContext.h"
2828
2929#include "FlexFormattingUtils.h"
 30#include "FlexLayoutState.h"
3031#include "InspectorInstrumentation.h"
3132#include "RenderBoxInlines.h"
3233#include "RenderFlexibleBox.h"

@@FlexFormattingContext::Result FlexFormattingContext::layout(FlexLayoutItems& fle
7172 // 9.3. (#6) Resolve the flexible lengths to find the used main size of each item.
7273 flexItemsMainSizeList = computeMainSizeForFlexItems(flexItems, flexLines, flexBaseAndHypotheticalMainSizeList.span());
7374 trimCrossAxisMarginsForFlexItems(flexItems, flexLines);
74  layoutFlexItems(flexItems.mutableSpan(), flexItemsMainSizeList.span());
 75 layoutFlexItems(flexItems, flexItemsMainSizeList.span());
7576 // 9.4. (#7) Determine the hypothetical cross size of each item.
7677 auto flexItemsHypotheticalCrossSizeList = hypotheticalCrossSizeForFlexItems(flexItems);
7778 // 9.4. (#8) Calculate the cross size of each flex line.

@@FlexFormattingContext::Result FlexFormattingContext::layout(FlexLayoutItems& fle
125126
126127FlexFormattingContext::FlexBaseAndHypotheticalMainSizeList FlexFormattingContext::computeFlexBaseAndHypotheticalMainSizes(FlexLayoutItems& flexItems)
127128{
128  FlexBaseAndHypotheticalMainSizeList flexBaseAndHypotheticalMainSizeList(flexItems.size());
 129 flexLayoutState().setPhase(FlexLayoutState::Phase::ComputingFlexBaseSizes);
129130
 131 FlexBaseAndHypotheticalMainSizeList flexBaseAndHypotheticalMainSizeList(flexItems.size());
130132 for (size_t index = 0; index < flexItems.size(); ++index) {
131133 auto& flexItem = flexItems[index];
132134 auto flexBase = flexBaseSizeForFlexItem(flexItem);

@@FlexFormattingContext::FlexBaseAndHypotheticalMainSizeList FlexFormattingContext
146148
147149FlexFormattingContext::FlexLines FlexFormattingContext::computeFlexLines(FlexLayoutItems& flexItems, std::span<const FlexBaseAndHypotheticalMainSize> flexBaseAndHypotheticalMainSizeList)
148150{
 151 flexLayoutState().setPhase(FlexLayoutState::Phase::CollectingLines);
149152 // 9.3. (#5) Collect flex items into flex lines: a single-line container collects all items into one line; a
150153 // multi-line container collects consecutive items until the next item's outer hypothetical main size would not
151154 // fit in the inner main size. The first uncollected item is always collected, even if it does not fit.

@@FlexFormattingContext::FlexLines FlexFormattingContext::computeFlexLines(FlexLay
193196
194197FlexFormattingContext::SizeList FlexFormattingContext::computeMainSizeForFlexItems(FlexLayoutItems& flexItems, const FlexLines& flexLines, std::span<const FlexBaseAndHypotheticalMainSize> flexBaseAndHypotheticalMainSizeList)
195198{
 199 flexLayoutState().setPhase(FlexLayoutState::Phase::ResolvingFlexibleLengths);
 200
196201 SizeList flexItemsMainSizeList(flexItems.size());
197202 for (size_t lineIndex = 0; lineIndex < flexLines.ranges.size(); ++lineIndex) {
198203 auto lineRange = flexLines.ranges[lineIndex];

@@void FlexFormattingContext::distributeMainAxisFreeSpaceForMultilineColumnIfNeede
370375 for (size_t index = 0; index < lineItems.size(); ++index)
371376 remainingFreeSpace -= lineItems[index].flexedMarginBoxSize(lineFlexItemsMainSizeList[index]);
372377
373  layoutFlexItems(lineItems, lineFlexItemsMainSizeList);
 378 layoutFlexItemsWithMainSizes(lineItems, lineFlexItemsMainSizeList);
374379 placeFlexItems(flexLinesCrossPositionList[lineIndex], lineItems, linePositions, remainingFreeSpace);
375380 }
376381}

@@void FlexFormattingContext::trimCrossAxisMarginsForFlexItems(FlexLayoutItems& fl
399404 }
400405}
401406
402 void FlexFormattingContext::layoutFlexItems(std::span<FlexLayoutItem> flexLayoutItems, std::span<const LayoutUnit> flexItemsMainSizeList)
 407void FlexFormattingContext::layoutFlexItems(FlexLayoutItems& flexItems, std::span<const LayoutUnit> flexItemsMainSizeList)
 408{
 409 // The MainAxisItemSizing phase begins and ends here: every item is laid out at its resolved main size, so only the items' main-axis sizes are final at this point.
 410 flexLayoutState().setPhase(FlexLayoutState::Phase::MainAxisItemSizing);
 411 layoutFlexItemsWithMainSizes(flexItems.mutableSpan(), flexItemsMainSizeList);
 412}
 413
 414void FlexFormattingContext::layoutFlexItemsWithMainSizes(std::span<FlexLayoutItem> flexLayoutItems, std::span<const LayoutUnit> flexItemsMainSizeList)
403415{
404416 for (size_t index = 0; index < flexLayoutItems.size(); ++index)
405417 m_flexBox->layoutFlexItemWithMainSize(flexLayoutItems[index], flexItemsMainSizeList[index]);

@@void FlexFormattingContext::layoutFlexItems(std::span<FlexLayoutItem> flexLayout
407419
408420FlexFormattingContext::SizeList FlexFormattingContext::hypotheticalCrossSizeForFlexItems(const FlexLayoutItems& flexItems)
409421{
 422 flexLayoutState().setPhase(FlexLayoutState::Phase::CrossSizing);
 423
410424 // 9.4. (#7) The hypothetical cross size of each item is the cross size it would have at its used main size.
411425 SizeList flexItemsHypotheticalCrossSizeList(flexItems.size());
412426 for (size_t flexItemIndex = 0; flexItemIndex < flexItems.size(); ++flexItemIndex) {

@@FlexFormattingContext::LinesCrossPositionList FlexFormattingContext::computeFlex
481495
482496FlexFormattingContext::PositionList FlexFormattingContext::handleMainAxisAlignment(const FlexLines& flexLines, FlexLayoutItems& flexItems, const SizeList& flexItemsMainSizeList, const LinesCrossPositionList& flexLinesCrossPositionList, LayoutUnit& columnMainContentExtent)
483497{
 498 flexLayoutState().setPhase(FlexLayoutState::Phase::MainAxisAlignment);
 499
484500 PositionList flexItemsPositionList(flexItems.size());
485501 columnMainContentExtent = 0_lu;
486502 for (size_t lineIndex = 0; lineIndex < flexLines.ranges.size(); ++lineIndex) {

@@FlexFormattingContext::PositionList FlexFormattingContext::handleMainAxisAlignme
506522
507523FlexFormattingContext::SizeList FlexFormattingContext::computeCrossSizeForFlexItems(const FlexLines& flexLines, FlexLayoutItems& flexItems, const LinesCrossSizeList& flexLinesCrossSizeList, LayoutUnit crossContentExtent)
508524{
 525 flexLayoutState().setPhase(FlexLayoutState::Phase::CrossAxisItemSizing);
 526 // Stretching items to their line's cross size relays them out, so both their main and cross sizes become final.
509527 SizeList flexItemsCrossSizeList(flexItems.size());
510528 for (size_t lineIndex = 0; lineIndex < flexLines.ranges.size(); ++lineIndex) {
511529 auto lineRange = flexLines.ranges[lineIndex];

@@void FlexFormattingContext::handleCrossAxisAlignmentForFlexLines(const FlexLines
565583
566584void FlexFormattingContext::handleCrossAxisAlignmentForFlexItems(const FlexLines& flexLines, FlexLayoutItems& flexItems, const SizeList& flexItemsCrossSizeList, const LinesCrossSizeList& flexLinesCrossSizeList, PositionList& flexItemsPositionList)
567585{
 586 flexLayoutState().setPhase(FlexLayoutState::Phase::CrossAxisAlignment);
568587 // 9.6. (#13, #14) For each item resolve its cross-axis auto margins, then -- when neither cross-axis margin is
569588 // auto -- align it within its line per align-self (baseline-aligned items go through performBaselineAlignment).
570589 Vector<LayoutUnit> flexItemsCrossOffsetList(flexItems.size());

@@LayoutUnit FlexFormattingContext::flexBaseSizeForFlexItem(const FlexLayoutItem&
866885 auto scoped = ScopedFlexBasisAsFlexItemMainSize { flexItem, flexBasis.tryPreferredSize().value_or(Style::PreferredSize { CSS::Keyword::MaxContent { } }), flexLayoutItem.mainAxisIsInlineAxis };
867886 // FIXME: While we are supposed to ignore min/max here, the cached
868887 // The cached block-axis size entry may hold a min/max-constrained size.
869  auto computingBaseSizesScope = m_flexBox->scopedComputingFlexBaseSizes();
870888 auto blockAxisContentSize = ensureBlockAxisContentSizeForFlexItemIfNeeded(flexLayoutItem);
871889
872890 // A. If the item has a definite used flex basis, that's the flex base size.

@@LayoutUnit FlexFormattingContext::applyStretchAlignmentToFlexItem(const FlexLayo
14271445 // 9.4. (#11) A stretched item's used cross size is its flex line's cross size minus the item's cross-axis
14281446 // margins, clamped by the item's used min and max cross sizes. (An item with a definite cross size is not
14291447 // stretched, but still gets the stretch min/max clamp via applyStretchMinMaxCrossSize.)
1430  auto flexLayoutScope = m_flexBox->scopedAfterCrossAxisItemSizing();
14311448 if (flexLayoutItem.mainAxisIsInlineAxis) {
14321449 // Cross axis is block axis (height).
14331450 if (!style->logicalHeight().isAuto() && !style->logicalHeight().isStretch())

@@const FlexFormattingUtils& FlexFormattingContext::flexFormattingUtils() const
15381555 return m_flexFormattingUtils;
15391556}
15401557
 1558FlexLayoutState& FlexFormattingContext::flexLayoutState() const
 1559{
 1560 return m_flexBox->flexLayoutState();
 1561}
 1562
15411563FlexLayoutItem::FlexLayoutItem(RenderBox& flexItem, bool flexContainerIsHorizontalFlow, bool everHadLayout, bool shouldInvalidateChildContent)
15421564 : renderer(flexItem)
15431565 , mainAxisBorderAndPadding(flexContainerIsHorizontalFlow ? flexItem.horizontalBorderAndPaddingExtent() : flexItem.verticalBorderAndPaddingExtent())

Source/WebCore/layout/formattingContexts/flex/FlexFormattingContext.h

@@class ComputedStyle;
3636struct MinimumSize;
3737}
3838
 39class FlexLayoutState;
3940class RenderFlexibleBox;
4041
4142class FlexLayoutItem {

@@private:
131132 using BaselineSharingGroups = Vector<BaselineSharingGroup, 1>;
132133
133134 FlexLines computeFlexLines(FlexLayoutItems& flexItems, std::span<const FlexBaseAndHypotheticalMainSize> flexBaseAndHypotheticalMainSizeList);
134  // Resolves each flex item's flexed main size (spec 9.7) for every line, and returns the used main size of each item.
135135 SizeList computeMainSizeForFlexItems(FlexLayoutItems& flexItems, const FlexLines&, std::span<const FlexBaseAndHypotheticalMainSize> flexBaseAndHypotheticalMainSizeList);
136136 void resolveFlexibleLengthsForLineItems(std::span<FlexLayoutItem> lineItems, std::span<const FlexBaseAndHypotheticalMainSize> lineFlexBaseAndHypotheticalMainSizeList, std::span<LayoutUnit> flexItemsMainSizeList, LayoutUnit flexContainerInnerMainSize);
137137 void distributeMainAxisFreeSpaceForMultilineColumnIfNeeded(const FlexLines&, FlexLayoutItems&, std::span<const FlexBaseAndHypotheticalMainSize> flexBaseAndHypotheticalMainSizeList, SizeList& flexItemsMainSizeList, PositionList& flexItemsPositionList, const LinesCrossPositionList& flexLinesCrossPositionList, LayoutUnit containerMainBlockContentExtent);
138  // CSS Flexbox 9.7/9.6: the space available to distribute among a line's items (respectively among the lines
139  // within the container) is the container's inner main (cross) size minus the gaps between them.
140138 LayoutUnit mainAxisAvailableSpaceForItemAlignment(LayoutUnit mainAxisAvailableSpace, size_t numberOfFlexItems) const;
141139 LayoutUnit crossAxisAvailableSpaceForLineSizingAndAlignment(LayoutUnit crossAxisAvailableSpace, size_t numberOfFlexLines) const;
142  // Trims the cross-axis margins of the items on the first and last flex line (must run before laying the items out).
143140 void trimCrossAxisMarginsForFlexItems(FlexLayoutItems& flexItems, const FlexLines&);
144  // Lays out each flex item at its resolved main size.
145  void layoutFlexItems(std::span<FlexLayoutItem>, std::span<const LayoutUnit> flexItemsMainSizeList);
 141 void layoutFlexItems(FlexLayoutItems&, std::span<const LayoutUnit> flexItemsMainSizeList);
 142 void layoutFlexItemsWithMainSizes(std::span<FlexLayoutItem>, std::span<const LayoutUnit> flexItemsMainSizeList);
146143 SizeList hypotheticalCrossSizeForFlexItems(const FlexLayoutItems&);
147144 LinesCrossSizeList crossSizeForFlexLines(const FlexLines&, const FlexLayoutItems&, const SizeList& flexItemsHypotheticalCrossSizeList);
148145 LinesCrossPositionList computeFlexLineCrossPositions(const FlexLines&, const LinesCrossSizeList&, LayoutUnit& flexContentBlockExtent);

@@private:
153150 void performBaselineAlignment(WTF::Range<size_t> lineRange, FlexLayoutItems&, Vector<LayoutUnit>& flexItemsCrossOffsetList, const SizeList& flexItemsCrossSizeList, LayoutUnit lineCrossAxisExtent);
154151 void computeFlexItemRects(const FlexLines&, FlexLayoutItems&, const PositionList& flexItemsPositionList, const LinesCrossPositionList& flexLinesCrossPositionList, const LinesCrossSizeList& flexLinesCrossSizeList, const SizeList& flexItemsCrossSizeList, LayoutUnit crossAxisStartEdge, LayoutUnit crossContentExtent, LayoutUnit crossExtent);
155152
156  // Places a flex line's items along the main axis and writes their positions; returns the line's main-axis content
157  // extent for column flow (0 for row flow, which builds its block extent elsewhere).
158153 LayoutUnit placeFlexItems(LayoutUnit crossAxisOffset, std::span<FlexLayoutItem>, std::span<LayoutPoint> positions, LayoutUnit availableFreeSpace);
159154 void reverseColumnLinesFromContainerMainEndIfNeeded(const FlexLines&, FlexLayoutItems&, const SizeList& flexItemsMainSizeList, PositionList& flexItemsPositionList, const LinesCrossPositionList& flexLinesCrossPositionList, LayoutUnit containerMainBlockContentExtent, LayoutUnit containerMainBorderBoxExtent);
160155 void layoutColumnReverse(std::span<FlexLayoutItem>, std::span<LayoutPoint> positions, LayoutUnit crossAxisOffset, LayoutUnit availableFreeSpace, LayoutUnit columnMainBorderBoxExtent);

@@private:
195190 void setFlexItemGeometry(FlexLayoutItem&, const LayoutPoint& location);
196191
197192 const FlexFormattingUtils& flexFormattingUtils() const;
 193 FlexLayoutState& flexLayoutState() const;
198194
199195 const CheckedRef<RenderFlexibleBox> m_flexBox;
200196 FlexFormattingUtils m_flexFormattingUtils;

Source/WebCore/layout/formattingContexts/flex/FlexLayoutState.h

 1/*
 2 * Copyright (C) 2026 Apple 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
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26#pragma once
 27
 28#include <cstdint>
 29
 30namespace WebCore {
 31
 32class FlexLayoutState {
 33public:
 34 enum class Phase : uint8_t {
 35 PreparingFlexItems, // layoutBlock setup, before the flex algorithm runs.
 36 ComputingFlexBaseSizes, // computeFlexBaseAndHypotheticalMainSizes (CSS Flexbox 9.2).
 37 CollectingLines, // computeFlexLines (9.3 #5).
 38 ResolvingFlexibleLengths, // computeMainSizeForFlexItems (9.3 #6).
 39 MainAxisItemSizing, // layoutFlexItems — items laid out at their resolved main size.
 40 CrossSizing, // hypotheticalCrossSizeForFlexItems / crossSizeForFlexLines (9.4 #7-#8).
 41 MainAxisAlignment, // handleMainAxisAlignment (9.5 #12).
 42 CrossAxisItemSizing, // computeCrossSizeForFlexItems — stretch items to the line's cross size (9.4 #11).
 43 CrossAxisAlignment, // handleCrossAxisAlignmentForFlexItems (9.6 #13-#14).
 44 PostFlexScrollbarLayout, // Scrollbar reconciliation relayout after flex layout.
 45 };
 46
 47 Phase phase() const { return m_phase; }
 48
 49 void setPhase(Phase phase)
 50 {
 51 if (phase > m_phase)
 52 m_phase = phase;
 53 }
 54
 55private:
 56 Phase m_phase { Phase::PreparingFlexItems };
 57};
 58
 59} // namespace WebCore

Source/WebCore/rendering/RenderFlexibleBox.cpp

@@void RenderFlexibleBox::layoutBlock(RelayoutChildren relayoutChildren, LayoutUni
208208 resetLogicalHeightBeforeLayoutIfNeeded();
209209 m_flexItemsWithCompletedLayout.clear();
210210
211  bool oldInLayout = m_inLayout;
212  m_inLayout = true;
 211 SetForScope flexLayoutStateScope(m_flexLayoutState, FlexLayoutState { });
213212
214213 if (!style().marginTrim().isNone())
215214 initializeMarginTrimState();

@@void RenderFlexibleBox::layoutBlock(RelayoutChildren relayoutChildren, LayoutUni
247246
248247 m_flexLayout.layout(relayoutChildren);
249248
250  {
251  auto scrollbarLayout = SetForScope(m_inPostFlexUpdateScrollbarLayout, true);
252  endAndCommitUpdateScrollInfoAfterLayoutTransaction();
253  }
 249 m_flexLayoutState->setPhase(FlexLayoutState::Phase::PostFlexScrollbarLayout);
 250 endAndCommitUpdateScrollInfoAfterLayoutTransaction();
254251
255252 repaintFlexItemsDuringLayoutIfMoved(oldFlexItemRects);
256253 // FIXME: css3/flexbox/repaint-rtl-column.html seems to repaint more overflow than it needs to.

@@void RenderFlexibleBox::layoutBlock(RelayoutChildren relayoutChildren, LayoutUni
274271 resetHasDefiniteHeight();
275272
276273 repainter.repaintAfterLayout();
277 
278  m_inLayout = oldInLayout;
279274}
280275
281276std::optional<LayoutUnit> RenderFlexibleBox::firstLineBaseline() const

@@bool RenderFlexibleBox::canUseFlexItemForPercentageResolution(const RenderBox& f
480475 if (m_inFlexItemIntrinsicWidthComputation)
481476 return FlexFormattingUtils::hasDefiniteCrossSizeForFlexItem(flexItem);
482477
483  if (m_afterMainAxisItemSizing) {
484  // Final sizes for flex items are available only along the main axis.
485  // Percentages can be resolved only against those items when they are orthogonal to the flex container (i.e., their logical height is computed and final)
486  return !FlexFormattingUtils::mainAxisIsFlexItemInlineAxis(flexItem);
487  }
488 
489  if (m_afterCrossAxisItemSizing) {
490  // Final sizes for flex items are known in both the main and cross directions, so it's fine to resolve percentage heights using those final values.
491  return true;
492  }
493 
494  if (m_inPostFlexUpdateScrollbarLayout) {
495  // We run layout on flex content _after_ performing flex layout (see endAndCommitUpdateScrollInfoAfterLayoutTransaction/updateScrollInfoAfterLayout).
496  // Final sizes for flex items are known in both the main and cross directions.
497  return true;
 478 if (m_flexLayoutState) {
 479 auto phase = m_flexLayoutState->phase();
 480 if (phase >= FlexLayoutState::Phase::CrossAxisItemSizing)
 481 return true;
 482 if (phase >= FlexLayoutState::Phase::MainAxisItemSizing)
 483 return !FlexFormattingUtils::mainAxisIsFlexItemInlineAxis(flexItem);
498484 }
499485
500486 if (m_inSimplifiedLayout) {

@@bool RenderFlexibleBox::canUseFlexItemForPercentageResolution(const RenderBox& f
508494 return !FlexFormattingUtils::mainAxisIsFlexItemInlineAxis(flexItem);
509495
510496 // Outside of layout (i.e. when using relative percentage positioning), base the decision on style.
511  return !m_inLayout;
 497 return !m_flexLayoutState;
512498 };
513499 if (!canUseByLayoutPhase())
514500 return false;

@@void RenderFlexibleBox::layoutFlexItemWithMainSize(FlexLayoutItem& flexLayoutIte
680666 if (flexItem.needsLayout())
681667 markFlexItemLayoutComplete(flexItem);
682668
683  {
684  auto flexLayoutScope = scopedAfterMainAxisItemSizing();
685  flexItem.layoutIfNeeded();
686  }
 669 flexItem.layoutIfNeeded();
687670
688671 if (!flexLayoutItem.everHadLayout && flexItem.checkForRepaintDuringLayout()) {
689672 flexItem.repaint();

@@template<typename SizeType> bool RenderFlexibleBox::canComputePercentageFlexBasi
858841 ASSERT(!isPercentResolveSuspended || is<RenderBlock>(flexItem));
859842
860843 bool definite = !isPercentResolveSuspended && flexItem.computePercentageLogicalHeight(flexBasis, updateDescendants).has_value();
861  if (m_inLayout && (isHorizontalWritingMode() == flexItem.isHorizontalWritingMode())) {
 844 if (m_flexLayoutState && (isHorizontalWritingMode() == flexItem.isHorizontalWritingMode())) {
862845 // We can reach this code even while we're not laying ourselves out, such
863846 // as from mainSizeForPercentageResolution.
864847 m_hasDefiniteHeight = definite ? SizeDefiniteness::Definite : SizeDefiniteness::Indefinite;

Source/WebCore/rendering/RenderFlexibleBox.h

3333#include <WebCore/BaselineAlignment.h>
3434#include <WebCore/FlexFormattingContext.h>
3535#include <WebCore/FlexFormattingUtils.h>
 36#include <WebCore/FlexLayoutState.h>
3637#include <WebCore/LayoutIntegrationFlexLayout.h>
3738#include <WebCore/RenderBlock.h>
3839#include <wtf/Range.h>

@@public:
9697 // Returns true if the position changed. In that case, the flexItem will have to be laid out again.
9798 bool setStaticPositionForPositionedLayout(const RenderBox&);
9899
99  bool isComputingFlexBaseSizes() const { return m_isComputingFlexBaseSizes; }
 100 bool isComputingFlexBaseSizes() const { return m_flexLayoutState && m_flexLayoutState->phase() == FlexLayoutState::Phase::ComputingFlexBaseSizes; }
100101
101102 bool shouldResetFlexItemLogicalHeightBeforeLayout() const { return m_shouldResetFlexItemLogicalHeightBeforeLayout; }
102  bool isInCrossAxisStretchLayout() const { return m_inLayout && m_afterCrossAxisItemSizing; }
 103 bool isInCrossAxisStretchLayout() const { return m_flexLayoutState && m_flexLayoutState->phase() == FlexLayoutState::Phase::CrossAxisItemSizing; }
103104
104105 class OverridingSizesScope {
105106 public:

@@private:
159160
160161 void resetHasDefiniteHeight() { m_hasDefiniteHeight = SizeDefiniteness::Unknown; }
161162
162  // The flex layout pipeline (FlexFormattingContext) reaches the container's layout-phase state through these
163  // rather than writing the members directly, so the state stays owned by RenderFlexibleBox.
164  SetForScope<bool> scopedComputingFlexBaseSizes() { return SetForScope(m_isComputingFlexBaseSizes, true); }
165  SetForScope<bool> scopedAfterMainAxisItemSizing() { return SetForScope(m_afterMainAxisItemSizing, true); }
166  SetForScope<bool> scopedAfterCrossAxisItemSizing() { return SetForScope(m_afterCrossAxisItemSizing, true); }
 163 FlexLayoutState& flexLayoutState() LIFETIME_BOUND { ASSERT(m_flexLayoutState); return *m_flexLayoutState; }
 164
167165 SetForScope<bool> scopedResetFlexItemLogicalHeightBeforeLayout() { return SetForScope(m_shouldResetFlexItemLogicalHeightBeforeLayout, true); }
168166 SizeDefiniteness hasDefiniteHeight() const { return m_hasDefiniteHeight; }
169167 void setHasDefiniteHeight(SizeDefiniteness definiteness) { m_hasDefiniteHeight = definiteness; }

@@private:
218216
219217 // This is SizeIsUnknown outside of layoutBlock()
220218 SizeDefiniteness m_hasDefiniteHeight { SizeDefiniteness::Unknown };
221  bool m_inLayout { false };
222  bool m_afterMainAxisItemSizing { false };
223  bool m_afterCrossAxisItemSizing { false };
 219 std::optional<FlexLayoutState> m_flexLayoutState;
224220 bool m_inSimplifiedLayout { false };
225  bool m_inPostFlexUpdateScrollbarLayout { false };
226221 mutable bool m_inFlexItemIntrinsicWidthComputation { false };
227222 bool m_shouldResetFlexItemLogicalHeightBeforeLayout { false };
228  bool m_isComputingFlexBaseSizes { false };
229223};
230224
231225} // namespace WebCore