/*
 * Copyright (C) 2011, 2022 Apple Inc. All rights reserved.
 * Copyright (C) 2013-2017 Igalia S.L.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "RenderGrid.h"

#include "GridArea.h"
#include "GridLayoutFunctions.h"
#include "GridMasonryLayout.h"
#include "GridTrackSizingAlgorithm.h"
#include "HitTestLocation.h"
#include "LayoutRepainter.h"
#include "RenderChildIterator.h"
#include "RenderElementInlines.h"
#include "RenderLayer.h"
#include "RenderLayoutState.h"
#include "RenderObjectInlines.h"
#include "RenderTreeBuilder.h"
#include "RenderView.h"
#include "StyleGridPositionsResolver.h"
#include "StylePrimitiveNumericTypes+Evaluation.h"
#include <wtf/Range.h>
#include <wtf/Scope.h>
#include <wtf/SetForScope.h>
#include <wtf/TZoneMallocInlines.h>

namespace WebCore {

WTF_MAKE_TZONE_OR_ISO_ALLOCATED_IMPL(RenderGrid);

enum class TrackSizeRestriction : uint8_t {
    AllowInfinity,
    ForbidInfinity,
};

RenderGrid::RenderGrid(Element& element, RenderStyle&& style)
    : RenderBlock(Type::Grid, element, WTFMove(style), { })
    , m_grid(*this)
    , m_trackSizingAlgorithm(this, currentGrid())
    , m_masonryLayout(*this)
{
    ASSERT(isRenderGrid());
    // All of our children must be block level.
    setChildrenInline(false);
}

RenderGrid::~RenderGrid() = default;

bool RenderGrid::isExtrinsicallySized() const
{
    auto& gridStyle = style();
    auto allTracksAreExtrinsicallySized = [&] {
        for (auto& column : gridStyle.gridTemplateColumns().sizes) {
            if (column.isContentSized())
                return false;
        }

        for (auto& row : gridStyle.gridTemplateRows().sizes) {
            if (row.isContentSized())
                return false;
        }
        return true;
    };

    // Since we currently only check if the grid's logical width is auto, it being
    // extrinsically sized in this regard depends on the formatting context it
    // participates in. For now we only check and allow if it participates in block
    // layout since that is simple.
    auto participatesInBlockLayout = [&] {
        auto* containingBlock = this->containingBlock();
        return containingBlock && containingBlock->isBlockContainer() && !containingBlock->childrenInline();
    };

    if (!gridStyle.logicalWidth().isAuto()
        || !participatesInBlockLayout()
        || !gridStyle.logicalHeight().isFixed()
        || !allTracksAreExtrinsicallySized()
        || gridStyle.hasAspectRatio()
        || isSubgrid()
        || isMasonry())
        return false;

    for (auto& gridItem : childrenOfType<RenderBox>(*this)) {
        // FIXME: If we do not need to perform item placement we should be able
        // to check any implicitly created tracks as well.
        if (!isPlacedWithinExtrinsicallySizedExplicitTracks(gridItem))
            return false;
    }

    return true;
}

StyleSelfAlignmentData RenderGrid::selfAlignmentForGridItem(Style::GridTrackSizingDirection alignmentContextType, const RenderBox& gridItem, const RenderStyle* gridStyle) const
{
    return alignmentContextType == Style::GridTrackSizingDirection::Columns ? justifySelfForGridItem(gridItem, StretchingMode::Any, gridStyle) : alignSelfForGridItem(gridItem, StretchingMode::Any, gridStyle);
}

bool RenderGrid::selfAlignmentChangedToStretch(Style::GridTrackSizingDirection alignmentContextType, const RenderStyle& oldStyle, const RenderStyle& newStyle, const RenderBox& gridItem) const
{
    return selfAlignmentForGridItem(alignmentContextType, gridItem, &oldStyle).position() != ItemPosition::Stretch
        && selfAlignmentForGridItem(alignmentContextType, gridItem, &newStyle).position() == ItemPosition::Stretch;
}

bool RenderGrid::selfAlignmentChangedFromStretch(Style::GridTrackSizingDirection alignmentContextType, const RenderStyle& oldStyle, const RenderStyle& newStyle, const RenderBox& gridItem) const
{
    return selfAlignmentForGridItem(alignmentContextType, gridItem, &oldStyle).position() == ItemPosition::Stretch
        && selfAlignmentForGridItem(alignmentContextType, gridItem, &newStyle).position() != ItemPosition::Stretch;
}

void RenderGrid::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
    RenderBlock::styleDidChange(diff, oldStyle);
    if (!oldStyle || diff != StyleDifference::Layout)
        return;

    m_intrinsicLogicalHeightsForRowSizingFirstPass.reset();
    const RenderStyle& newStyle = this->style();

    auto hasDifferentTrackSizes = [&newStyle, &oldStyle](Style::GridTrackSizingDirection direction) {
        return newStyle.gridTemplateList(direction).sizes != oldStyle->gridTemplateList(direction).sizes;
    };

    if (oldStyle->writingMode().isOrthogonal(newStyle.writingMode())) {
        for (auto& gridItem : childrenOfType<RenderGrid>(*this)) {
            if (gridItem.isSubgrid())
                gridItem.setNeedsItemPlacement();
        }
    }

    if (hasDifferentTrackSizes(Style::GridTrackSizingDirection::Columns) || hasDifferentTrackSizes(Style::GridTrackSizingDirection::Rows)) {
        for (auto& gridItem : childrenOfType<RenderBox>(*this))
            gridItem.setChildNeedsLayout();
    }

    if (oldStyle->resolvedAlignItems(selfAlignmentNormalBehavior(this)).position() == ItemPosition::Stretch) {
        // Style changes on the grid container implying stretching (to-stretch) or
        // shrinking (from-stretch) require the affected items to be laid out again.
        // These logic only applies to 'stretch' since the rest of the alignment
        // values don't change the size of the box.
        // In any case, the items' overrideSize will be cleared and recomputed (if
        // necessary)  as part of the Grid layout logic, triggered by this style
        // change.
        for (auto& gridItem : childrenOfType<RenderBox>(*this)) {
            if (gridItem.isOutOfFlowPositioned())
                continue;

            if (selfAlignmentChangedToStretch(Style::GridTrackSizingDirection::Columns, *oldStyle, newStyle, gridItem)
                || selfAlignmentChangedFromStretch(Style::GridTrackSizingDirection::Columns, *oldStyle, newStyle, gridItem)
                || selfAlignmentChangedToStretch(Style::GridTrackSizingDirection::Rows, *oldStyle, newStyle, gridItem)
                || selfAlignmentChangedFromStretch(Style::GridTrackSizingDirection::Rows, *oldStyle, newStyle, gridItem)) {
                gridItem.setNeedsLayout();
            }
        }
    }

    auto subgridDidChange = this->subgridDidChange(*oldStyle);
    auto isSubgridWithIndependentFormattingContextChange = [&] {
        if (newStyle.gridTemplateRows().subgrid || newStyle.gridTemplateColumns().subgrid)
            return establishesIndependentFormattingContextIgnoringDisplayType(*oldStyle) != establishesIndependentFormattingContextIgnoringDisplayType(style());
        return false;
    };
    if (explicitGridDidResize(*oldStyle)
        || namedGridLinesDefinitionDidChange(*oldStyle)
        || implicitGridLinesDefinitionDidChange(*oldStyle)
        || oldStyle->gridAutoFlow() != style().gridAutoFlow()
        || style().gridTemplateColumns().autoRepeatSizes.size()
        || style().gridTemplateRows().autoRepeatSizes.size()
        || subgridDidChange == SubgridDidChange::Yes
        || isSubgridWithIndependentFormattingContextChange())
        setNeedsItemPlacement(subgridDidChange);
}

SubgridDidChange RenderGrid::subgridDidChange(const RenderStyle& oldStyle) const
{
    if (oldStyle.gridTemplateRows().subgrid != style().gridTemplateRows().subgrid
        || oldStyle.gridTemplateColumns().subgrid != style().gridTemplateColumns().subgrid)
        return SubgridDidChange::Yes;
    return SubgridDidChange::No;
}

bool RenderGrid::explicitGridDidResize(const RenderStyle& oldStyle) const
{
    auto& oldGridTemplateColumns = oldStyle.gridTemplateColumns();
    auto& oldGridTemplateRows = oldStyle.gridTemplateRows();
    auto& oldGridTemplateAreas = oldStyle.gridTemplateAreas();
    auto& newGridTemplateColumns = style().gridTemplateColumns();
    auto& newGridTemplateRows = style().gridTemplateRows();
    auto& newGridTemplateAreas = style().gridTemplateAreas();

    return oldGridTemplateColumns.sizes.size() != newGridTemplateColumns.sizes.size()
        || oldGridTemplateColumns.autoRepeatSizes.size() != newGridTemplateColumns.autoRepeatSizes.size()
        || oldGridTemplateRows.sizes.size() != newGridTemplateRows.sizes.size()
        || oldGridTemplateRows.autoRepeatSizes.size() != newGridTemplateRows.autoRepeatSizes.size()
        || oldGridTemplateAreas.map.columnCount != newGridTemplateAreas.map.columnCount
        || oldGridTemplateAreas.map.rowCount != newGridTemplateAreas.map.rowCount;
}

bool RenderGrid::namedGridLinesDefinitionDidChange(const RenderStyle& oldStyle) const
{
    return oldStyle.gridTemplateRows().namedLines.map != style().gridTemplateRows().namedLines.map
        || oldStyle.gridTemplateColumns().namedLines.map != style().gridTemplateColumns().namedLines.map;
}

bool RenderGrid::implicitGridLinesDefinitionDidChange(const RenderStyle& oldStyle) const
{
    auto& oldGridTemplateAreas = oldStyle.gridTemplateAreas();
    auto& newGridTemplateAreas = style().gridTemplateAreas();

    return oldGridTemplateAreas.implicitNamedGridRowLines != newGridTemplateAreas.implicitNamedGridRowLines
        || oldGridTemplateAreas.implicitNamedGridColumnLines != newGridTemplateAreas.implicitNamedGridColumnLines;
}

// This method optimizes the gutters computation by skipping the available size
// call if gaps are fixed size (it's only needed for percentages).
std::optional<LayoutUnit> RenderGrid::availableSpaceForGutters(Style::GridTrackSizingDirection direction) const
{
    if (!style().gap(direction).isPercentOrCalculated())
        return std::nullopt;

    return direction == Style::GridTrackSizingDirection::Columns ? contentBoxLogicalWidth() : contentBoxLogicalHeight();
}

void RenderGrid::computeTrackSizesForDefiniteSize(Style::GridTrackSizingDirection direction, LayoutUnit availableSpace, GridLayoutState& gridLayoutState)
{
    auto autoMarginResolutionScope = SetForScope(m_isComputingTrackSizes, true);
    m_trackSizingAlgorithm.run(direction, numTracks(direction), SizingOperation::TrackSizing, availableSpace, gridLayoutState);
    ASSERT(m_trackSizingAlgorithm.tracksAreWiderThanMinTrackBreadth());
}

void RenderGrid::repeatTracksSizingIfNeeded(LayoutUnit availableSpaceForColumns, LayoutUnit availableSpaceForRows, GridLayoutState& gridLayoutState)
{
    // In orthogonal flow cases column track's size is determined by using the computed
    // row track's size, which it was estimated during the first cycle of the sizing
    // algorithm. Hence we need to repeat computeUsedBreadthOfGridTracks for both,
    // columns and rows, to determine the final values.
    // TODO (lajava): orthogonal flows is just one of the cases which may require
    // a new cycle of the sizing algorithm; there may be more. In addition, not all the
    // cases with orthogonal flows require this extra cycle; we need a more specific
    // condition to detect whether grid item's min-content contribution has changed or not.
    // The complication with repeating the track sizing algorithm for flex max-sizing is that
    // it might change a grid item's status of participating in Baseline Alignment for
    // a cyclic sizing dependency case, which should be definitively excluded. See
    // https://github.com/w3c/csswg-drafts/issues/3046 for details.
    // FIXME: we are avoiding repeating the track sizing algorithm for grid item with baseline alignment
    // here in the case of using flex max-sizing functions. We probably also need to investigate whether
    // it is applicable for the case of percent-sized rows with indefinite height as well.
    if (gridLayoutState.needsSecondTrackSizingPass() || m_trackSizingAlgorithm.hasAnyPercentSizedRowsIndefiniteHeight() || (m_trackSizingAlgorithm.hasAnyFlexibleMaxTrackBreadth() && !m_trackSizingAlgorithm.hasAnyBaselineAlignmentItem()) || gridLayoutState.hasAspectRatioBlockSizeDependentItem()) {

        populateGridPositionsForDirection(m_trackSizingAlgorithm, Style::GridTrackSizingDirection::Rows);
        computeTrackSizesForDefiniteSize(Style::GridTrackSizingDirection::Columns, availableSpaceForColumns, gridLayoutState);
        m_offsetBetweenColumns = computeContentPositionAndDistributionOffset(Style::GridTrackSizingDirection::Columns, m_trackSizingAlgorithm.freeSpace(Style::GridTrackSizingDirection::Columns).value(), nonCollapsedTracks(Style::GridTrackSizingDirection::Columns));

        computeTrackSizesForDefiniteSize(Style::GridTrackSizingDirection::Rows, availableSpaceForRows, gridLayoutState);
        m_offsetBetweenRows = computeContentPositionAndDistributionOffset(Style::GridTrackSizingDirection::Rows, m_trackSizingAlgorithm.freeSpace(Style::GridTrackSizingDirection::Rows).value(), nonCollapsedTracks(Style::GridTrackSizingDirection::Rows));
    }
}

bool RenderGrid::canPerformSimplifiedLayout() const
{
    // We cannot perform a simplified layout if we need to position the items and we have some
    // positioned items to be laid out.
    if (currentGrid().needsItemsPlacement() && outOfFlowChildNeedsLayout())
        return false;

    return RenderBlock::canPerformSimplifiedLayout();
}

enum class AlignmentContextTypes : uint8_t {
    Columns = 1 << 0,
    Rows = 1 << 1
};

template<typename F>
static void cacheBaselineAlignedGridItems(const RenderGrid& grid, GridTrackSizingAlgorithm& algorithm, OptionSet<AlignmentContextTypes> alignmentContextTypes, F& callback, bool cachingRowSubgridsForRootGrid)
{
    ASSERT_IMPLIES(cachingRowSubgridsForRootGrid, !algorithm.renderGrid()->isSubgridRows() && (algorithm.renderGrid() == &grid || grid.isSubgridOf(GridLayoutFunctions::flowAwareDirectionForGridItem(*algorithm.renderGrid(), grid, Style::GridTrackSizingDirection::Rows), *algorithm.renderGrid())));

    for (auto& gridItem : childrenOfType<RenderBox>(grid)) {
        if (gridItem.isOutOfFlowPositioned() || gridItem.isLegend())
            continue;

        callback(const_cast<RenderBox*>(&gridItem));

        // We keep a cache of items with baseline as alignment values so that we only compute the baseline shims for
        // such items. This cache is needed for performance related reasons due to the cost of evaluating the item's
        // participation in a baseline context during the track sizing algorithm.
        OptionSet<AlignmentContextTypes> innerAlignmentContextTypes = { };
        CheckedPtr inner = dynamicDowncast<RenderGrid>(gridItem);

        if (alignmentContextTypes.contains(AlignmentContextTypes::Rows)) {
            if (inner && inner->isSubgridInParentDirection(Style::GridTrackSizingDirection::Rows))
                innerAlignmentContextTypes.add(GridLayoutFunctions::isOrthogonalGridItem(grid, gridItem) ? AlignmentContextTypes::Columns : AlignmentContextTypes::Rows);
            else if (grid.isBaselineAlignmentForGridItem(gridItem, Style::GridTrackSizingDirection::Rows))
                algorithm.cacheBaselineAlignedItem(gridItem, Style::GridTrackSizingDirection::Rows, cachingRowSubgridsForRootGrid);
        }

        if (alignmentContextTypes.contains(AlignmentContextTypes::Columns)) {
            if (inner && inner->isSubgridInParentDirection(Style::GridTrackSizingDirection::Columns))
                innerAlignmentContextTypes.add(GridLayoutFunctions::isOrthogonalGridItem(grid, gridItem) ? AlignmentContextTypes::Rows : AlignmentContextTypes::Columns);
            else if (grid.isBaselineAlignmentForGridItem(gridItem, Style::GridTrackSizingDirection::Columns))
                algorithm.cacheBaselineAlignedItem(gridItem, Style::GridTrackSizingDirection::Columns, cachingRowSubgridsForRootGrid);
        }

        if (inner && cachingRowSubgridsForRootGrid)
            cachingRowSubgridsForRootGrid = GridLayoutFunctions::isOrthogonalGridItem(*algorithm.renderGrid(), *inner) ? inner->isSubgridColumns() : inner->isSubgridRows();

        if (innerAlignmentContextTypes)
            cacheBaselineAlignedGridItems(*inner, algorithm, innerAlignmentContextTypes, callback, cachingRowSubgridsForRootGrid);
    }
}

Vector<RenderBox*> RenderGrid::computeAspectRatioDependentAndBaselineItems(GridLayoutState& gridLayoutState)
{
    Vector<RenderBox*> dependentGridItems;

    m_baselineItemsCached = true;

    auto computeOrthogonalAndDependentItems = [&](RenderBox* gridItem) {
        // For a grid item that has an aspect-ratio and block-constraints such as the relative logical height,
        // when the grid width is auto, we may need get the real grid width before laying out the item.
        if (GridLayoutFunctions::isAspectRatioBlockSizeDependentGridItem(*gridItem) && (style().logicalWidth().isAuto() || style().logicalWidth().isMinContent() || style().logicalWidth().isMaxContent())) {
            dependentGridItems.append(gridItem);
            gridLayoutState.setHasAspectRatioBlockSizeDependentItem();
        }
    };

    cacheBaselineAlignedGridItems(*this, m_trackSizingAlgorithm, { AlignmentContextTypes::Columns, AlignmentContextTypes::Rows }, computeOrthogonalAndDependentItems, !isSubgridRows());
    return dependentGridItems;
}

bool RenderGrid::canSetColumnAxisStretchRequirementForItem(const RenderBox& gridItem) const
{
    auto gridItemBlockFlowDirection = GridLayoutFunctions::flowAwareDirectionForGridItem(*this, gridItem, Style::GridTrackSizingDirection::Rows);
    return gridItemBlockFlowDirection == Style::GridTrackSizingDirection::Rows && allowedToStretchGridItemAlongColumnAxis(gridItem);
}

void RenderGrid::computeLayoutRequirementsForItemsBeforeLayout(GridLayoutState& gridLayoutState) const
{
    for (auto& gridItem : childrenOfType<RenderBox>(*this)) {

        auto gridItemAlignSelf = alignSelfForGridItem(gridItem).position();
        if (GridLayoutFunctions::isGridItemInlineSizeDependentOnBlockConstraints(gridItem, *this, gridItemAlignSelf)) {
            gridLayoutState.setNeedsSecondTrackSizingPass();
            gridLayoutState.setLayoutRequirementForGridItem(gridItem, ItemLayoutRequirement::MinContentContributionForSecondColumnPass);
        }

        if (!gridItem.needsLayout() || gridItem.isOutOfFlowPositioned() || gridItem.isExcludedFromNormalLayout())
            continue;

        if (canSetColumnAxisStretchRequirementForItem(gridItem))
            gridLayoutState.setLayoutRequirementForGridItem(gridItem, ItemLayoutRequirement::NeedsColumnAxisStretchAlignment);
    }
}

void RenderGrid::layoutBlock(RelayoutChildren relayoutChildren, LayoutUnit)
{
    ASSERT(needsLayout());

    auto postLayoutTasks = WTF::makeScopeExit([&] {
        m_outOfFlowItemColumn.clear();
        m_outOfFlowItemRow.clear();
    });

    if (relayoutChildren ==RelayoutChildren::No && simplifiedLayout())
        return;

    // The layoutBlock was handling the layout of both the grid and masonry implementations.
    // This caused a huge amount of branching code to handle masonry specific cases. Splitting up the code
    // to layout will simplify both implementations.
    if (!isMasonry())
        layoutGrid(relayoutChildren);
    else
        layoutMasonry(relayoutChildren);
}

static void clearGridItemOverridingSizesBeforeLayout(RenderGrid& renderGrid)
{
    // Grid's layout logic controls the grid item's override content size, hence we need to
    // clear any override set previously, so it doesn't interfere in current layout
    // execution.
    for (auto& gridItem : childrenOfType<RenderBox>(renderGrid)) {
        if (gridItem.isOutOfFlowPositioned() || gridItem.isLegend())
            continue;
        gridItem.clearOverridingSize();
    }
}


bool RenderGrid::hasDefiniteLogicalHeight() const
{
    // FIXME: We should use RenderBlock::hasDefiniteLogicalHeight() only but it does not work for out of flow content.
    return RenderBlock::hasDefiniteLogicalHeight() || overridingBorderBoxLogicalHeight() || computeContentLogicalHeight(style().logicalHeight(), std::nullopt) || shouldComputeLogicalHeightFromAspectRatio();
}

const std::optional<LayoutUnit> RenderGrid::availableLogicalHeightForContentBox() const
{
    if (!hasDefiniteLogicalHeight())
        return { };

    if (auto overridingLogicalHeight = this->overridingBorderBoxLogicalHeight())
        return constrainContentBoxLogicalHeightByMinMax(*overridingLogicalHeight - borderAndPaddingLogicalHeight(), { });
    return availableLogicalHeight(AvailableLogicalHeightType::ExcludeMarginBorderPadding);
}

void RenderGrid::layoutGrid(RelayoutChildren relayoutChildren)
{
    LayoutRepainter repainter(*this);
    {
        LayoutStateMaintainer statePusher(*this, locationOffset(), isTransformed() || hasReflection() || writingMode().isBlockFlipped());

        GridLayoutState gridLayoutState;

        updateIntrinsicLogicalHeightsForRowSizingFirstPassCacheAvailability();
        clearGridItemOverridingSizesBeforeLayout(*this);
        computeLayoutRequirementsForItemsBeforeLayout(gridLayoutState);

        preparePaginationBeforeBlockLayout(relayoutChildren);
        beginUpdateScrollInfoAfterLayoutTransaction();

        LayoutSize previousSize = size();

        auto aspectRatioBlockSizeDependentGridItems = computeAspectRatioDependentAndBaselineItems(gridLayoutState);

        resetLogicalHeightBeforeLayoutIfNeeded();

        updateLogicalWidth();

        // Fieldsets need to find their legend and position it inside the border of the object.
        // The legend then gets skipped during normal layout. The same is true for ruby text.
        // It doesn't get included in the normal layout process but is instead skipped.
        layoutExcludedChildren(relayoutChildren);

        LayoutUnit availableSpaceForColumns = contentBoxLogicalWidth();
        placeItemsOnGrid(availableSpaceForColumns);

        m_trackSizingAlgorithm.setAvailableSpace(Style::GridTrackSizingDirection::Columns, availableSpaceForColumns);
        performPreLayoutForGridItems(m_trackSizingAlgorithm, ShouldUpdateGridAreaLogicalSize::Yes);

        // 1. First, the track sizing algorithm is used to resolve the sizes of the grid columns. At this point the
        // logical width is always definite as the above call to updateLogicalWidth() properly resolves intrinsic
        // sizes. We cannot do the same for heights though because many code paths inside updateLogicalHeight() require
        // a previous call to setLogicalHeight() to resolve heights properly (like for positioned items for example).
        computeTrackSizesForDefiniteSize(Style::GridTrackSizingDirection::Columns, availableSpaceForColumns, gridLayoutState);

        // 1.5. Compute Content Distribution offsets for column tracks
        m_offsetBetweenColumns = computeContentPositionAndDistributionOffset(Style::GridTrackSizingDirection::Columns, m_trackSizingAlgorithm.freeSpace(Style::GridTrackSizingDirection::Columns).value(), nonCollapsedTracks(Style::GridTrackSizingDirection::Columns));

        // 2. Next, the track sizing algorithm resolves the sizes of the grid rows,
        // using the grid column sizes calculated in the previous step.
        auto availableLogicalHeightForContentBox = this->availableLogicalHeightForContentBox();
        bool shouldRecomputeHeight = false;
        if (!availableLogicalHeightForContentBox) {
            computeTrackSizesForIndefiniteSize(m_trackSizingAlgorithm, Style::GridTrackSizingDirection::Rows, gridLayoutState);
            if (shouldApplySizeContainment())
                shouldRecomputeHeight = true;
        } else
            computeTrackSizesForDefiniteSize(Style::GridTrackSizingDirection::Rows, *availableLogicalHeightForContentBox, gridLayoutState);

        LayoutUnit trackBasedLogicalHeight = borderAndPaddingLogicalHeight() + scrollbarLogicalHeight();
        if (auto size = explicitIntrinsicInnerLogicalSize(Style::GridTrackSizingDirection::Rows))
            trackBasedLogicalHeight += size.value();
        else
            trackBasedLogicalHeight += m_trackSizingAlgorithm.computeTrackBasedSize();

        if (shouldRecomputeHeight)
            computeTrackSizesForDefiniteSize(Style::GridTrackSizingDirection::Rows, trackBasedLogicalHeight, gridLayoutState);

        setLogicalHeight(trackBasedLogicalHeight);

        updateLogicalHeight();

        // Once grid's indefinite height is resolved, we can compute the
        // available free space for Content Alignment.
        if (!availableLogicalHeightForContentBox)
            m_trackSizingAlgorithm.setFreeSpace(Style::GridTrackSizingDirection::Rows, logicalHeight() - trackBasedLogicalHeight);

        // 2.5. Compute Content Distribution offsets for rows tracks
        m_offsetBetweenRows = computeContentPositionAndDistributionOffset(Style::GridTrackSizingDirection::Rows, m_trackSizingAlgorithm.freeSpace(Style::GridTrackSizingDirection::Rows).value(), nonCollapsedTracks(Style::GridTrackSizingDirection::Rows));

        if (!aspectRatioBlockSizeDependentGridItems.isEmpty()) {
            updateGridAreaForAspectRatioItems(aspectRatioBlockSizeDependentGridItems, gridLayoutState);
            updateLogicalWidth();
        }

        // 3. If the min-content contribution of any grid items have changed based on the row
        // sizes calculated in step 2, steps 1 and 2 are repeated with the new min-content
        // contribution (once only).
        repeatTracksSizingIfNeeded(availableSpaceForColumns, contentBoxLogicalHeight(), gridLayoutState);

        // Grid container should have the minimum height of a line if it's editable. That does not affect track sizing though.
        if (hasLineIfEmpty()) {
            LayoutUnit minHeightForEmptyLine = borderAndPaddingLogicalHeight()
                + lineHeight()
                + scrollbarLogicalHeight();
            setLogicalHeight(std::max(logicalHeight(), minHeightForEmptyLine));
        }

        layoutGridItems(gridLayoutState);

        endAndCommitUpdateScrollInfoAfterLayoutTransaction();

        if (size() != previousSize)
            relayoutChildren = RelayoutChildren::Yes;

        if (isDocumentElementRenderer())
            layoutOutOfFlowBoxes(RelayoutChildren::Yes);
        else
            layoutOutOfFlowBoxes(relayoutChildren);

        m_trackSizingAlgorithm.reset();

        computeOverflow(layoutOverflowLogicalBottom(*this));

        updateDescendantTransformsAfterLayout();
    }

    updateLayerTransform();

    // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
    // we overflow or not.
    updateScrollInfoAfterLayout();

    repainter.repaintAfterLayout();

    clearNeedsLayout();

    m_trackSizingAlgorithm.clearBaselineItemsCache();
    m_baselineItemsCached = false;
}

void RenderGrid::layoutMasonry(RelayoutChildren relayoutChildren)
{
    LayoutRepainter repainter(*this);
    {
        LayoutStateMaintainer statePusher(*this, locationOffset(), isTransformed() || hasReflection() || writingMode().isBlockFlipped());
        GridLayoutState gridLayoutState;

        clearGridItemOverridingSizesBeforeLayout(*this);

        preparePaginationBeforeBlockLayout(relayoutChildren);
        beginUpdateScrollInfoAfterLayoutTransaction();

        LayoutSize previousSize = size();

        // FIXME: We should use RenderBlock::hasDefiniteLogicalHeight() only but it does not work for positioned stuff.
        // FIXME: Consider caching the hasDefiniteLogicalHeight value throughout the layout.
        // FIXME: We might need to cache the hasDefiniteLogicalHeight if the call of RenderBlock::hasDefiniteLogicalHeight() causes a relevant performance regression.
        bool hasDefiniteLogicalHeight = RenderBlock::hasDefiniteLogicalHeight() || overridingBorderBoxLogicalHeight() || computeContentLogicalHeight(style().logicalHeight(), std::nullopt);

        auto aspectRatioBlockSizeDependentGridItems = computeAspectRatioDependentAndBaselineItems(gridLayoutState);

        resetLogicalHeightBeforeLayoutIfNeeded();

        // Fieldsets need to find their legend and position it inside the border of the object.
        // The legend then gets skipped during normal layout. The same is true for ruby text.
        // It doesn't get included in the normal layout process but is instead skipped.
        layoutExcludedChildren(relayoutChildren);

        updateLogicalWidth();

        LayoutUnit availableSpaceForColumns = contentBoxLogicalWidth();
        placeItemsOnGrid(availableSpaceForColumns);

        m_trackSizingAlgorithm.setAvailableSpace(Style::GridTrackSizingDirection::Columns, availableSpaceForColumns);
        performPreLayoutForGridItems(m_trackSizingAlgorithm, ShouldUpdateGridAreaLogicalSize::Yes);

        // 1. First, the track sizing algorithm is used to resolve the sizes of the grid columns. At this point the
        // logical width is always definite as the above call to updateLogicalWidth() properly resolves intrinsic
        // sizes. We cannot do the same for heights though because many code paths inside updateLogicalHeight() require
        // a previous call to setLogicalHeight() to resolve heights properly (like for positioned items for example).
        computeTrackSizesForDefiniteSize(Style::GridTrackSizingDirection::Columns, availableSpaceForColumns, gridLayoutState);

        // 1.5. Compute Content Distribution offsets for column tracks
        m_offsetBetweenColumns = computeContentPositionAndDistributionOffset(Style::GridTrackSizingDirection::Columns, m_trackSizingAlgorithm.freeSpace(Style::GridTrackSizingDirection::Columns).value(), nonCollapsedTracks(Style::GridTrackSizingDirection::Columns));

        // 2. Next, the track sizing algorithm resolves the sizes of the grid rows,
        // using the grid column sizes calculated in the previous step.
        bool shouldRecomputeHeight = false;
        if (!hasDefiniteLogicalHeight) {
            computeTrackSizesForIndefiniteSize(m_trackSizingAlgorithm, Style::GridTrackSizingDirection::Rows, gridLayoutState);
            if (shouldApplySizeContainment())
                shouldRecomputeHeight = true;
        } else
            computeTrackSizesForDefiniteSize(Style::GridTrackSizingDirection::Rows, availableLogicalHeight(AvailableLogicalHeightType::ExcludeMarginBorderPadding), gridLayoutState);

        auto performMasonryPlacement = [&](const Style::GridTrackSizingDirection masonryAxisDirection) {
            auto gridAxisDirection = masonryAxisDirection == Style::GridTrackSizingDirection::Rows ? Style::GridTrackSizingDirection::Columns : Style::GridTrackSizingDirection::Rows;
            unsigned gridAxisTracksBeforeAutoPlacement = currentGrid().numTracks(gridAxisDirection);

            m_masonryLayout.performMasonryPlacement(m_trackSizingAlgorithm, gridAxisTracksBeforeAutoPlacement, masonryAxisDirection, GridMasonryLayout::MasonryLayoutPhase::LayoutPhase);
        };

        if (areMasonryRows())
            performMasonryPlacement(Style::GridTrackSizingDirection::Rows);
        else if (areMasonryColumns())
            performMasonryPlacement(Style::GridTrackSizingDirection::Columns);

        LayoutUnit trackBasedLogicalHeight = borderAndPaddingLogicalHeight() + scrollbarLogicalHeight();
        if (auto size = explicitIntrinsicInnerLogicalSize(Style::GridTrackSizingDirection::Rows))
            trackBasedLogicalHeight += size.value();
        else {
            if (areMasonryRows())
                trackBasedLogicalHeight += m_masonryLayout.gridContentSize();
            else
                trackBasedLogicalHeight += m_trackSizingAlgorithm.computeTrackBasedSize();
        }
        if (shouldRecomputeHeight)
            computeTrackSizesForDefiniteSize(Style::GridTrackSizingDirection::Rows, trackBasedLogicalHeight, gridLayoutState);

        setLogicalHeight(trackBasedLogicalHeight);

        updateLogicalHeight();

        // Once grid's indefinite height is resolved, we can compute the
        // available free space for Content Alignment.
        if (!hasDefiniteLogicalHeight || areMasonryRows())
            m_trackSizingAlgorithm.setFreeSpace(Style::GridTrackSizingDirection::Rows, logicalHeight() - trackBasedLogicalHeight);

        // 2.5. Compute Content Distribution offsets for rows tracks
        m_offsetBetweenRows = computeContentPositionAndDistributionOffset(Style::GridTrackSizingDirection::Rows, m_trackSizingAlgorithm.freeSpace(Style::GridTrackSizingDirection::Rows).value(), nonCollapsedTracks(Style::GridTrackSizingDirection::Rows));

        if (!aspectRatioBlockSizeDependentGridItems.isEmpty()) {
            updateGridAreaForAspectRatioItems(aspectRatioBlockSizeDependentGridItems, gridLayoutState);
            updateLogicalWidth();
        }

        // Grid container should have the minimum height of a line if it's editable. That does not affect track sizing though.
        if (hasLineIfEmpty()) {
            LayoutUnit minHeightForEmptyLine = borderAndPaddingLogicalHeight()
                + lineHeight()
                + scrollbarLogicalHeight();
            setLogicalHeight(std::max(logicalHeight(), minHeightForEmptyLine));
        }

        layoutMasonryItems(gridLayoutState);

        endAndCommitUpdateScrollInfoAfterLayoutTransaction();

        if (size() != previousSize)
            relayoutChildren = RelayoutChildren::Yes;

        if (isDocumentElementRenderer())
            layoutOutOfFlowBoxes(RelayoutChildren::Yes);
        else
            layoutOutOfFlowBoxes(relayoutChildren);

        m_trackSizingAlgorithm.reset();

        computeOverflow(layoutOverflowLogicalBottom(*this));

        updateDescendantTransformsAfterLayout();
    }

    updateLayerTransform();

    // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
    // we overflow or not.
    updateScrollInfoAfterLayout();

    repainter.repaintAfterLayout();

    clearNeedsLayout();

    m_trackSizingAlgorithm.clearBaselineItemsCache();
    m_baselineItemsCached = false;
}

LayoutUnit RenderGrid::gridGap(Style::GridTrackSizingDirection direction, std::optional<LayoutUnit> availableSize) const
{
    ASSERT(!availableSize || *availableSize >= 0);
    auto& gap = style().gap(direction);
    if (gap.isNormal()) {
        if (!isSubgrid(direction))
            return 0_lu;

        auto parentDirection = GridLayoutFunctions::flowAwareDirectionForParent(*this, *parent(), direction);
        if (!availableSize)
            return downcast<RenderGrid>(parent())->gridGap(parentDirection, std::nullopt);
        return downcast<RenderGrid>(parent())->gridGap(parentDirection);
    }

    return Style::evaluate(gap, availableSize.value_or(0_lu));
}

LayoutUnit RenderGrid::gridGap(Style::GridTrackSizingDirection direction) const
{
    return gridGap(direction, availableSpaceForGutters(direction));
}

LayoutUnit RenderGrid::gridItemOffset(Style::GridTrackSizingDirection direction) const
{
    return direction == Style::GridTrackSizingDirection::Rows ? m_offsetBetweenRows.distributionOffset : m_offsetBetweenColumns.distributionOffset;
}

LayoutUnit RenderGrid::guttersSize(Style::GridTrackSizingDirection direction, unsigned startLine, unsigned span, std::optional<LayoutUnit> availableSize) const
{
    if (span <= 1)
        return { };

    LayoutUnit gap = gridGap(direction, availableSize);

    // Fast path, no collapsing tracks.
    if (!currentGrid().hasAutoRepeatEmptyTracks(direction))
        return gap * (span - 1);

    // If there are collapsing tracks we need to be sure that gutters are properly collapsed. Apart
    // from that, if we have a collapsed track in the edges of the span we're considering, we need
    // to move forward (or backwards) in order to know whether the collapsed tracks reach the end of
    // the grid (so the gap becomes 0) or there is a non empty track before that.

    LayoutUnit gapAccumulator;
    unsigned endLine = startLine + span;

    for (unsigned line = startLine; line < endLine - 1; ++line) {
        if (!currentGrid().isEmptyAutoRepeatTrack(direction, line))
            gapAccumulator += gap;
    }

    // The above loop adds one extra gap for trailing collapsed tracks.
    if (gapAccumulator && currentGrid().isEmptyAutoRepeatTrack(direction, endLine - 1)) {
        ASSERT(gapAccumulator >= gap);
        gapAccumulator -= gap;
    }

    // If the startLine is the start line of a collapsed track we need to go backwards till we reach
    // a non collapsed track. If we find a non collapsed track we need to add that gap.
    size_t nonEmptyTracksBeforeStartLine = 0;
    if (startLine && currentGrid().isEmptyAutoRepeatTrack(direction, startLine)) {
        nonEmptyTracksBeforeStartLine = startLine;
        auto begin = currentGrid().autoRepeatEmptyTracks(direction)->begin();
        for (auto it = begin; *it != startLine; ++it) {
            ASSERT(nonEmptyTracksBeforeStartLine);
            --nonEmptyTracksBeforeStartLine;
        }
        if (nonEmptyTracksBeforeStartLine)
            gapAccumulator += gap;
    }

    // If the endLine is the end line of a collapsed track we need to go forward till we reach a non
    // collapsed track. If we find a non collapsed track we need to add that gap.
    if (currentGrid().isEmptyAutoRepeatTrack(direction, endLine - 1)) {
        unsigned nonEmptyTracksAfterEndLine = currentGrid().numTracks(direction) - endLine;
        auto currentEmptyTrack = currentGrid().autoRepeatEmptyTracks(direction)->find(endLine - 1);
        auto endEmptyTrack = currentGrid().autoRepeatEmptyTracks(direction)->end();
        // HashSet iterators do not implement operator- so we have to manually iterate to know the number of remaining empty tracks.
        for (auto it = ++currentEmptyTrack; it != endEmptyTrack; ++it) {
            ASSERT(nonEmptyTracksAfterEndLine >= 1);
            --nonEmptyTracksAfterEndLine;
        }
        if (nonEmptyTracksAfterEndLine) {
            // We shouldn't count the gap twice if the span starts and ends in a collapsed track between two non-empty tracks.
            if (!nonEmptyTracksBeforeStartLine)
                gapAccumulator += gap;
        } else if (nonEmptyTracksBeforeStartLine) {
            // We shouldn't count the gap if the span starts and ends in a collapsed but there isn't non-empty tracks afterwards (it's at the end of the grid).
            gapAccumulator -= gap;
        }
    }

    return gapAccumulator;
}

void RenderGrid::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
{
    GridLayoutState gridLayoutState;

    LayoutUnit gridItemMinWidth;
    LayoutUnit gridItemMaxWidth;
    bool hadExcludedChildren = computePreferredWidthsForExcludedChildren(gridItemMinWidth, gridItemMaxWidth);

    Grid grid(const_cast<RenderGrid&>(*this));
    m_grid.m_currentGrid = std::ref(grid);
    GridTrackSizingAlgorithm algorithm(this, grid);
    // placeItemsOnGrid isn't const since it mutates our grid, but it's safe to do
    // so here since we've overridden m_currentGrid with a stack based temporary.
    const_cast<RenderGrid&>(*this).placeItemsOnGrid(std::nullopt);

    performPreLayoutForGridItems(algorithm, ShouldUpdateGridAreaLogicalSize::No);

        if (m_baselineItemsCached)
        algorithm.copyBaselineItemsCache(m_trackSizingAlgorithm, Style::GridTrackSizingDirection::Columns);
        else {
            auto emptyCallback = [](RenderBox*) { };
        cacheBaselineAlignedGridItems(*this, algorithm, { AlignmentContextTypes::Columns }, emptyCallback, !isSubgridRows());
        }

    computeTrackSizesForIndefiniteSize(algorithm, Style::GridTrackSizingDirection::Columns, gridLayoutState, &minLogicalWidth, &maxLogicalWidth);

    if (isMasonry(Style::GridTrackSizingDirection::Columns)) {
        // The track sizing algorithm will only be run once in this case, since track sizing will not run in the masonry direction.
        computeTrackSizesForIndefiniteSize(algorithm, Style::GridTrackSizingDirection::Rows, gridLayoutState, &minLogicalWidth, &maxLogicalWidth);

        auto gridAxisTracksCountBeforeAutoPlacement = currentGrid().numTracks(Style::GridTrackSizingDirection::Rows);

        // To determine the width of the grid when we have a masonry layout in the column direction we need to perform a layout with the min and max
        // content sizes. We will override the grid items widths to accomplish this and then calculate the final grid content size after placement.
        m_masonryLayout.performMasonryPlacement(algorithm, gridAxisTracksCountBeforeAutoPlacement, Style::GridTrackSizingDirection::Columns, GridMasonryLayout::MasonryLayoutPhase::MinContentPhase);
        minLogicalWidth = m_masonryLayout.gridContentSize();

        m_masonryLayout.performMasonryPlacement(algorithm, gridAxisTracksCountBeforeAutoPlacement, Style::GridTrackSizingDirection::Columns, GridMasonryLayout::MasonryLayoutPhase::MaxContentPhase);
        maxLogicalWidth = m_masonryLayout.gridContentSize();
    }

    m_grid.resetCurrentGrid();

    if (hadExcludedChildren) {
        minLogicalWidth = std::max(minLogicalWidth, gridItemMinWidth);
        maxLogicalWidth = std::max(maxLogicalWidth, gridItemMaxWidth);
    }

    LayoutUnit scrollbarWidth = intrinsicScrollbarLogicalWidthIncludingGutter();
    minLogicalWidth += scrollbarWidth;
    maxLogicalWidth += scrollbarWidth;
}

void RenderGrid::computeTrackSizesForIndefiniteSize(GridTrackSizingAlgorithm& algorithm, Style::GridTrackSizingDirection direction, GridLayoutState& gridLayoutState, LayoutUnit* minIntrinsicSize, LayoutUnit* maxIntrinsicSize) const
{
    auto autoMarginResolutionScope = SetForScope(m_isComputingTrackSizes, true);
    algorithm.run(direction, numTracks(direction), SizingOperation::IntrinsicSizeComputation, std::nullopt, gridLayoutState);

    size_t numberOfTracks = algorithm.tracks(direction).size();
    LayoutUnit totalGuttersSize = direction == Style::GridTrackSizingDirection::Columns && explicitIntrinsicInnerLogicalSize(direction).has_value() ? 0_lu : guttersSize(direction, 0, numberOfTracks, std::nullopt);

    if (minIntrinsicSize)
        *minIntrinsicSize = algorithm.minContentSize() + totalGuttersSize;
    if (maxIntrinsicSize)
        *maxIntrinsicSize = algorithm.maxContentSize() + totalGuttersSize;

    ASSERT(algorithm.tracksAreWiderThanMinTrackBreadth());
}

bool RenderGrid::shouldCheckExplicitIntrinsicInnerLogicalSize(Style::GridTrackSizingDirection direction) const
{
    return direction == Style::GridTrackSizingDirection::Columns ? shouldApplySizeOrInlineSizeContainment() : shouldApplySizeContainment();
}

std::optional<LayoutUnit> RenderGrid::explicitIntrinsicInnerLogicalSize(Style::GridTrackSizingDirection direction) const
{
    if (!shouldCheckExplicitIntrinsicInnerLogicalSize(direction))
        return std::nullopt;
    if (direction == Style::GridTrackSizingDirection::Columns)
        return explicitIntrinsicInnerLogicalWidth();
    return explicitIntrinsicInnerLogicalHeight();
}

unsigned RenderGrid::computeAutoRepeatTracksCount(Style::GridTrackSizingDirection direction, std::optional<LayoutUnit> availableSize) const
{
    ASSERT(!availableSize || availableSize.value() != -1);
    bool isRowAxis = direction == Style::GridTrackSizingDirection::Columns;
    if (isSubgrid(direction))
        return 0;

    const auto& tracks = style().gridTemplateList(direction);
    const auto& autoRepeatTracks = tracks.autoRepeatSizes;
    unsigned autoRepeatTrackListLength = autoRepeatTracks.size();

    if (!autoRepeatTrackListLength)
        return 0;

    bool needsToFulfillMinimumSize = false;
    if (!availableSize) {
        // Both min-width/height and max-width/height calculations may need the containing block size, so it is cached if lazily computed.
        std::optional<LayoutUnit> cachedContainingBlockAvailableSize;
        auto containingBlockAvailableSize = [&] -> LayoutUnit {
            if (cachedContainingBlockAvailableSize)
                return *cachedContainingBlockAvailableSize;
            cachedContainingBlockAvailableSize = isRowAxis
                ? containingBlockLogicalWidthForContent()
                : containingBlockLogicalHeightForContent(AvailableLogicalHeightType::ExcludeMarginBorderPadding);
            return *cachedContainingBlockAvailableSize;
        };

        auto& maxSize = isRowAxis ? style().logicalMaxWidth() : style().logicalMaxHeight();
        auto availableMaxSize = WTF::switchOn(maxSize,
            [&](const Style::MaximumSize::Fixed& fixedMaxSize) -> std::optional<LayoutUnit> {
                auto maxSizeValue = LayoutUnit { fixedMaxSize.value };
                return isRowAxis
                    ? adjustContentBoxLogicalWidthForBoxSizing(maxSizeValue)
                    : adjustContentBoxLogicalHeightForBoxSizing(maxSizeValue);
            },
            [&](const Style::MaximumSize::Percentage& percentageMaxSize) -> std::optional<LayoutUnit> {
                auto maxSizeValue = Style::evaluate(percentageMaxSize, containingBlockAvailableSize());
                return isRowAxis
                    ? adjustContentBoxLogicalWidthForBoxSizing(maxSizeValue)
                    : adjustContentBoxLogicalHeightForBoxSizing(maxSizeValue);
            },
            [&](const Style::MaximumSize::Calc& calcMaxSize) -> std::optional<LayoutUnit> {
                auto maxSizeValue = Style::evaluate(calcMaxSize, containingBlockAvailableSize());
                return isRowAxis
                    ? adjustContentBoxLogicalWidthForBoxSizing(maxSizeValue)
                    : adjustContentBoxLogicalHeightForBoxSizing(maxSizeValue);
            },
            [&](const auto&) -> std::optional<LayoutUnit> {
                return { };
        }
        );

        auto& minSize = isRowAxis ? style().logicalMinWidth() : style().logicalMinHeight();
        auto& minSizeForOrthogonalAxis = isRowAxis ? style().logicalMinHeight() : style().logicalMinWidth();
        bool shouldComputeMinSizeFromAspectRatio = minSizeForOrthogonalAxis.isSpecified() && !shouldIgnoreAspectRatio();
        auto explicitIntrinsicInnerSize = explicitIntrinsicInnerLogicalSize(direction);

        if (!availableMaxSize && !minSize.isSpecified() && !shouldComputeMinSizeFromAspectRatio && !explicitIntrinsicInnerSize)
            return autoRepeatTrackListLength;

        auto availableMinSize = WTF::switchOn(minSize,
            [&](const Style::MinimumSize::Fixed& fixedMinSize) -> std::optional<LayoutUnit> {
                auto minSizeValue = LayoutUnit { fixedMinSize.value };
                return isRowAxis
                    ? adjustContentBoxLogicalWidthForBoxSizing(minSizeValue)
                    : adjustContentBoxLogicalHeightForBoxSizing(minSizeValue);
            },
            [&](const Style::MinimumSize::Percentage& percentageMinSize) -> std::optional<LayoutUnit> {
                auto minSizeValue = Style::evaluate(percentageMinSize, containingBlockAvailableSize());
                return isRowAxis
                    ? adjustContentBoxLogicalWidthForBoxSizing(minSizeValue)
                    : adjustContentBoxLogicalHeightForBoxSizing(minSizeValue);
            },
            [&](const Style::MinimumSize::Calc& calcMinSize) -> std::optional<LayoutUnit> {
                auto minSizeValue = Style::evaluate(calcMinSize, containingBlockAvailableSize());
                return isRowAxis
                    ? adjustContentBoxLogicalWidthForBoxSizing(minSizeValue)
                    : adjustContentBoxLogicalHeightForBoxSizing(minSizeValue);
            },
            [&](const auto&) -> std::optional<LayoutUnit> {
                if (!shouldComputeMinSizeFromAspectRatio)
                    return { };
            auto [logicalMinWidth, logicalMaxWidth] = computeMinMaxLogicalWidthFromAspectRatio();
                return logicalMinWidth;
        }
        );

        if (!maxSize.isSpecified() || explicitIntrinsicInnerSize)
            needsToFulfillMinimumSize = true;

        availableSize = std::max(std::max(valueOrDefault(availableMinSize), valueOrDefault(availableMaxSize)), valueOrDefault(explicitIntrinsicInnerSize));
        if (maxSize.isSpecified() && availableMaxSize < availableSize)
            availableSize = std::max(availableMinSize, availableMaxSize);
    }

    LayoutUnit autoRepeatTracksSize;
    for (auto& autoTrackSize : autoRepeatTracks) {
        ASSERT(autoTrackSize.minTrackBreadth().isLength());
        ASSERT(!autoTrackSize.minTrackBreadth().isFlex());

        auto& minTrackSizingFunction = autoTrackSize.minTrackBreadth();
        auto& maxTrackSizingFunction = autoTrackSize.maxTrackBreadth();
        bool hasDefiniteMaxTrackSizingFunction = maxTrackSizingFunction.isLength() && !maxTrackSizingFunction.isContentSized();
        bool hasDefiniteMinTrackSizingFunction = minTrackSizingFunction.isLength() && !minTrackSizingFunction.isContentSized();

        auto contributingTrackSize = [&] {
            if (hasDefiniteMaxTrackSizingFunction && hasDefiniteMinTrackSizingFunction)
                return std::max(Style::evaluate(minTrackSizingFunction.length(), *availableSize), Style::evaluate(maxTrackSizingFunction.length(), *availableSize));
            return hasDefiniteMaxTrackSizingFunction ? Style::evaluate(maxTrackSizingFunction.length(), *availableSize) : Style::evaluate(minTrackSizingFunction.length(), *availableSize);
        };
        autoRepeatTracksSize += contributingTrackSize();
    }
    // For the purpose of finding the number of auto-repeated tracks, the UA must floor the track size to a UA-specified
    // value to avoid division by zero. It is suggested that this floor be 1px.
    autoRepeatTracksSize = std::max<LayoutUnit>(1_lu, autoRepeatTracksSize);

    // There will be always at least 1 auto-repeat track, so take it already into account when computing the total track size.
    LayoutUnit tracksSize = autoRepeatTracksSize;
    auto& trackSizes = tracks.sizes;

    for (const auto& track : trackSizes) {
        bool hasDefiniteMaxTrackBreadth = track.maxTrackBreadth().isLength() && !track.maxTrackBreadth().isContentSized();
        ASSERT(hasDefiniteMaxTrackBreadth || (track.minTrackBreadth().isLength() && !track.minTrackBreadth().isContentSized()));
        tracksSize += Style::evaluate(hasDefiniteMaxTrackBreadth ? track.maxTrackBreadth().length() : track.minTrackBreadth().length(), availableSize.value());
    }

    // Add gutters as if auto repeat tracks were only repeated once. Gaps between different repetitions will be added later when
    // computing the number of repetitions of the auto repeat().
    LayoutUnit gapSize = gridGap(direction, availableSize);
    tracksSize += gapSize * (trackSizes.size() + autoRepeatTrackListLength - 1);

    LayoutUnit freeSpace = availableSize.value() - tracksSize;
    if (freeSpace <= 0)
        return autoRepeatTrackListLength;

    LayoutUnit autoRepeatSizeWithGap = autoRepeatTracksSize + gapSize * autoRepeatTrackListLength;
    unsigned repetitions = 1 + (freeSpace / autoRepeatSizeWithGap).toUnsigned();
    freeSpace -= autoRepeatSizeWithGap * (repetitions - 1);
    ASSERT(freeSpace >= 0);

    // Provided the grid container does not have a definite size or max-size in the relevant axis,
    // if the min size is definite then the number of repetitions is the largest possible positive
    // integer that fulfills that minimum requirement.
    if (needsToFulfillMinimumSize && freeSpace)
        ++repetitions;

    return repetitions * autoRepeatTrackListLength;
}

WTF::Range<size_t> RenderGrid::autoRepeatTracksRange(Style::GridTrackSizingDirection direction) const
{
    auto insertionPoint = style().gridTemplateList(direction).autoRepeatInsertionPoint;
    auto firstAutoRepeatTrack = insertionPoint + currentGrid().explicitGridStart(direction);
    auto lastAutoRepeatTrack = firstAutoRepeatTrack + currentGrid().autoRepeatTracks(direction);

    return { firstAutoRepeatTrack, lastAutoRepeatTrack };
}

std::unique_ptr<OrderedTrackIndexSet> RenderGrid::computeEmptyTracksForAutoRepeat(Style::GridTrackSizingDirection direction) const
{
    if (autoRepeatType(direction) != AutoRepeatType::Fit)
        return nullptr;

    std::unique_ptr<OrderedTrackIndexSet> emptyTrackIndexes;
    auto autoRepeatTracksRange = this->autoRepeatTracksRange(direction);

    if (!currentGrid().hasGridItems()) {
        emptyTrackIndexes = makeUnique<OrderedTrackIndexSet>();
        for (unsigned trackIndex = autoRepeatTracksRange.begin(); trackIndex < autoRepeatTracksRange.end(); ++trackIndex)
            emptyTrackIndexes->add(trackIndex);
    } else {
        for (unsigned trackIndex = autoRepeatTracksRange.begin(); trackIndex < autoRepeatTracksRange.end(); ++trackIndex) {
            GridIterator iterator(currentGrid(), direction, trackIndex);
            if (!iterator.nextGridItem()) {
                if (!emptyTrackIndexes)
                    emptyTrackIndexes = makeUnique<OrderedTrackIndexSet>();
                emptyTrackIndexes->add(trackIndex);
            }
        }
    }
    return emptyTrackIndexes;
}

unsigned RenderGrid::clampAutoRepeatTracks(Style::GridTrackSizingDirection direction, unsigned autoRepeatTracks) const
{
    if (!autoRepeatTracks)
        return 0;

    unsigned insertionPoint = style().gridTemplateList(direction).autoRepeatInsertionPoint;
    unsigned maxTracks = static_cast<unsigned>(Style::GridPosition::max());

    if (!insertionPoint)
        return std::min(autoRepeatTracks, maxTracks);

    if (insertionPoint >= maxTracks)
        return 0;

    return std::min(autoRepeatTracks, maxTracks - insertionPoint);
}

void RenderGrid::placeItems()
{
    updateLogicalWidth();

    LayoutUnit availableSpaceForColumns = contentBoxLogicalWidth();
    placeItemsOnGrid(availableSpaceForColumns);
}

static GridArea insertIntoGrid(Grid& grid, RenderBox& gridItem, const GridArea& area)
{
    GridArea clamped = grid.insert(gridItem, area);

    CheckedPtr renderGrid = dynamicDowncast<RenderGrid>(gridItem);
    if (!renderGrid)
        return clamped;

    if (renderGrid->isSubgridRows() || renderGrid->isSubgridColumns())
        renderGrid->placeItems();
    return clamped;
}

bool RenderGrid::isMasonry() const
{
    return areMasonryRows() || areMasonryColumns();
}

// Masonry Spec Section 2
// "If masonry is specified for both grid-template-columns and grid-template-rows, then the used value for grid-template-columns is none,
// and thus the inline axis will be the grid axis."
bool RenderGrid::isMasonry(Style::GridTrackSizingDirection direction) const
{
    // isSubgrid will return false if the masonry axis matches. Need to check style if we are a subgrid
    auto& tracks = style().gridTemplateList(direction);
    if (auto* parentGrid = dynamicDowncast<RenderGrid>(parent()); parentGrid && tracks.subgrid)
        return parentGrid->isMasonry(direction);
    return tracks.masonry;
}

// Masonry Spec Section 2.3.1 repeat(auto-fit)
// "repeat(auto-fit) behaves as repeat(auto-fill) when the other axis is a masonry axis."
// We need to lie here that we are really an auto-fill instead of an auto-fit.
AutoRepeatType RenderGrid::autoRepeatType(Style::GridTrackSizingDirection direction) const
{
    auto autoRepeatType = style().gridTemplateList(direction).autoRepeatType;

    if (isMasonry(orthogonalDirection(direction)) && autoRepeatType == AutoRepeatType::Fit)
        return AutoRepeatType::Fill;

    return autoRepeatType;
}

// FIXME: We shouldn't have to pass the available logical width as argument. The problem is that
// contentBoxLogicalWidth() does always return a value even if we cannot resolve it like when
// computing the intrinsic size (preferred widths). That's why we pass the responsibility to the
// caller who does know whether the available logical width is indefinite or not.
void RenderGrid::placeItemsOnGrid(std::optional<LayoutUnit> availableLogicalWidth)
{
    unsigned autoRepeatColumns = computeAutoRepeatTracksCount(Style::GridTrackSizingDirection::Columns, availableLogicalWidth);
    unsigned autoRepeatRows = computeAutoRepeatTracksCount(Style::GridTrackSizingDirection::Rows, availableLogicalHeightForPercentageComputation());
    autoRepeatRows = clampAutoRepeatTracks(Style::GridTrackSizingDirection::Rows, autoRepeatRows);
    autoRepeatColumns = clampAutoRepeatTracks(Style::GridTrackSizingDirection::Columns, autoRepeatColumns);

    if (isSubgridInParentDirection(Style::GridTrackSizingDirection::Columns) || isSubgridInParentDirection(Style::GridTrackSizingDirection::Rows)) {
        auto* parent = dynamicDowncast<RenderGrid>(this->parent());
        if (parent && parent->currentGrid().needsItemsPlacement())
            currentGrid().setNeedsItemsPlacement(true);
    }

    if (autoRepeatColumns != currentGrid().autoRepeatTracks(Style::GridTrackSizingDirection::Columns)
        || autoRepeatRows != currentGrid().autoRepeatTracks(Style::GridTrackSizingDirection::Rows)
        || isMasonry()) {
        currentGrid().setNeedsItemsPlacement(true);
        currentGrid().setAutoRepeatTracks(autoRepeatRows, autoRepeatColumns);
    }

    if (!currentGrid().needsItemsPlacement())
        return;

    ASSERT(!currentGrid().hasGridItems());
    populateExplicitGridAndOrderIterator();

    Vector<RenderBox*> autoMajorAxisAutoGridItems;
    Vector<RenderBox*> specifiedMajorAxisAutoGridItems;
    for (auto* gridItem = currentGrid().orderIterator().first(); gridItem; gridItem = currentGrid().orderIterator().next()) {
        if (currentGrid().orderIterator().shouldSkipChild(*gridItem))
            continue;

        // Grid items should use the grid area sizes instead of the containing block (grid container)
        // sizes, we initialize the overrides here if needed to ensure it.
        if (!gridItem->gridAreaContentLogicalWidth())
            gridItem->setGridAreaContentLogicalWidth(0_lu);
        if (!gridItem->gridAreaContentLogicalHeight())
            gridItem->setGridAreaContentLogicalHeight(std::nullopt);

        GridArea area = currentGrid().gridItemArea(*gridItem);
        currentGrid().clampAreaToSubgridIfNeeded(area);
        if (!area.rows.isIndefinite())
            area.rows.translate(currentGrid().explicitGridStart(Style::GridTrackSizingDirection::Rows));
        if (!area.columns.isIndefinite())
            area.columns.translate(currentGrid().explicitGridStart(Style::GridTrackSizingDirection::Columns));

        if (area.rows.isIndefinite() || area.columns.isIndefinite()) {
            currentGrid().setGridItemArea(*gridItem, area);
            bool majorAxisDirectionIsForColumns = autoPlacementMajorAxisDirection() == Style::GridTrackSizingDirection::Columns;
            if ((majorAxisDirectionIsForColumns && area.columns.isIndefinite())
                || (!majorAxisDirectionIsForColumns && area.rows.isIndefinite()))
                autoMajorAxisAutoGridItems.append(gridItem);
            else
                specifiedMajorAxisAutoGridItems.append(gridItem);
            continue;
        }
        insertIntoGrid(currentGrid(), *gridItem, { area.rows, area.columns });
    }

#if ASSERT_ENABLED
    if (currentGrid().hasGridItems()) {
        ASSERT(currentGrid().numTracks(Style::GridTrackSizingDirection::Rows) >= Style::GridPositionsResolver::explicitGridCount(*this, Style::GridTrackSizingDirection::Rows));
        ASSERT(currentGrid().numTracks(Style::GridTrackSizingDirection::Columns) >= Style::GridPositionsResolver::explicitGridCount(*this, Style::GridTrackSizingDirection::Columns));
    }
#endif

    auto performAutoPlacement = [&]() {
        placeSpecifiedMajorAxisItemsOnGrid(specifiedMajorAxisAutoGridItems);
        placeAutoMajorAxisItemsOnGrid(autoMajorAxisAutoGridItems);
    // Compute collapsible tracks for auto-fit.
        currentGrid().setAutoRepeatEmptyColumns(computeEmptyTracksForAutoRepeat(Style::GridTrackSizingDirection::Columns));
        currentGrid().setAutoRepeatEmptyRows(computeEmptyTracksForAutoRepeat(Style::GridTrackSizingDirection::Rows));

        currentGrid().setNeedsItemsPlacement(false);
    };

    performAutoPlacement();

#if ASSERT_ENABLED
    for (auto* gridItem = currentGrid().orderIterator().first(); gridItem; gridItem = currentGrid().orderIterator().next()) {
        if (currentGrid().orderIterator().shouldSkipChild(*gridItem))
            continue;

        GridArea area = currentGrid().gridItemArea(*gridItem);
        ASSERT(area.rows.isTranslatedDefinite() && area.columns.isTranslatedDefinite());
    }
#endif
}

LayoutUnit RenderGrid::masonryContentSize() const
{
    return m_masonryLayout.gridContentSize();
}

void RenderGrid::performPreLayoutForGridItems(const GridTrackSizingAlgorithm& algorithm, const ShouldUpdateGridAreaLogicalSize shouldUpdateGridAreaLogicalSize) const
{
    ASSERT(!algorithm.grid().needsItemsPlacement());
    // FIXME: We need a way when we are calling this during intrinsic size computation before performing
    // the layout. Maybe using the PreLayout phase ?
    for (auto* gridItem = firstChildBox(); gridItem; gridItem = gridItem->nextSiblingBox()) {
        if (gridItem->isOutOfFlowPositioned())
            continue;
        // Orthogonal items should be laid out in order to properly compute content-sized tracks that may depend on item's intrinsic size.
        // We also need to properly estimate its grid area size, since it may affect to the baseline shims if such item participates in baseline alignment.
        if (GridLayoutFunctions::isOrthogonalGridItem(*this, *gridItem)) {
            updateGridAreaLogicalSize(*gridItem, algorithm.estimatedGridAreaBreadthForGridItem(*gridItem, Style::GridTrackSizingDirection::Columns), algorithm.estimatedGridAreaBreadthForGridItem(*gridItem, Style::GridTrackSizingDirection::Rows));
            gridItem->layoutIfNeeded();
            continue;
        }
        // We need to layout the item to know whether it must synthesize its
        // baseline or not, which may imply a cyclic sizing dependency.
        // FIXME: Can we avoid it ?
        // FIXME: We also want to layout baseline aligned items within subgrids, but
        // we don't currently have a way to do that here.
        if (isBaselineAlignmentForGridItem(*gridItem)) {
            // FIXME: Hack to fix nested grid text size overflow during re-layouts.
            if (shouldUpdateGridAreaLogicalSize == ShouldUpdateGridAreaLogicalSize::Yes)
                updateGridAreaLogicalSize(*gridItem, algorithm.estimatedGridAreaBreadthForGridItem(*gridItem, Style::GridTrackSizingDirection::Columns), algorithm.estimatedGridAreaBreadthForGridItem(*gridItem, Style::GridTrackSizingDirection::Rows));
            gridItem->layoutIfNeeded();
        }
    }
}

void RenderGrid::populateExplicitGridAndOrderIterator()
{
    OrderIteratorPopulator populator(currentGrid().orderIterator());
    unsigned explicitRowStart = 0;
    unsigned explicitColumnStart = 0;
    unsigned maximumRowIndex = Style::GridPositionsResolver::explicitGridCount(*this, Style::GridTrackSizingDirection::Rows);
    unsigned maximumColumnIndex = Style::GridPositionsResolver::explicitGridCount(*this, Style::GridTrackSizingDirection::Columns);

    for (auto& gridItem : childrenOfType<RenderBox>(*this)) {
        if (!populator.collectChild(gridItem))
            continue;

        auto rowPositions = Style::GridPositionsResolver::resolveGridPositionsFromStyle(*this, gridItem, Style::GridTrackSizingDirection::Rows);
        if (!isSubgridRows()) {
            if (!rowPositions.isIndefinite()) {
                explicitRowStart = std::max<int>(explicitRowStart, -rowPositions.untranslatedStartLine());
                maximumRowIndex = std::max<int>(maximumRowIndex, rowPositions.untranslatedEndLine());
            } else {
                // Grow the grid for items with a definite row span, getting the largest such span.
                unsigned spanSize = Style::GridPositionsResolver::spanSizeForAutoPlacedItem(gridItem, Style::GridTrackSizingDirection::Rows);
                maximumRowIndex = std::max(maximumRowIndex, spanSize);
            }
        }

        auto columnPositions = Style::GridPositionsResolver::resolveGridPositionsFromStyle(*this, gridItem, Style::GridTrackSizingDirection::Columns);
        if (!isSubgridColumns()) {
            if (!columnPositions.isIndefinite()) {
                explicitColumnStart = std::max<int>(explicitColumnStart, -columnPositions.untranslatedStartLine());
                maximumColumnIndex = std::max<int>(maximumColumnIndex, columnPositions.untranslatedEndLine());
            } else {
                // Grow the grid for items with a definite column span, getting the largest such span.
                unsigned spanSize = Style::GridPositionsResolver::spanSizeForAutoPlacedItem(gridItem, Style::GridTrackSizingDirection::Columns);
                maximumColumnIndex = std::max(maximumColumnIndex, spanSize);
            }
        }

        currentGrid().setGridItemArea(gridItem, { rowPositions, columnPositions });
    }

    currentGrid().setExplicitGridStart(explicitRowStart, explicitColumnStart);
    currentGrid().ensureGridSize(maximumRowIndex + explicitRowStart, maximumColumnIndex + explicitColumnStart);
    currentGrid().setClampingForSubgrid(isSubgridRows() ? maximumRowIndex : 0, isSubgridColumns() ? maximumColumnIndex : 0);
}

GridArea RenderGrid::createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(const RenderBox& gridItem, Style::GridTrackSizingDirection specifiedDirection, const GridSpan& specifiedPositions) const
{
    auto crossDirection = orthogonalDirection(specifiedDirection);
    auto endOfCrossDirection = currentGrid().numTracks(crossDirection);
    auto crossDirectionSpanSize = Style::GridPositionsResolver::spanSizeForAutoPlacedItem(gridItem, crossDirection);
    auto crossDirectionPositions = GridSpan::translatedDefiniteGridSpan(endOfCrossDirection, endOfCrossDirection + crossDirectionSpanSize);

    return specifiedDirection == Style::GridTrackSizingDirection::Columns
        ? GridArea { crossDirectionPositions, specifiedPositions }
        : GridArea { specifiedPositions, crossDirectionPositions };
}

bool RenderGrid::isPlacedWithinExtrinsicallySizedExplicitTracks(const RenderBox& gridItem) const
{
    auto& currentGrid = this->currentGrid();
    if (currentGrid.needsItemsPlacement())
        return false;

    auto& gridStyle = style();
    auto gridItemArea = currentGrid.gridItemArea(gridItem);
    auto& gridColumnSizes = gridStyle.gridTemplateColumns().sizes;
    if (gridItemArea.columns.endLine() > gridColumnSizes.size())
        return false;

    for (auto columnIndex : gridItemArea.columns) {
        if (gridColumnSizes[columnIndex].isContentSized())
            return false;
    }

    auto& gridRowSizes = gridStyle.gridTemplateRows().sizes;
    if (gridItemArea.rows.endLine() > gridRowSizes.size())
        return false;

    for (auto rowIndex : gridItemArea.rows) {
        if (gridRowSizes[rowIndex].isContentSized())
            return false;
    }
    return true;
}

void RenderGrid::placeSpecifiedMajorAxisItemsOnGrid(const Vector<RenderBox*>& autoGridItems)
{
    bool isForColumns = autoPlacementMajorAxisDirection() == Style::GridTrackSizingDirection::Columns;
    bool isGridAutoFlowDense = style().isGridAutoFlowAlgorithmDense();

    // Mapping between the major axis tracks (rows or columns) and the last auto-placed item's position inserted on
    // that track. This is needed to implement "sparse" packing for items locked to a given track.
    // See https://drafts.csswg.org/css-grid-2/#auto-placement-algo
    HashMap<unsigned, unsigned, DefaultHash<unsigned>, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> minorAxisCursors;

    for (auto& autoGridItem : autoGridItems) {
        GridSpan majorAxisPositions = currentGrid().gridItemSpan(*autoGridItem, autoPlacementMajorAxisDirection());
        ASSERT(majorAxisPositions.isTranslatedDefinite());
        ASSERT(currentGrid().gridItemSpan(*autoGridItem, autoPlacementMinorAxisDirection()).isIndefinite());
        unsigned minorAxisSpanSize = Style::GridPositionsResolver::spanSizeForAutoPlacedItem(*autoGridItem, autoPlacementMinorAxisDirection());
        unsigned majorAxisInitialPosition = majorAxisPositions.startLine();

        GridIterator iterator(currentGrid(), autoPlacementMajorAxisDirection(), majorAxisPositions.startLine(), isGridAutoFlowDense ? 0 : minorAxisCursors.get(majorAxisInitialPosition));
        auto emptyGridArea = iterator.nextEmptyGridArea(majorAxisPositions.integerSpan(), minorAxisSpanSize);
        if (!emptyGridArea)
            emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(*autoGridItem, autoPlacementMajorAxisDirection(), majorAxisPositions);

        emptyGridArea = insertIntoGrid(currentGrid(), *autoGridItem, *emptyGridArea);

        if (!isGridAutoFlowDense)
            minorAxisCursors.set(majorAxisInitialPosition, isForColumns ? emptyGridArea->rows.startLine() : emptyGridArea->columns.startLine());
    }
}

void RenderGrid::placeAutoMajorAxisItemsOnGrid(const Vector<RenderBox*>& autoGridItems)
{
    AutoPlacementCursor autoPlacementCursor = {0, 0};
    bool isGridAutoFlowDense = style().isGridAutoFlowAlgorithmDense();

    for (auto& autoGridItem : autoGridItems) {
        placeAutoMajorAxisItemOnGrid(*autoGridItem, autoPlacementCursor);

        if (isGridAutoFlowDense) {
            autoPlacementCursor.first = 0;
            autoPlacementCursor.second = 0;
        }
    }
}

void RenderGrid::placeAutoMajorAxisItemOnGrid(RenderBox& gridItem, AutoPlacementCursor& autoPlacementCursor)
{
    ASSERT(currentGrid().gridItemSpan(gridItem, autoPlacementMajorAxisDirection()).isIndefinite());
    unsigned majorAxisSpanSize = Style::GridPositionsResolver::spanSizeForAutoPlacedItem(gridItem, autoPlacementMajorAxisDirection());

    const unsigned endOfMajorAxis = currentGrid().numTracks(autoPlacementMajorAxisDirection());
    unsigned majorAxisAutoPlacementCursor = autoPlacementMajorAxisDirection() == Style::GridTrackSizingDirection::Columns ? autoPlacementCursor.second : autoPlacementCursor.first;
    unsigned minorAxisAutoPlacementCursor = autoPlacementMajorAxisDirection() == Style::GridTrackSizingDirection::Columns ? autoPlacementCursor.first : autoPlacementCursor.second;

    auto emptyGridArea = std::optional<GridArea> { };
    GridSpan minorAxisPositions = currentGrid().gridItemSpan(gridItem, autoPlacementMinorAxisDirection());
    if (minorAxisPositions.isTranslatedDefinite()) {
        // Move to the next track in major axis if initial position in minor axis is before auto-placement cursor.
        if (minorAxisPositions.startLine() < minorAxisAutoPlacementCursor)
            majorAxisAutoPlacementCursor++;

        if (majorAxisAutoPlacementCursor < endOfMajorAxis) {
            GridIterator iterator(currentGrid(), autoPlacementMinorAxisDirection(), minorAxisPositions.startLine(), majorAxisAutoPlacementCursor);
            emptyGridArea = iterator.nextEmptyGridArea(minorAxisPositions.integerSpan(), majorAxisSpanSize);
        }

        if (!emptyGridArea)
            emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(gridItem, autoPlacementMinorAxisDirection(), minorAxisPositions);
    } else {
        unsigned minorAxisSpanSize = Style::GridPositionsResolver::spanSizeForAutoPlacedItem(gridItem, autoPlacementMinorAxisDirection());

        for (unsigned majorAxisIndex = majorAxisAutoPlacementCursor; majorAxisIndex < endOfMajorAxis; ++majorAxisIndex) {
            GridIterator iterator(currentGrid(), autoPlacementMajorAxisDirection(), majorAxisIndex, minorAxisAutoPlacementCursor);
            emptyGridArea = iterator.nextEmptyGridArea(majorAxisSpanSize, minorAxisSpanSize);

            if (emptyGridArea) {
                // Check that it fits in the minor axis direction, as we shouldn't grow in that direction here (it was already managed in populateExplicitGridAndOrderIterator()).
                unsigned minorAxisFinalPositionIndex = autoPlacementMinorAxisDirection() == Style::GridTrackSizingDirection::Columns ? emptyGridArea->columns.endLine() : emptyGridArea->rows.endLine();
                const unsigned endOfMinorAxis = currentGrid().numTracks(autoPlacementMinorAxisDirection());
                if (minorAxisFinalPositionIndex <= endOfMinorAxis)
                    break;

                // Discard empty grid area as it does not fit in the minor axis direction.
                // We don't need to create a new empty grid area yet as we might find a valid one in the next iteration.
                emptyGridArea = { };
            }

            // As we're moving to the next track in the major axis we should reset the auto-placement cursor in the minor axis.
            minorAxisAutoPlacementCursor = 0;
        }

        if (!emptyGridArea)
            emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(gridItem, autoPlacementMinorAxisDirection(), GridSpan::translatedDefiniteGridSpan(0, minorAxisSpanSize));
    }

    emptyGridArea = insertIntoGrid(currentGrid(), gridItem, *emptyGridArea);
    autoPlacementCursor.first = emptyGridArea->rows.startLine();
    autoPlacementCursor.second = emptyGridArea->columns.startLine();
}

Style::GridTrackSizingDirection RenderGrid::autoPlacementMajorAxisDirection() const
{
    if (areMasonryColumns())
        return Style::GridTrackSizingDirection::Columns;
    if (areMasonryRows())
        return Style::GridTrackSizingDirection::Rows;

    return style().isGridAutoFlowDirectionColumn() ? Style::GridTrackSizingDirection::Columns : Style::GridTrackSizingDirection::Rows;
}

Style::GridTrackSizingDirection RenderGrid::autoPlacementMinorAxisDirection() const
{
    return orthogonalDirection(autoPlacementMajorAxisDirection());
}

void RenderGrid::setNeedsItemPlacement(SubgridDidChange subgridDidChange)
{
    if (currentGrid().needsItemsPlacement())
        return;

    currentGrid().setNeedsItemsPlacement(true);

    auto currentChild = this;
    while (currentChild && (subgridDidChange == SubgridDidChange::Yes || currentChild->isSubgridRows() || currentChild->isSubgridColumns())) {
        currentChild = dynamicDowncast<RenderGrid>(currentChild->parent());
        if (currentChild)
            currentChild->currentGrid().setNeedsItemsPlacement(true);
        subgridDidChange = SubgridDidChange::No;
    }
}

Vector<LayoutUnit> RenderGrid::trackSizesForComputedStyle(Style::GridTrackSizingDirection direction) const
{
    const auto& positions = this->positions(direction);
    auto numPositions = positions.size();

    Vector<LayoutUnit> tracks;
    if (numPositions < 2)
        return tracks;

    ASSERT(!currentGrid().needsItemsPlacement());
    auto offsetBetweenTracks = this->offsetBetweenTracks(direction).distributionOffset;
    bool hasCollapsedTracks = currentGrid().hasAutoRepeatEmptyTracks(direction);
    auto gap = !hasCollapsedTracks ? gridGap(direction) : 0_lu;
    tracks.reserveInitialCapacity(numPositions - 1);
    tracks.appendUsingFunctor(numPositions - 2, [&](size_t i) {
        return positions[i + 1] - positions[i] - offsetBetweenTracks - gap;
    });
    tracks.append(positions[numPositions - 1] - positions[numPositions - 2]);

    if (!hasCollapsedTracks)
        return tracks;

    size_t remainingEmptyTracks = currentGrid().autoRepeatEmptyTracks(direction)->size();
    size_t lastLine = tracks.size();
    gap = gridGap(direction);
    for (size_t i = 1; i < lastLine; ++i) {
        if (currentGrid().isEmptyAutoRepeatTrack(direction, i - 1))
            --remainingEmptyTracks;
        else {
            // Remove the gap between consecutive non empty tracks. Remove it also just once for an
            // arbitrary number of empty tracks between two non empty ones.
            bool allRemainingTracksAreEmpty = remainingEmptyTracks == (lastLine - i);
            if (!allRemainingTracksAreEmpty || !currentGrid().isEmptyAutoRepeatTrack(direction, i))
                tracks[i - 1] -= gap;
        }
    }

    return tracks;
}

static const StyleContentAlignmentData& contentAlignmentNormalBehaviorGrid()
{
    static const StyleContentAlignmentData normalBehavior = {ContentPosition::Normal, ContentDistribution::Stretch};
    return normalBehavior;
}

static bool overrideSizeChanged(const RenderBox& gridItem, Style::GridTrackSizingDirection direction, std::optional<LayoutUnit> width, std::optional<LayoutUnit> height)
{
    if (direction == Style::GridTrackSizingDirection::Columns) {
        if (auto gridAreaContentLogicalWidth = gridItem.gridAreaContentLogicalWidth())
            return *gridAreaContentLogicalWidth != width;
        return true;
    }
    if (auto gridAreaContentLogicalHeight = gridItem.gridAreaContentLogicalHeight())
        return *gridAreaContentLogicalHeight != height;
    return true;
}

static bool hasRelativeBlockAxisSize(const RenderGrid& grid, const RenderBox& gridItem)
{
    return GridLayoutFunctions::isOrthogonalGridItem(grid, gridItem) ? gridItem.hasRelativeLogicalWidth() || gridItem.style().logicalWidth().isAuto() : gridItem.hasRelativeLogicalHeight();
}

void RenderGrid::updateGridAreaLogicalSize(RenderBox& gridItem, std::optional<LayoutUnit> width, std::optional<LayoutUnit> height) const
{
    // Because the grid area cannot be styled, we don't need to adjust
    // the grid breadth to account for 'box-sizing'.
    bool gridAreaWidthChanged = overrideSizeChanged(gridItem, Style::GridTrackSizingDirection::Columns, width, height);
    bool gridAreaHeightChanged = overrideSizeChanged(gridItem, Style::GridTrackSizingDirection::Rows, width, height);
    if (gridAreaWidthChanged || (gridAreaHeightChanged && hasRelativeBlockAxisSize(*this, gridItem)))
        gridItem.setNeedsLayout(MarkOnlyThis);

    gridItem.setGridAreaContentLogicalWidth(width);
    gridItem.setGridAreaContentLogicalHeight(height);
}

void RenderGrid::updateGridAreaForAspectRatioItems(const Vector<RenderBox*>& autoGridItems, GridLayoutState& gridLayoutState)
{
    populateGridPositionsForDirection(m_trackSizingAlgorithm, Style::GridTrackSizingDirection::Columns);
    populateGridPositionsForDirection(m_trackSizingAlgorithm, Style::GridTrackSizingDirection::Rows);

    for (auto& autoGridItem : autoGridItems) {
        updateGridAreaLogicalSize(*autoGridItem, gridAreaBreadthForGridItemIncludingAlignmentOffsets(*autoGridItem, Style::GridTrackSizingDirection::Columns), gridAreaBreadthForGridItemIncludingAlignmentOffsets(*autoGridItem, Style::GridTrackSizingDirection::Rows));
        // For an item with aspect-ratio, if it has stretch alignment that stretches to the definite row, we also need to transfer the size before laying out the grid item.
        if (autoGridItem->hasStretchedLogicalHeight())
            applyStretchAlignmentToGridItemIfNeeded(*autoGridItem, gridLayoutState);
    }
}

void RenderGrid::layoutGridItems(GridLayoutState& gridLayoutState)
{
    populateGridPositionsForDirection(m_trackSizingAlgorithm, Style::GridTrackSizingDirection::Columns);
    populateGridPositionsForDirection(m_trackSizingAlgorithm, Style::GridTrackSizingDirection::Rows);

    for (auto& gridItem : childrenOfType<RenderBox>(*this)) {
        if (currentGrid().orderIterator().shouldSkipChild(gridItem)) {
            if (gridItem.isOutOfFlowPositioned())
                prepareGridItemForPositionedLayout(gridItem);
            continue;
        }

        auto* renderGrid = dynamicDowncast<RenderGrid>(gridItem);
        if (renderGrid && (renderGrid->isSubgridColumns() || renderGrid->isSubgridRows()))
            gridItem.setNeedsLayout(MarkOnlyThis);

        // Setting the definite grid area's sizes. It may imply that the
        // item must perform a layout if its area differs from the one
        // used during the track sizing algorithm.
        updateGridAreaLogicalSize(gridItem, gridAreaBreadthForGridItemIncludingAlignmentOffsets(gridItem, Style::GridTrackSizingDirection::Columns), gridAreaBreadthForGridItemIncludingAlignmentOffsets(gridItem, Style::GridTrackSizingDirection::Rows));

        LayoutRect oldGridItemRect = gridItem.frameRect();

        // Stretching logic might force a grid item layout, so we need to run it before the layoutIfNeeded
        // call to avoid unnecessary relayouts. This might imply that grid item margins, needed to correctly
        // determine the available space before stretching, are not set yet.
        applyStretchAlignmentToGridItemIfNeeded(gridItem, gridLayoutState);
        applySubgridStretchAlignmentToGridItemIfNeeded(gridItem);

        gridItem.layoutIfNeeded();

        // We need pending layouts to be done in order to compute auto-margins properly.
        updateAutoMarginsInColumnAxisIfNeeded(gridItem);
        updateAutoMarginsInRowAxisIfNeeded(gridItem);

        setLogicalPositionForGridItem(gridItem);

        // If the grid item moved, we have to repaint it as well as any floating/positioned
        // descendants. An exception is if we need a layout. In this case, we know we're going to
        // repaint ourselves (and the grid item) anyway.
        if (!selfNeedsLayout() && gridItem.checkForRepaintDuringLayout())
            gridItem.repaintDuringLayoutIfMoved(oldGridItemRect);
    }
}

void RenderGrid::layoutMasonryItems(GridLayoutState& gridLayoutState)
{
    populateGridPositionsForDirection(m_trackSizingAlgorithm, Style::GridTrackSizingDirection::Columns);
    populateGridPositionsForDirection(m_trackSizingAlgorithm, Style::GridTrackSizingDirection::Rows);

    for (auto& gridItem : childrenOfType<RenderBox>(*this)) {
        if (currentGrid().orderIterator().shouldSkipChild(gridItem)) {
            if (gridItem.isOutOfFlowPositioned())
                prepareGridItemForPositionedLayout(gridItem);
            continue;
        }

        auto* renderGrid = dynamicDowncast<RenderGrid>(gridItem);
        if (renderGrid && (renderGrid->isSubgridColumns() || renderGrid->isSubgridRows()))
            gridItem.setNeedsLayout(MarkOnlyThis);

        // Setting the definite grid area's sizes. It may imply that the
        // item must perform a layout if its area differs from the one
        // used during the track sizing algorithm.
        updateGridAreaLogicalSize(gridItem, gridAreaBreadthForGridItemIncludingAlignmentOffsets(gridItem, Style::GridTrackSizingDirection::Columns), gridAreaBreadthForGridItemIncludingAlignmentOffsets(gridItem, Style::GridTrackSizingDirection::Rows));

        LayoutRect oldGridItemRect = gridItem.frameRect();

        // Stretching logic might force a grid item layout, so we need to run it before the layoutIfNeeded
        // call to avoid unnecessary relayouts. This might imply that grid item margins, needed to correctly
        // determine the available space before stretching, are not set yet.
        applyStretchAlignmentToGridItemIfNeeded(gridItem, gridLayoutState);
        applySubgridStretchAlignmentToGridItemIfNeeded(gridItem);

        gridItem.layoutIfNeeded();

        // We need pending layouts to be done in order to compute auto-margins properly.
        updateAutoMarginsInColumnAxisIfNeeded(gridItem);
        updateAutoMarginsInRowAxisIfNeeded(gridItem);

        setLogicalPositionForGridItem(gridItem);

        // If the grid item moved, we have to repaint it as well as any floating/positioned
        // descendants. An exception is if we need a layout. In this case, we know we're going to
        // repaint ourselves (and the grid item) anyway.
        if (!selfNeedsLayout() && gridItem.checkForRepaintDuringLayout())
            gridItem.repaintDuringLayoutIfMoved(oldGridItemRect);
    }
}

void RenderGrid::prepareGridItemForPositionedLayout(RenderBox& gridItem)
{
    ASSERT(gridItem.isOutOfFlowPositioned());
    gridItem.containingBlock()->addOutOfFlowBox(gridItem);

    RenderLayer* gridItemLayer = gridItem.layer();
    // Static position of a positioned grid item should use the content-box (https://drafts.csswg.org/css-grid/#static-position).
    gridItemLayer->setStaticInlinePosition(borderAndPaddingStart());
    gridItemLayer->setStaticBlockPosition(borderAndPaddingBefore());
}

bool RenderGrid::hasStaticPositionForGridItem(const RenderBox& gridItem, Style::GridTrackSizingDirection direction) const
{
    return direction == Style::GridTrackSizingDirection::Columns ? gridItem.style().hasStaticInlinePosition(isHorizontalWritingMode()) : gridItem.style().hasStaticBlockPosition(isHorizontalWritingMode());
}

void RenderGrid::layoutOutOfFlowBox(RenderBox& gridItem, RelayoutChildren relayoutChildren, bool fixedPositionObjectsOnly)
{
    if (layoutContext().isSkippedContentRootForLayout(*this)) {
        gridItem.clearNeedsLayoutForSkippedContent();
        return;
    }

    LayoutUnit columnBreadth = gridAreaBreadthForOutOfFlowGridItem(gridItem, Style::GridTrackSizingDirection::Columns);
    LayoutUnit rowBreadth = gridAreaBreadthForOutOfFlowGridItem(gridItem, Style::GridTrackSizingDirection::Rows);

    gridItem.setGridAreaContentLogicalWidth(columnBreadth);
    gridItem.setGridAreaContentLogicalHeight(rowBreadth);

    // Mark for layout as we're resetting the position before and we relay in generic layout logic
    // for positioned items in order to get the offsets properly resolved.
    gridItem.setChildNeedsLayout(MarkOnlyThis);

    RenderBlock::layoutOutOfFlowBox(gridItem, relayoutChildren, fixedPositionObjectsOnly);
}

LayoutUnit RenderGrid::gridAreaBreadthForGridItemIncludingAlignmentOffsets(const RenderBox& gridItem, Style::GridTrackSizingDirection direction) const
{
    if (direction == Style::GridTrackSizingDirection::Rows) {
        if (areMasonryRows())
            return isHorizontalWritingMode() ? gridItem.height() + gridItem.verticalMarginExtent() : gridItem.width() + gridItem.horizontalMarginExtent();
    } else if (areMasonryColumns())
        return isHorizontalWritingMode() ? gridItem.width() + gridItem.horizontalMarginExtent() : gridItem.height() + gridItem.verticalMarginExtent();

    // We need the cached value when available because Content Distribution alignment properties
    // may have some influence in the final grid area breadth.
    const auto& tracks = m_trackSizingAlgorithm.tracks(direction);
    const auto& span = currentGrid().gridItemSpan(gridItem, direction);
    const auto& positions = this->positions(direction);

    auto initialTrackPosition = positions[span.startLine()];
    auto finalTrackPosition = positions[span.endLine() - 1];

    // Track Positions vector stores the 'start' grid line of each track, so we have to add last track's baseSize.
    return finalTrackPosition - initialTrackPosition + tracks[span.endLine() - 1].baseSize();
}

void RenderGrid::populateGridPositionsForDirection(const GridTrackSizingAlgorithm& algorithm, Style::GridTrackSizingDirection direction)
{
    // Since we add alignment offsets and track gutters, grid lines are not always adjacent. Hence, we will have to
    // assume from now on that we just store positions of the initial grid lines of each track,
    // except the last one, which is the only one considered as a final grid line of a track.

    // The grid container's frame elements (border, padding and <content-position> offset) are sensible to the
    // inline-axis flow direction. However, column lines positions are 'direction' unaware. This simplification
    // allows us to use the same indexes to identify the columns independently on the inline-axis direction.
    bool isRowAxis = direction == Style::GridTrackSizingDirection::Columns;
    auto& tracks = algorithm.tracks(direction);
    unsigned numberOfTracks = tracks.size();
    unsigned numberOfLines = numberOfTracks + 1;
    unsigned lastLine = numberOfLines - 1;
    bool hasCollapsedTracks = currentGrid().hasAutoRepeatEmptyTracks(direction);
    size_t numberOfCollapsedTracks = hasCollapsedTracks ? currentGrid().autoRepeatEmptyTracks(direction)->size() : 0;
    const auto& offsetBetweenTracks = this->offsetBetweenTracks(direction);
    auto& positions = this->positions(direction);
    positions.resize(numberOfLines);

    auto borderAndPadding = isRowAxis ? borderAndPaddingStart() : borderAndPaddingBefore();

    positions[0] = borderAndPadding + offsetBetweenTracks.positionOffset;
    if (numberOfLines > 1) {
        // If we have collapsed tracks we just ignore gaps here and add them later as we might not
        // compute the gap between two consecutive tracks without examining the surrounding ones.
        LayoutUnit gap = !hasCollapsedTracks ? gridGap(direction) : 0_lu;
        unsigned nextToLastLine = numberOfLines - 2;

        for (unsigned i = 0; i < nextToLastLine; ++i)
            positions[i + 1] = positions[i] + offsetBetweenTracks.distributionOffset + tracks[i].unclampedBaseSize() + gap;
        positions[lastLine] = positions[nextToLastLine] + tracks[nextToLastLine].unclampedBaseSize();

        if (isMasonry(direction))
            positions[lastLine] = m_masonryLayout.gridContentSize() + positions[0];

        // Adjust collapsed gaps. Collapsed tracks cause the surrounding gutters to collapse (they
        // coincide exactly) except on the edges of the grid where they become 0.
        if (hasCollapsedTracks) {
            gap = gridGap(direction);
            unsigned remainingEmptyTracks = numberOfCollapsedTracks;
            LayoutUnit offsetAccumulator;
            LayoutUnit gapAccumulator;
            for (unsigned i = 1; i < lastLine; ++i) {
                if (currentGrid().isEmptyAutoRepeatTrack(direction, i - 1)) {
                    --remainingEmptyTracks;
                    offsetAccumulator += offsetBetweenTracks.distributionOffset;
                } else {
                    // Add gap between consecutive non empty tracks. Add it also just once for an
                    // arbitrary number of empty tracks between two non empty ones.
                    bool allRemainingTracksAreEmpty = remainingEmptyTracks == (lastLine - i);
                    if (!allRemainingTracksAreEmpty || !currentGrid().isEmptyAutoRepeatTrack(direction, i))
                        gapAccumulator += gap;
                }
                positions[i] += gapAccumulator - offsetAccumulator;
            }
            positions[lastLine] += gapAccumulator - offsetAccumulator;
        }
    }
}

static LayoutUnit computeOverflowAlignmentOffset(OverflowAlignment overflow, LayoutUnit trackSize, LayoutUnit gridItemSize)
{
    LayoutUnit offset = trackSize - gridItemSize;
    switch (overflow) {
    case OverflowAlignment::Safe:
        // If overflow is 'safe', we have to make sure we don't overflow the 'start'
        // edge (potentially cause some data loss as the overflow is unreachable).
        return std::max<LayoutUnit>(0, offset);
    case OverflowAlignment::Unsafe:
    case OverflowAlignment::Default:
        // If we overflow our alignment container and overflow is 'true' (default), we
        // ignore the overflow and just return the value regardless (which may cause data
        // loss as we overflow the 'start' edge).
        return offset;
    }

    ASSERT_NOT_REACHED();
    return 0;
}

LayoutUnit RenderGrid::availableAlignmentSpaceForGridItemBeforeStretching(LayoutUnit gridAreaBreadthForGridItem, const RenderBox& gridItem, Style::GridTrackSizingDirection direction) const
{
    // Because we want to avoid multiple layouts, stretching logic might be performed before
    // grid items are laid out, so we can't use the grid item cached values. Hence, we need to
    // compute margins in order to determine the available height before stretching.
    auto gridItemFlowDirection = GridLayoutFunctions::flowAwareDirectionForGridItem(*this, gridItem, direction);
    return std::max(0_lu, gridAreaBreadthForGridItem - GridLayoutFunctions::marginLogicalSizeForGridItem(*this, gridItemFlowDirection, gridItem));
}

StyleSelfAlignmentData RenderGrid::alignSelfForGridItem(const RenderBox& gridItem, StretchingMode stretchingMode, const RenderStyle* gridStyle) const
{
    CheckedPtr renderGrid = dynamicDowncast<RenderGrid>(gridItem);
    if (renderGrid && renderGrid->isSubgridInParentDirection(Style::GridTrackSizingDirection::Rows))
        return { ItemPosition::Stretch, OverflowAlignment::Default };
    if (!gridStyle)
        gridStyle = &style();
    auto normalBehavior = stretchingMode == StretchingMode::Any ? selfAlignmentNormalBehavior(&gridItem) : ItemPosition::Normal;
    return gridItem.style().resolvedAlignSelf(gridStyle, normalBehavior);
}

StyleSelfAlignmentData RenderGrid::justifySelfForGridItem(const RenderBox& gridItem, StretchingMode stretchingMode, const RenderStyle* gridStyle) const
{
    CheckedPtr renderGrid = dynamicDowncast<RenderGrid>(gridItem);
    if (renderGrid && renderGrid->isSubgridInParentDirection(Style::GridTrackSizingDirection::Columns))
        return { ItemPosition::Stretch, OverflowAlignment::Default };
    if (!gridStyle)
        gridStyle = &style();
    auto normalBehavior = stretchingMode == StretchingMode::Any ? selfAlignmentNormalBehavior(&gridItem) : ItemPosition::Normal;
    return gridItem.style().resolvedJustifySelf(gridStyle, normalBehavior);
}

bool RenderGrid::aspectRatioPrefersInline(const RenderBox& gridItem, bool blockFlowIsColumnAxis)
{
    if (!gridItem.style().hasAspectRatio())
        return false;
    bool hasExplicitInlineStretch = justifySelfForGridItem(gridItem, StretchingMode::Explicit).position() == ItemPosition::Stretch;
    bool hasExplicitBlockStretch = alignSelfForGridItem(gridItem, StretchingMode::Explicit).position() == ItemPosition::Stretch;
    if (!blockFlowIsColumnAxis)
        std::swap(hasExplicitInlineStretch, hasExplicitBlockStretch);
    return !hasExplicitBlockStretch;
}

inline bool RenderGrid::allowedToStretchGridItemAlongColumnAxis(const RenderBox& gridItem) const
{
    return alignSelfForGridItem(gridItem).position() == ItemPosition::Stretch && hasAutoSizeInColumnAxis(gridItem) && !hasAutoMarginsInColumnAxis(gridItem);
}

inline bool RenderGrid::allowedToStretchGridItemAlongRowAxis(const RenderBox& gridItem) const
{
    return justifySelfForGridItem(gridItem).position() == ItemPosition::Stretch && hasAutoSizeInRowAxis(gridItem) && !hasAutoMarginsInRowAxis(gridItem);
}

// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
void RenderGrid::applyStretchAlignmentToGridItemIfNeeded(RenderBox& gridItem, GridLayoutState& gridLayoutState)
{
    ASSERT(gridItem.gridAreaContentLogicalHeight());
    ASSERT(gridItem.gridAreaContentLogicalWidth());

    // We clear height and width override values because we will decide now whether it's allowed or
    // not, evaluating the conditions which might have changed since the old values were set.
    gridItem.clearOverridingSize();

    auto gridItemBlockDirection = GridLayoutFunctions::flowAwareDirectionForGridItem(*this, gridItem, Style::GridTrackSizingDirection::Rows);
    auto gridItemInlineDirection = GridLayoutFunctions::flowAwareDirectionForGridItem(*this, gridItem, Style::GridTrackSizingDirection::Columns);
    bool blockFlowIsColumnAxis = gridItemBlockDirection == Style::GridTrackSizingDirection::Rows;
    bool allowedToStretchgridItemBlockSize = blockFlowIsColumnAxis ? allowedToStretchGridItemAlongColumnAxis(gridItem) : allowedToStretchGridItemAlongRowAxis(gridItem);
    if (allowedToStretchgridItemBlockSize && !aspectRatioPrefersInline(gridItem, blockFlowIsColumnAxis)) {
        auto overridingContainingBlockContentSizeForGridItem = GridLayoutFunctions::overridingContainingBlockContentSizeForGridItem(gridItem, gridItemBlockDirection);
        ASSERT(overridingContainingBlockContentSizeForGridItem && *overridingContainingBlockContentSizeForGridItem);
        LayoutUnit stretchedLogicalHeight = availableAlignmentSpaceForGridItemBeforeStretching(overridingContainingBlockContentSizeForGridItem->value(), gridItem, Style::GridTrackSizingDirection::Rows);
        LayoutUnit desiredLogicalHeight = gridItem.constrainLogicalHeightByMinMax(stretchedLogicalHeight, std::nullopt);
        gridItem.setOverridingBorderBoxLogicalHeight(desiredLogicalHeight);

        auto itemNeedsRelayoutForStretchAlignment = [&]() {
            if (desiredLogicalHeight != gridItem.logicalHeight())
                return true;

            if (canSetColumnAxisStretchRequirementForItem(gridItem))
                return gridLayoutState.containsLayoutRequirementForGridItem(gridItem, ItemLayoutRequirement::NeedsColumnAxisStretchAlignment);

            return is<RenderBlock>(gridItem) && downcast<RenderBlock>(gridItem).hasPercentHeightDescendants();
        }();
        // Checking the logical-height of a grid item isn't enough. Setting an override logical-height
        // changes the definiteness, resulting in percentages to resolve differently.
        //
        // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905.
        if (itemNeedsRelayoutForStretchAlignment) {
            gridItem.setLogicalHeight(0_lu);
            gridItem.setNeedsLayout(MarkOnlyThis);
        }
    } else if (!allowedToStretchgridItemBlockSize && allowedToStretchGridItemAlongRowAxis(gridItem)) {
        auto overridingContainingBlockContentSizeForGridItem = GridLayoutFunctions::overridingContainingBlockContentSizeForGridItem(gridItem, gridItemInlineDirection);
        ASSERT(overridingContainingBlockContentSizeForGridItem && *overridingContainingBlockContentSizeForGridItem);
        LayoutUnit stretchedLogicalWidth = availableAlignmentSpaceForGridItemBeforeStretching(overridingContainingBlockContentSizeForGridItem->value(), gridItem, Style::GridTrackSizingDirection::Columns);
        LayoutUnit desiredLogicalWidth = gridItem.constrainLogicalWidthByMinMax(stretchedLogicalWidth, contentBoxWidth(), *this);
        gridItem.setOverridingBorderBoxLogicalWidth(desiredLogicalWidth);
        if (desiredLogicalWidth != gridItem.logicalWidth())
            gridItem.setNeedsLayout(MarkOnlyThis);
    }
}

void RenderGrid::applySubgridStretchAlignmentToGridItemIfNeeded(RenderBox& gridItem)
{
    CheckedPtr renderGrid = dynamicDowncast<RenderGrid>(gridItem);
    if (!renderGrid)
        return;

    if (renderGrid->isSubgrid(Style::GridTrackSizingDirection::Rows)) {
        auto gridItemBlockDirection = GridLayoutFunctions::flowAwareDirectionForGridItem(*this, gridItem, Style::GridTrackSizingDirection::Rows);
        auto overridingContainingBlockContentSizeForGridItem = GridLayoutFunctions::overridingContainingBlockContentSizeForGridItem(gridItem, gridItemBlockDirection);
        ASSERT(overridingContainingBlockContentSizeForGridItem && *overridingContainingBlockContentSizeForGridItem);
        auto stretchedLogicalHeight = availableAlignmentSpaceForGridItemBeforeStretching(overridingContainingBlockContentSizeForGridItem->value(), gridItem, Style::GridTrackSizingDirection::Rows);
        gridItem.setOverridingBorderBoxLogicalHeight(stretchedLogicalHeight);
    }

    if (renderGrid->isSubgrid(Style::GridTrackSizingDirection::Columns)) {
        auto gridItemInlineDirection = GridLayoutFunctions::flowAwareDirectionForGridItem(*this, gridItem, Style::GridTrackSizingDirection::Columns);
        auto overridingContainingBlockContentSizeForGridItem = GridLayoutFunctions::overridingContainingBlockContentSizeForGridItem(gridItem, gridItemInlineDirection);
        ASSERT(overridingContainingBlockContentSizeForGridItem && *overridingContainingBlockContentSizeForGridItem);
        auto stretchedLogicalWidth = availableAlignmentSpaceForGridItemBeforeStretching(overridingContainingBlockContentSizeForGridItem->value(), gridItem, Style::GridTrackSizingDirection::Columns);
        gridItem.setOverridingBorderBoxLogicalWidth(stretchedLogicalWidth);
    }
}

// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
bool RenderGrid::hasAutoMarginsInColumnAxis(const RenderBox& gridItem) const
{
    if (isHorizontalWritingMode())
        return gridItem.style().marginTop().isAuto() || gridItem.style().marginBottom().isAuto();
    return gridItem.style().marginLeft().isAuto() || gridItem.style().marginRight().isAuto();
}

// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
bool RenderGrid::hasAutoMarginsInRowAxis(const RenderBox& gridItem) const
{
    if (isHorizontalWritingMode())
        return gridItem.style().marginLeft().isAuto() || gridItem.style().marginRight().isAuto();
    return gridItem.style().marginTop().isAuto() || gridItem.style().marginBottom().isAuto();
}

// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
void RenderGrid::updateAutoMarginsInRowAxisIfNeeded(RenderBox& gridItem)
{
    ASSERT(!gridItem.isOutOfFlowPositioned());

    auto& marginStart = gridItem.style().marginStart(writingMode());
    auto& marginEnd = gridItem.style().marginEnd(writingMode());
    LayoutUnit marginLogicalWidth;
    // We should only consider computed margins if their specified value isn't
    // 'auto', since such computed value may come from a previous layout and may
    // be incorrect now.
    if (!marginStart.isAuto())
        marginLogicalWidth += gridItem.marginStart();
    if (!marginEnd.isAuto())
        marginLogicalWidth += gridItem.marginEnd();

    auto availableAlignmentSpace = gridItem.gridAreaContentLogicalWidth()->value() - gridItem.logicalWidth() - marginLogicalWidth;
    if (availableAlignmentSpace <= 0)
        return;

    if (marginStart.isAuto() && marginEnd.isAuto()) {
        gridItem.setMarginStart(availableAlignmentSpace / 2, writingMode());
        gridItem.setMarginEnd(availableAlignmentSpace / 2, writingMode());
    } else if (marginStart.isAuto()) {
        gridItem.setMarginStart(availableAlignmentSpace, writingMode());
    } else if (marginEnd.isAuto()) {
        gridItem.setMarginEnd(availableAlignmentSpace, writingMode());
    }
}

// FIXME: This logic is shared by RenderFlexibleBox, so it should be moved to RenderBox.
void RenderGrid::updateAutoMarginsInColumnAxisIfNeeded(RenderBox& gridItem)
{
    ASSERT(!gridItem.isOutOfFlowPositioned());

    auto& marginBefore = gridItem.style().marginBefore(writingMode());
    auto& marginAfter = gridItem.style().marginAfter(writingMode());
    LayoutUnit marginLogicalHeight;
    // We should only consider computed margins if their specified value isn't
    // 'auto', since such computed value may come from a previous layout and may
    // be incorrect now.
    if (!marginBefore.isAuto())
        marginLogicalHeight += gridItem.marginBefore();
    if (!marginAfter.isAuto())
        marginLogicalHeight += gridItem.marginAfter();

    auto availableAlignmentSpace = gridItem.gridAreaContentLogicalHeight()->value() - gridItem.logicalHeight() - marginLogicalHeight;
    if (availableAlignmentSpace <= 0)
        return;

    if (marginBefore.isAuto() && marginAfter.isAuto()) {
        gridItem.setMarginBefore(availableAlignmentSpace / 2, writingMode());
        gridItem.setMarginAfter(availableAlignmentSpace / 2, writingMode());
    } else if (marginBefore.isAuto()) {
        gridItem.setMarginBefore(availableAlignmentSpace, writingMode());
    } else if (marginAfter.isAuto()) {
        gridItem.setMarginAfter(availableAlignmentSpace, writingMode());
    }
}

bool RenderGrid::isChildEligibleForMarginTrim(MarginTrimType marginTrimType, const RenderBox& gridItem) const
{
    ASSERT(style().marginTrim().contains(marginTrimType));

    auto isTrimmingBlockDirection = marginTrimType == MarginTrimType::BlockStart || marginTrimType == MarginTrimType::BlockEnd;
    auto itemGridSpan = isTrimmingBlockDirection ? currentGrid().gridItemSpanIgnoringCollapsedTracks(gridItem, Style::GridTrackSizingDirection::Rows) : currentGrid().gridItemSpanIgnoringCollapsedTracks(gridItem, Style::GridTrackSizingDirection::Columns);
    switch (marginTrimType) {
    case MarginTrimType::BlockStart:
    case MarginTrimType::InlineStart:
        return !itemGridSpan.startLine();
    case MarginTrimType::BlockEnd:
        return itemGridSpan.endLine() == currentGrid().numTracks(Style::GridTrackSizingDirection::Rows);
    case MarginTrimType::InlineEnd:
        return itemGridSpan.endLine() == currentGrid().numTracks(Style::GridTrackSizingDirection::Columns);
    }
    ASSERT_NOT_REACHED();
    return false;
}

bool RenderGrid::isBaselineAlignmentForGridItem(const RenderBox& gridItem) const
{
    return isBaselineAlignmentForGridItem(gridItem, Style::GridTrackSizingDirection::Columns) || isBaselineAlignmentForGridItem(gridItem, Style::GridTrackSizingDirection::Rows);
}

bool RenderGrid::isBaselineAlignmentForGridItem(const RenderBox& gridItem, Style::GridTrackSizingDirection alignmentContextType) const
{
    if (gridItem.isOutOfFlowPositioned())
        return false;
    auto align = selfAlignmentForGridItem(alignmentContextType, gridItem).position();
    bool hasAutoMargins = alignmentContextType == Style::GridTrackSizingDirection::Rows ? hasAutoMarginsInColumnAxis(gridItem) : hasAutoMarginsInRowAxis(gridItem);
    return isBaselinePosition(align) && !hasAutoMargins;
}

std::optional<LayoutUnit> RenderGrid::firstLineBaseline() const
{
    if ((isWritingModeRoot() && !isFlexItem()) || !currentGrid().hasGridItems() || shouldApplyLayoutContainment())
        return std::nullopt;

    // Finding the first grid item in grid order.
    auto baselineGridItem = getBaselineGridItem(ItemPosition::Baseline);

    if (!baselineGridItem)
        return std::nullopt;

    auto baseline = GridLayoutFunctions::isOrthogonalGridItem(*this, *baselineGridItem) ? std::nullopt : baselineGridItem->firstLineBaseline();
    // We take border-box's bottom if no valid baseline.
    if (!baseline) {
        // FIXME: We should pass |direction| into firstLineBaseline and stop bailing out if we're a writing
        // mode root. This would also fix some cases where the grid is orthogonal to its container.
        auto gridWritingMode = style().writingMode();
        auto dominantBaseline = BaselineAlignmentState::dominantBaseline(gridWritingMode);
        auto direction = isHorizontalWritingMode() ? LineDirection::Horizontal : LineDirection::Vertical;
        return BaselineAlignmentState::synthesizedBaseline(*baselineGridItem, dominantBaseline, gridWritingMode, direction, BaselineSynthesisEdge::BorderBox) + logicalTopForChild(*baselineGridItem);
    }
    return baseline.value() + baselineGridItem->logicalTop().toInt();
}

std::optional<LayoutUnit> RenderGrid::lastLineBaseline() const
{
    if (isWritingModeRoot() || !currentGrid().hasGridItems() || shouldApplyLayoutContainment())
        return std::nullopt;

    auto baselineGridItem = getBaselineGridItem(ItemPosition::LastBaseline);
    if (!baselineGridItem)
        return std::nullopt;

    auto baseline = GridLayoutFunctions::isOrthogonalGridItem(*this, *baselineGridItem) ? std::nullopt : baselineGridItem->lastLineBaseline();
    if (!baseline) {
        auto direction = isHorizontalWritingMode() ? LineDirection::Horizontal : LineDirection::Vertical;
        auto gridWritingMode = style().writingMode();
        auto dominantBaseline = BaselineAlignmentState::dominantBaseline(gridWritingMode);
        return BaselineAlignmentState::synthesizedBaseline(*baselineGridItem, dominantBaseline, gridWritingMode, direction, BaselineSynthesisEdge::BorderBox) + logicalTopForChild(*baselineGridItem);

    }

    return baseline.value() + baselineGridItem->logicalTop().toInt();
}

SingleThreadWeakPtr<RenderBox> RenderGrid::getBaselineGridItem(ItemPosition alignment) const
{
    ASSERT(alignment == ItemPosition::Baseline || alignment == ItemPosition::LastBaseline);
    const RenderBox* baselineGridItem = nullptr;
    unsigned numColumns = currentGrid().numTracks(Style::GridTrackSizingDirection::Columns);
    auto rowIndexDeterminingBaseline = alignment == ItemPosition::Baseline ? 0 : currentGrid().numTracks(Style::GridTrackSizingDirection::Rows) - 1;
    for (size_t column = 0; column < numColumns; column++) {
        auto cell = currentGrid().cell(rowIndexDeterminingBaseline, alignment == ItemPosition::Baseline ? column : numColumns - column - 1);

        for (auto& gridItem : cell) {
            ASSERT(gridItem.get());
            // If an item participates in baseline alignment, we select such item.
            if (isBaselineAlignmentForGridItem(*gridItem, Style::GridTrackSizingDirection::Rows)) {
                auto gridItemAlignment = selfAlignmentForGridItem(Style::GridTrackSizingDirection::Rows, *gridItem).position();
                if (rowIndexDeterminingBaseline == GridLayoutFunctions::alignmentContextForBaselineAlignment(gridSpanForGridItem(*gridItem, Style::GridTrackSizingDirection::Rows), gridItemAlignment)) {
                // FIXME: self-baseline and content-baseline alignment not implemented yet.
                    baselineGridItem = gridItem.get();
                break;
            }
            }
            if (!baselineGridItem)
                baselineGridItem = gridItem.get();
        }
    }
    return baselineGridItem;
}

LayoutUnit RenderGrid::columnAxisBaselineOffsetForGridItem(const RenderBox& gridItem) const
{
    // FIXME : CSS Masonry does not properly handle baseline calculations currently.
    // We will just skip this running this step if we detect the RenderGrid is Masonry for now.
    if (isMasonry())
        return LayoutUnit { };

    if (isSubgridRows()) {
        RenderGrid* outer = downcast<RenderGrid>(parent());
        if (GridLayoutFunctions::isOrthogonalGridItem(*outer, *this))
            return outer->rowAxisBaselineOffsetForGridItem(gridItem);
        return outer->columnAxisBaselineOffsetForGridItem(gridItem);
    }
    return m_trackSizingAlgorithm.baselineOffsetForGridItem(gridItem, Style::GridTrackSizingDirection::Rows);
}

LayoutUnit RenderGrid::rowAxisBaselineOffsetForGridItem(const RenderBox& gridItem) const
{
    // FIXME : CSS Masonry does not properly handle baseline calculations currently.
    // We will just skip this running this step if we detect the RenderGrid is Masonry for now.
    if (isMasonry())
        return LayoutUnit { };

    if (isSubgridColumns()) {
        RenderGrid* outer = downcast<RenderGrid>(parent());
        if (GridLayoutFunctions::isOrthogonalGridItem(*outer, *this))
            return outer->columnAxisBaselineOffsetForGridItem(gridItem);
        return outer->rowAxisBaselineOffsetForGridItem(gridItem);
    }
    return m_trackSizingAlgorithm.baselineOffsetForGridItem(gridItem, Style::GridTrackSizingDirection::Columns);
}

GridAxisPosition RenderGrid::columnAxisPositionForGridItem(const RenderBox& gridItem) const
{
    if (gridItem.isOutOfFlowPositioned() && !hasStaticPositionForGridItem(gridItem, Style::GridTrackSizingDirection::Rows))
        return GridAxisPosition::GridAxisStart;

    bool hasSameDirection = isHorizontalWritingMode()
        ? writingMode().isBlockTopToBottom() == gridItem.writingMode().isAnyTopToBottom()
        : writingMode().isBlockLeftToRight() == gridItem.writingMode().isAnyLeftToRight();

    switch (const auto gridItemAlignSelf = alignSelfForGridItem(gridItem).position()) {
    case ItemPosition::SelfStart:
        // self-start is based on the grid item's block-flow direction.
        return hasSameDirection ? GridAxisPosition::GridAxisStart : GridAxisPosition::GridAxisEnd;
    case ItemPosition::SelfEnd:
        // self-end is based on the grid item's block-flow direction.
        return hasSameDirection ? GridAxisPosition::GridAxisEnd : GridAxisPosition::GridAxisStart;
    case ItemPosition::Left:
        // Aligns the alignment subject to be flush with the alignment container's 'line-left' edge.
        // The alignment axis (column axis) is always orthogonal to the inline axis, hence this value behaves as 'start'.
        return GridAxisPosition::GridAxisStart;
    case ItemPosition::Right:
        // Aligns the alignment subject to be flush with the alignment container's 'line-right' edge.
        // The alignment axis (column axis) is always orthogonal to the inline axis, hence this value behaves as 'start'.
        return GridAxisPosition::GridAxisStart;
    case ItemPosition::Center:
    case ItemPosition::AnchorCenter:
        return GridAxisPosition::GridAxisCenter;
    case ItemPosition::FlexStart: // Only used in flex layout, otherwise equivalent to 'start'.
        // Aligns the alignment subject to be flush with the alignment container's 'start' edge (block-start) in the column axis.
    case ItemPosition::Start:
        return GridAxisPosition::GridAxisStart;
    case ItemPosition::FlexEnd: // Only used in flex layout, otherwise equivalent to 'end'.
        // Aligns the alignment subject to be flush with the alignment container's 'end' edge (block-end) in the column axis.
    case ItemPosition::End:
        return GridAxisPosition::GridAxisEnd;
    case ItemPosition::Stretch:
        return GridAxisPosition::GridAxisStart;
    case ItemPosition::Baseline:
    case ItemPosition::LastBaseline: {
        auto fallbackAlignment = [&] {
            if (gridItemAlignSelf == ItemPosition::Baseline)
                return hasSameDirection ? GridAxisPosition::GridAxisStart : GridAxisPosition::GridAxisEnd;
            return hasSameDirection ? GridAxisPosition::GridAxisEnd : GridAxisPosition::GridAxisStart;
        };
        if (GridLayoutFunctions::isOrthogonalGridItem(*this, gridItem))
            return gridItemAlignSelf == ItemPosition::Baseline ? GridAxisPosition::GridAxisStart : GridAxisPosition::GridAxisEnd;
        return fallbackAlignment();
    }
    case ItemPosition::Legacy:
    case ItemPosition::Auto:
    case ItemPosition::Normal:
        break;
    }

    ASSERT_NOT_REACHED();
    return GridAxisPosition::GridAxisStart;
}

GridAxisPosition RenderGrid::rowAxisPositionForGridItem(const RenderBox& gridItem) const
{
    if (gridItem.isOutOfFlowPositioned() && !hasStaticPositionForGridItem(gridItem, Style::GridTrackSizingDirection::Columns))
        return GridAxisPosition::GridAxisStart;

    bool hasSameDirection = isHorizontalWritingMode()
        ? writingMode().isInlineLeftToRight() == gridItem.writingMode().isAnyLeftToRight()
        : writingMode().isInlineTopToBottom() == gridItem.writingMode().isAnyTopToBottom();

    switch (justifySelfForGridItem(gridItem).position()) {
    case ItemPosition::SelfStart:
        // self-start is based on the grid item's inline-flow direction.
        return hasSameDirection ? GridAxisPosition::GridAxisStart : GridAxisPosition::GridAxisEnd;
    case ItemPosition::SelfEnd:
        // self-end is based on the grid item's inline-flow direction.
        return hasSameDirection ? GridAxisPosition::GridAxisEnd : GridAxisPosition::GridAxisStart;
    case ItemPosition::Left:
        // Aligns the alignment subject to be flush with the alignment container's 'line-left' edge.
        // We want the physical 'left' side, so we have to take account, container's inline-flow direction.
        return writingMode().isBidiLTR() ? GridAxisPosition::GridAxisStart : GridAxisPosition::GridAxisEnd;
    case ItemPosition::Right:
        // Aligns the alignment subject to be flush with the alignment container's 'line-right' edge.
        // We want the physical 'right' side, so we have to take account, container's inline-flow direction.
        return writingMode().isBidiLTR() ? GridAxisPosition::GridAxisEnd : GridAxisPosition::GridAxisStart;
    case ItemPosition::Center:
    case ItemPosition::AnchorCenter:
        return GridAxisPosition::GridAxisCenter;
    case ItemPosition::FlexStart: // Only used in flex layout, otherwise equivalent to 'start'.
        // Aligns the alignment subject to be flush with the alignment container's 'start' edge (inline-start) in the row axis.
    case ItemPosition::Start:
        return GridAxisPosition::GridAxisStart;
    case ItemPosition::FlexEnd: // Only used in flex layout, otherwise equivalent to 'end'.
        // Aligns the alignment subject to be flush with the alignment container's 'end' edge (inline-end) in the row axis.
    case ItemPosition::End:
        return GridAxisPosition::GridAxisEnd;
    case ItemPosition::Stretch:
        return GridAxisPosition::GridAxisStart;
    case ItemPosition::Baseline:
    case ItemPosition::LastBaseline:
        // FIXME: Implement the previous values. For now, we always 'start' align the grid item.
        return GridAxisPosition::GridAxisStart;
    case ItemPosition::Legacy:
    case ItemPosition::Auto:
    case ItemPosition::Normal:
        break;
    }

    ASSERT_NOT_REACHED();
    return GridAxisPosition::GridAxisStart;
}

LayoutUnit RenderGrid::columnAxisOffsetForGridItem(const RenderBox& gridItem) const
{
    auto [startOfRow, endOfRow] = gridAreaPositionForGridItem(gridItem, Style::GridTrackSizingDirection::Rows);
    LayoutUnit startPosition = startOfRow + marginBeforeForChild(gridItem);
    LayoutUnit columnAxisGridItemSize = GridLayoutFunctions::isOrthogonalGridItem(*this, gridItem) ? gridItem.logicalWidth() + gridItem.marginLogicalWidth() : gridItem.logicalHeight() + gridItem.marginLogicalHeight();
    LayoutUnit masonryOffset = areMasonryRows() ? m_masonryLayout.offsetForGridItem(gridItem) : 0_lu;
    auto overflow = alignSelfForGridItem(gridItem).overflow();
        LayoutUnit offsetFromStartPosition = computeOverflowAlignmentOffset(overflow, endOfRow - startOfRow, columnAxisGridItemSize);
    if (hasAutoMarginsInColumnAxis(gridItem))
        return startPosition;
    GridAxisPosition axisPosition = columnAxisPositionForGridItem(gridItem);
    switch (axisPosition) {
    case GridAxisPosition::GridAxisStart:
        return startPosition + columnAxisBaselineOffsetForGridItem(gridItem) + masonryOffset;
    case GridAxisPosition::GridAxisEnd:
        return (startPosition + offsetFromStartPosition) - columnAxisBaselineOffsetForGridItem(gridItem);
    case GridAxisPosition::GridAxisCenter:
        return startPosition + (offsetFromStartPosition / 2);
    }

    ASSERT_NOT_REACHED();
    return 0;
}

LayoutUnit RenderGrid::rowAxisOffsetForGridItem(const RenderBox& gridItem) const
{
    auto [startOfColumn, endOfColumn] = gridAreaPositionForGridItem(gridItem, Style::GridTrackSizingDirection::Columns);
    LayoutUnit startPosition = startOfColumn + marginStartForChild(gridItem);
    LayoutUnit masonryOffset = areMasonryColumns() ? m_masonryLayout.offsetForGridItem(gridItem) : 0_lu;
    if (hasAutoMarginsInRowAxis(gridItem))
        return startPosition;
    GridAxisPosition axisPosition = rowAxisPositionForGridItem(gridItem);
    switch (axisPosition) {
    case GridAxisPosition::GridAxisStart:
        return startPosition + rowAxisBaselineOffsetForGridItem(gridItem) + masonryOffset;
    case GridAxisPosition::GridAxisEnd:
    case GridAxisPosition::GridAxisCenter: {
        LayoutUnit rowAxisGridItemSize = GridLayoutFunctions::isOrthogonalGridItem(*this, gridItem) ? gridItem.logicalHeight() + gridItem.marginLogicalHeight() : gridItem.logicalWidth() + gridItem.marginLogicalWidth();
        auto overflow = justifySelfForGridItem(gridItem).overflow();
        LayoutUnit offsetFromStartPosition = computeOverflowAlignmentOffset(overflow, endOfColumn - startOfColumn, rowAxisGridItemSize);
        return startPosition + (axisPosition == GridAxisPosition::GridAxisEnd ? offsetFromStartPosition : offsetFromStartPosition / 2);
    }
    }

    ASSERT_NOT_REACHED();
    return 0;
}

bool RenderGrid::isSubgrid() const
{
    return isSubgrid(Style::GridTrackSizingDirection::Rows) || isSubgrid(Style::GridTrackSizingDirection::Columns);
}

bool RenderGrid::isSubgrid(Style::GridTrackSizingDirection direction) const
{
    // If the grid container is forced to establish an independent formatting
    // context (like contain layout, or position:absolute), then the used value
    // of grid-template-rows/columns is 'none' and the container is not a subgrid.
    // https://drafts.csswg.org/css-grid-2/#subgrid-listing
    if (establishesIndependentFormattingContextIgnoringDisplayType(style()))
        return false;
    if (!style().gridTemplateList(direction).subgrid)
        return false;
    auto* renderGrid = dynamicDowncast<RenderGrid>(parent());
    if (!renderGrid)
        return false;
    return !renderGrid->isMasonry(direction);
}

bool RenderGrid::isSubgridInParentDirection(Style::GridTrackSizingDirection parentDirection) const
{
    auto* renderGrid = dynamicDowncast<RenderGrid>(parent());
    if (!renderGrid)
        return false;
    auto direction = GridLayoutFunctions::flowAwareDirectionForGridItem(*renderGrid, *this, parentDirection);
    return isSubgrid(direction);
}

bool RenderGrid::isSubgridOf(Style::GridTrackSizingDirection direction, const RenderGrid& ancestor) const
{
    if (!isSubgrid(direction))
        return false;
    if (parent() == &ancestor)
        return true;

    auto& parentGrid = *downcast<RenderGrid>(parent());
    auto parentDirection = GridLayoutFunctions::flowAwareDirectionForParent(parentGrid, *this, direction);
    return parentGrid.isSubgridOf(parentDirection, ancestor);
}

const Grid& RenderGrid::currentGrid() const
{
    return m_grid.m_currentGrid;
}

Grid& RenderGrid::currentGrid()
{
    return m_grid.m_currentGrid;
}

LayoutUnit RenderGrid::gridAreaBreadthForOutOfFlowGridItem(const RenderBox& gridItem, Style::GridTrackSizingDirection direction)
{
    ASSERT(gridItem.isOutOfFlowPositioned());
    bool isRowAxis = direction == Style::GridTrackSizingDirection::Columns;
    int lastLine = numTracks(direction);

    int startLine, endLine;
    bool startIsAuto, endIsAuto;
    if (!computeGridPositionsForOutOfFlowGridItem(gridItem, direction, startLine, startIsAuto, endLine, endIsAuto))
        return isRowAxis ? clientLogicalWidth() : clientLogicalHeight();

    if (startIsAuto && endIsAuto)
        return isRowAxis ? clientLogicalWidth() : clientLogicalHeight();

    LayoutUnit start;
    LayoutUnit end;
    auto& positions = this->positions(direction);
    auto& outOfFlowItemLine = outOfFlowItem(direction);
    LayoutUnit borderEdge = isRowAxis ? borderStart() : borderBefore();
    if (startIsAuto)
        start = borderEdge;
    else {
        outOfFlowItemLine.set(gridItem, startLine);
        start = positions[startLine];
    }
    if (endIsAuto)
        end = ((direction == Style::GridTrackSizingDirection::Rows) ? clientLogicalHeight() : clientLogicalWidth()) + borderEdge;
    else {
        end = positions[endLine];
        // These vectors store line positions including gaps, but we shouldn't consider them for the edges of the grid.
        std::optional<LayoutUnit> availableSizeForGutters = availableSpaceForGutters(direction);
        if (endLine > 0 && endLine < lastLine) {
            ASSERT(!currentGrid().needsItemsPlacement());
            end -= guttersSize(direction, endLine - 1, 2, availableSizeForGutters);
            end -= isRowAxis ? m_offsetBetweenColumns.distributionOffset : m_offsetBetweenRows.distributionOffset;
        }
    }
    return std::max(end - start, 0_lu);
}

LayoutUnit RenderGrid::logicalOffsetForOutOfFlowGridItem(const RenderBox& gridItem, Style::GridTrackSizingDirection direction, LayoutUnit trackBreadth) const
{
    ASSERT(gridItem.isOutOfFlowPositioned());
    if (hasStaticPositionForGridItem(gridItem, direction))
        return 0_lu;

    bool isRowAxis = direction == Style::GridTrackSizingDirection::Columns;
    bool isFlowAwareRowAxis = GridLayoutFunctions::flowAwareDirectionForGridItem(*this, gridItem, direction) == Style::GridTrackSizingDirection::Columns;
    LayoutUnit gridItemPosition = isFlowAwareRowAxis ? gridItem.logicalLeft() : gridItem.logicalTop();
    LayoutUnit gridBorder = isRowAxis ? borderLogicalLeft() : borderBefore();
    LayoutUnit gridItemMargin = isRowAxis ? gridItem.marginLogicalLeft(writingMode()) : gridItem.marginBefore(writingMode());
    LayoutUnit offset = gridItemPosition - gridBorder - gridItemMargin;
    if (!isRowAxis || writingMode().isLogicalLeftInlineStart())
        return offset;

    LayoutUnit gridItemBreadth = isFlowAwareRowAxis ? gridItem.logicalWidth() + gridItem.marginLogicalWidth() : gridItem.logicalHeight() + gridItem.marginLogicalHeight();
    return trackBreadth - offset - gridItemBreadth;
}

std::optional<LayoutRange> RenderGrid::gridAreaRowRangeForOutOfFlow(const RenderBox& gridItem) const
{
    ASSERT(gridItem.isOutOfFlowPositioned());
    auto areaSize = GridLayoutFunctions::overridingContainingBlockContentSizeForGridItem(gridItem, Style::GridTrackSizingDirection::Rows);
    if (!areaSize)
        return std::nullopt;
    LayoutRange range(borderBefore(), areaSize->value());
    if (auto line = m_outOfFlowItemRow.get(gridItem))
        range.moveTo(m_rowPositions[line.value()]);
    return range;
}

std::optional<LayoutRange> RenderGrid::gridAreaColumnRangeForOutOfFlow(const RenderBox& gridItem) const
{
    ASSERT(gridItem.isOutOfFlowPositioned());
    auto areaSize = GridLayoutFunctions::overridingContainingBlockContentSizeForGridItem(gridItem, Style::GridTrackSizingDirection::Columns);
    if (!areaSize)
        return std::nullopt;
    LayoutRange range(borderStart(), areaSize->value());
    if (auto line = m_outOfFlowItemColumn.get(gridItem))
        range.moveTo(m_columnPositions[line.value()]);
    return range;
}

std::pair<LayoutUnit, LayoutUnit> RenderGrid::gridAreaPositionForOutOfFlowGridItem(const RenderBox& gridItem, Style::GridTrackSizingDirection direction) const
{
    std::optional<LayoutRange> range = direction == Style::GridTrackSizingDirection::Columns
        ? gridAreaColumnRangeForOutOfFlow(gridItem)
        : gridAreaRowRangeForOutOfFlow(gridItem);
    ASSERT(range);
    range->moveBy(logicalOffsetForOutOfFlowGridItem(gridItem, direction, range->size()));
    return { range->min(), range->max() };
}

std::pair<LayoutUnit, LayoutUnit> RenderGrid::gridAreaPositionForInFlowGridItem(const RenderBox& gridItem, Style::GridTrackSizingDirection direction) const
{
    ASSERT(!gridItem.isOutOfFlowPositioned());
    const auto& span = currentGrid().gridItemSpan(gridItem, direction);
    const auto& positions = this->positions(direction);
    auto start = positions[span.startLine()];
    auto end = positions[span.endLine()];
    // The 'positions' vector includes distribution offset (because of content
    // alignment) and gutters, so we need to subtract them to get the actual
    // end position for a given track (this does not have to be done for the
    // last track as there are no more positions' elements after it, nor for
    // collapsed tracks).
    if (span.endLine() < positions.size() - 1
        && !(currentGrid().hasAutoRepeatEmptyTracks(direction)
        && currentGrid().isEmptyAutoRepeatTrack(direction, span.endLine()))) {
        end -= gridGap(direction) + gridItemOffset(direction);
    }
    return { start, end };
}

std::pair<LayoutUnit, LayoutUnit> RenderGrid::gridAreaPositionForGridItem(const RenderBox& gridItem, Style::GridTrackSizingDirection direction) const
{
    if (gridItem.isOutOfFlowPositioned())
        return gridAreaPositionForOutOfFlowGridItem(gridItem, direction);
    return gridAreaPositionForInFlowGridItem(gridItem, direction);
}

std::pair<OverflowAlignment, ContentPosition> static resolveContentDistributionFallback(ContentDistribution distribution)
{
    switch (distribution) {
    case ContentDistribution::SpaceBetween:
        return { OverflowAlignment::Default, ContentPosition::Start };
    case ContentDistribution::SpaceAround:
        return { OverflowAlignment::Safe, ContentPosition::Center };
    case ContentDistribution::SpaceEvenly:
        return { OverflowAlignment::Safe, ContentPosition::Center };
    case ContentDistribution::Stretch:
        return { OverflowAlignment::Default, ContentPosition::Start };
    case ContentDistribution::Default:
        return { OverflowAlignment::Default, ContentPosition::Normal };
    }

    ASSERT_NOT_REACHED();
    return { OverflowAlignment::Default, ContentPosition::Normal };
}

StyleContentAlignmentData RenderGrid::contentAlignment(Style::GridTrackSizingDirection direction) const
{
    return direction == Style::GridTrackSizingDirection::Columns ? style().resolvedJustifyContent(contentAlignmentNormalBehaviorGrid()) : style().resolvedAlignContent(contentAlignmentNormalBehaviorGrid());
}

ContentAlignmentData RenderGrid::computeContentPositionAndDistributionOffset(Style::GridTrackSizingDirection direction, const LayoutUnit& availableFreeSpace, unsigned numberOfGridTracks) const
{
    if (isSubgrid(direction))
        return { };

    auto contentAlignmentData = contentAlignment(direction);
    auto contentAlignmentDistribution = contentAlignmentData.distribution();

    // Apply <content-distribution> and return, or continue to fallback positioning if we can't distribute.
    if (contentAlignmentDistribution != ContentDistribution::Default) {
        if (availableFreeSpace > 0) {
            switch (contentAlignmentDistribution) {
    case ContentDistribution::SpaceBetween:
        if (numberOfGridTracks < 2)
        break;
                return { 0_lu, availableFreeSpace / (numberOfGridTracks - 1) };
            case ContentDistribution::SpaceAround: {
        if (numberOfGridTracks < 1)
        break;
                auto spaceBetweenTracks = availableFreeSpace / numberOfGridTracks;
                return { spaceBetweenTracks / 2, spaceBetweenTracks };
            }
            case ContentDistribution::SpaceEvenly: {
                auto spaceEvenlyDistribution = availableFreeSpace / (numberOfGridTracks + 1);
                return { spaceEvenlyDistribution, spaceEvenlyDistribution };
            }
            case ContentDistribution::Stretch:
                break;
    default:
        ASSERT_NOT_REACHED();
    }
        }
    }

    auto [fallbackOverflow, fallbackContentPosition] = resolveContentDistributionFallback(contentAlignmentDistribution);
    auto contentAlignmentOverflow = contentAlignmentData.overflow();

    // Apply alignment safety.
    if (availableFreeSpace <= 0 && (contentAlignmentOverflow == OverflowAlignment::Safe || fallbackOverflow == OverflowAlignment::Safe))
        return { };

    auto usedContentPosition = contentAlignmentDistribution == ContentDistribution::Default ? contentAlignmentData.position() : fallbackContentPosition;
    // Apply <content-position> / fallback positioning.
    switch (usedContentPosition) {
    case ContentPosition::Left:
        ASSERT(direction == Style::GridTrackSizingDirection::Columns);
        if (!writingMode().isBidiLTR())
            return { availableFreeSpace, 0_lu };
        return { };
    case ContentPosition::Right:
        ASSERT(direction == Style::GridTrackSizingDirection::Columns);
        if (writingMode().isBidiLTR())
            return { availableFreeSpace, 0_lu };
        return { };
    case ContentPosition::Center:
        return { availableFreeSpace / 2, 0_lu };
    case ContentPosition::FlexEnd: // Only used in flex layout, for other layout, it's equivalent to 'end'.
    case ContentPosition::End:
        return { availableFreeSpace, 0_lu };
    case ContentPosition::FlexStart: // Only used in flex layout, for other layout, it's equivalent to 'start'.
    case ContentPosition::Start:
    case ContentPosition::Baseline:
    case ContentPosition::LastBaseline:
        // FIXME: Implement the baseline values. For now, we always 'start' align.
        // http://webkit.org/b/145566
        return { };
    case ContentPosition::Normal:
    default:
        ASSERT_NOT_REACHED();
        return { };
    }
}

LayoutOptionalOutsets RenderGrid::allowedLayoutOverflow() const
{
    LayoutOptionalOutsets allowance = RenderBox::allowedLayoutOverflow();
    if (m_offsetBetweenColumns.positionOffset < 0)
        allowance.setStart(-m_offsetBetweenColumns.positionOffset, writingMode());

    if (m_offsetBetweenRows.positionOffset < 0) {
        if (isHorizontalWritingMode())
            allowance.setTop(-m_offsetBetweenRows.positionOffset);
        else
            allowance.setLeft(-m_offsetBetweenRows.positionOffset);
    }

    return allowance;
}

LayoutUnit RenderGrid::translateRTLCoordinate(LayoutUnit coordinate) const
{
    LayoutUnit width = borderLogicalLeft() + borderLogicalRight() + clientLogicalWidth();

#if !PLATFORM(IOS_FAMILY)
    // FIXME: Ideally scrollbarLogicalWidth() should return zero in iOS so we don't need this
    // (see bug https://webkit.org/b/191857).
    // If we are in horizontal writing mode and RTL direction the scrollbar is painted on the left,
    // so we need to take into account when computing the position of the columns.
    if (writingMode().isHorizontal())
        width += scrollbarLogicalWidth();
#endif

    return width - coordinate;
}

// FIXME: SetLogicalPositionForGridItem has only one caller, consider its refactoring in the future.
void RenderGrid::setLogicalPositionForGridItem(RenderBox& gridItem) const
{
    // "In the positioning phase [...] calculations are performed according to the writing mode of the containing block of the box establishing the
    // orthogonal flow." However, 'setLogicalLocation' will only take into account the grid item's writing-mode, so the position may need to be transposed.
    LayoutPoint gridItemLocation(logicalOffsetForGridItem(gridItem, Style::GridTrackSizingDirection::Columns), logicalOffsetForGridItem(gridItem, Style::GridTrackSizingDirection::Rows));
    gridItem.setLogicalLocation(GridLayoutFunctions::isOrthogonalGridItem(*this, gridItem) ? gridItemLocation.transposedPoint() : gridItemLocation);
}

LayoutUnit RenderGrid::logicalOffsetForGridItem(const RenderBox& gridItem, Style::GridTrackSizingDirection direction) const
{
    if (direction == Style::GridTrackSizingDirection::Rows)
        return columnAxisOffsetForGridItem(gridItem);
    LayoutUnit rowAxisOffset = rowAxisOffsetForGridItem(gridItem);
    // We stored m_columnPositions's data ignoring the direction, hence we might need now
    // to translate positions from RTL to LTR, as it's more convenient for painting.
    if (writingMode().isInlineFlipped())
        rowAxisOffset = translateRTLCoordinate(rowAxisOffset) - (GridLayoutFunctions::isOrthogonalGridItem(*this, gridItem) ? gridItem.logicalHeight()  : gridItem.logicalWidth());
    return rowAxisOffset;
}

unsigned RenderGrid::nonCollapsedTracks(Style::GridTrackSizingDirection direction) const
{
    auto& tracks = m_trackSizingAlgorithm.tracks(direction);
    size_t numberOfTracks = tracks.size();
    bool hasCollapsedTracks = currentGrid().hasAutoRepeatEmptyTracks(direction);
    size_t numberOfCollapsedTracks = hasCollapsedTracks ? currentGrid().autoRepeatEmptyTracks(direction)->size() : 0;
    return numberOfTracks - numberOfCollapsedTracks;
}

unsigned RenderGrid::numTracks(Style::GridTrackSizingDirection direction) const
{
    // Due to limitations in our internal representation, we cannot know the number of columns from
    // currentGrid *if* there is no row (because currentGrid would be empty). That's why in that case we need
    // to get it from the style. Note that we know for sure that there aren't any implicit tracks,
    // because not having rows implies that there are no "normal" grid items (out-of-flow grid items are
    // not stored in currentGrid).
    ASSERT(!currentGrid().needsItemsPlacement());
    if (direction == Style::GridTrackSizingDirection::Rows)
        return currentGrid().numTracks(Style::GridTrackSizingDirection::Rows);

    // FIXME: This still requires knowledge about currentGrid internals.
    return currentGrid().numTracks(Style::GridTrackSizingDirection::Rows)
        ? currentGrid().numTracks(Style::GridTrackSizingDirection::Columns)
        : Style::GridPositionsResolver::explicitGridCount(*this, Style::GridTrackSizingDirection::Columns);
}

void RenderGrid::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& forChild, bool usePrintRect)
{
    ASSERT(!currentGrid().needsItemsPlacement());
    for (RenderBox* gridItem = currentGrid().orderIterator().first(); gridItem; gridItem = currentGrid().orderIterator().next())
        paintChild(*gridItem, paintInfo, paintOffset, forChild, usePrintRect, PaintAsInlineBlock);
}

bool RenderGrid::hitTestChildren(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& adjustedLocation, HitTestAction hitTestAction)
{
    if (hitTestAction != HitTestForeground)
        return false;

    LayoutPoint scrolledOffset = hasNonVisibleOverflow() ? adjustedLocation - toLayoutSize(scrollPosition()) : adjustedLocation;

    Vector<RenderBox*> reversedOrderIteratorForHitTesting;
    for (auto* gridItem = currentGrid().orderIterator().first(); gridItem; gridItem = currentGrid().orderIterator().next()) {
        if (gridItem->isOutOfFlowPositioned())
            continue;
        reversedOrderIteratorForHitTesting.append(gridItem);
    }
    reversedOrderIteratorForHitTesting.reverse();

    for (auto* gridItem : reversedOrderIteratorForHitTesting) {
        if (gridItem->hasSelfPaintingLayer())
            continue;
        auto location = flipForWritingModeForChild(*gridItem, scrolledOffset);
        if (gridItem->hitTest(request, result, locationInContainer, location)) {
            updateHitTestResult(result, flipForWritingMode(toLayoutPoint(locationInContainer.point() - adjustedLocation)));
            return true;
        }
    }

    return false;
}

ASCIILiteral RenderGrid::renderName() const
{
    if (isFloating())
        return "RenderGrid (floating)"_s;
    if (isOutOfFlowPositioned())
        return "RenderGrid (positioned)"_s;
    if (isAnonymous())
        return "RenderGrid (generated)"_s;
    if (isRelativelyPositioned())
        return "RenderGrid (relative positioned)"_s;
    return "RenderGrid"_s;
}

bool RenderGrid::hasAutoSizeInColumnAxis(const RenderBox& gridItem) const
{
    if (gridItem.style().hasAspectRatio()) {
        // FIXME: should align-items + align-self: auto/justify-items + justify-self: auto be taken into account?
        if (isHorizontalWritingMode() == gridItem.isHorizontalWritingMode() && gridItem.style().alignSelf().position() != ItemPosition::Stretch) {
            // A non-auto inline size means the same for block size (column axis size) because of the aspect ratio.
            if (!gridItem.style().logicalWidth().isAuto())
                return false;
        } else if (gridItem.style().justifySelf().position() != ItemPosition::Stretch) {
            auto& logicalHeight = gridItem.style().logicalHeight();
            if (logicalHeight.isFixed() || (logicalHeight.isPercentOrCalculated() && gridItem.percentageLogicalHeightIsResolvable()))
                return false;
        }
    }
    return isHorizontalWritingMode() ? gridItem.style().height().isAuto() : gridItem.style().width().isAuto();
}

bool RenderGrid::hasAutoSizeInRowAxis(const RenderBox& gridItem) const
{
    if (gridItem.style().hasAspectRatio()) {
        // FIXME: should align-items + align-self: auto/justify-items + justify-self: auto be taken into account?
        if (isHorizontalWritingMode() == gridItem.isHorizontalWritingMode() && gridItem.style().justifySelf().position() != ItemPosition::Stretch) {
            // A non-auto block size means the same for inline size (row axis size) because of the aspect ratio.
            auto& logicalHeight = gridItem.style().logicalHeight();
            if (logicalHeight.isFixed() || (logicalHeight.isPercentOrCalculated() && gridItem.percentageLogicalHeightIsResolvable()))
                return false;
        } else if (gridItem.style().alignSelf().position() != ItemPosition::Stretch) {
            if (!gridItem.style().logicalWidth().isAuto())
                return false;
        }
    }
    return isHorizontalWritingMode() ? gridItem.style().width().isAuto() : gridItem.style().height().isAuto();
}

bool RenderGrid::computeGridPositionsForOutOfFlowGridItem(const RenderBox& gridItem, Style::GridTrackSizingDirection direction, int& startLine, bool& startIsAuto, int& endLine, bool& endIsAuto) const
{
    ASSERT(gridItem.isOutOfFlowPositioned());
    int lastLine = numTracks(direction);
    auto span = Style::GridPositionsResolver::resolveGridPositionsFromStyle(*this, gridItem, direction);
    if (span.isIndefinite())
        return false;

    unsigned explicitStart = currentGrid().explicitGridStart(direction);
    startLine = span.untranslatedStartLine() + explicitStart;
    endLine = span.untranslatedEndLine() + explicitStart;
    startIsAuto = gridItem.style().gridItemStart(direction).isAuto() || startLine < 0 || startLine > lastLine;
    endIsAuto = gridItem.style().gridItemEnd(direction).isAuto() || endLine < 0 || endLine > lastLine;
    return true;
}

GridSpan RenderGrid::gridSpanForOutOfFlowGridItem(const RenderBox& gridItem, Style::GridTrackSizingDirection direction) const
{
    int lastLine = numTracks(direction);
    int startLine, endLine;
    bool startIsAuto, endIsAuto;
    if (!computeGridPositionsForOutOfFlowGridItem(gridItem, direction, startLine, startIsAuto, endLine, endIsAuto))
        return GridSpan::translatedDefiniteGridSpan(0, lastLine);
    return GridSpan::translatedDefiniteGridSpan(startIsAuto ? 0 : startLine, endIsAuto ? lastLine : endLine);
}

GridSpan RenderGrid::gridSpanForGridItem(const RenderBox& gridItem, Style::GridTrackSizingDirection direction) const
{
    RenderGrid* renderGrid = downcast<RenderGrid>(gridItem.parent());
    // |direction| is specified relative to this grid, switch it if |gridItem|'s direct parent grid
    // is using a different writing mode.
    direction = GridLayoutFunctions::flowAwareDirectionForGridItem(*this, *renderGrid, direction);
    GridSpan span = gridItem.isOutOfFlowPositioned() ? renderGrid->gridSpanForOutOfFlowGridItem(gridItem, direction) : renderGrid->currentGrid().gridItemSpan(gridItem, direction);

    while (renderGrid != this) {
        RenderGrid* parent = downcast<RenderGrid>(renderGrid->parent());

        bool isSubgrid = renderGrid->isSubgrid(direction);

        direction = GridLayoutFunctions::flowAwareDirectionForGridItem(*parent, *renderGrid, direction);

        GridSpan parentSpan = renderGrid->isOutOfFlowPositioned() ? parent->gridSpanForOutOfFlowGridItem(*renderGrid, direction) :  parent->currentGrid().gridItemSpan(*renderGrid, direction);
        if (isSubgrid)
            span.translateTo(parentSpan, GridLayoutFunctions::isSubgridReversedDirection(*parent, direction, *renderGrid));
        else
            span = parentSpan;
        renderGrid = parent;
    }
    return span;
}

RenderGrid::GridWrapper::GridWrapper(RenderGrid& renderGrid)
    : m_layoutGrid(renderGrid)
{ }

void RenderGrid::GridWrapper::resetCurrentGrid() const
{
    m_currentGrid = std::ref(const_cast<Grid&>(m_layoutGrid));
}

void RenderGrid::computeOverflow(LayoutUnit oldClientAfterEdge, bool recomputeFloats)
{
    RenderBlock::computeOverflow(oldClientAfterEdge, recomputeFloats);

    if (!hasPotentiallyScrollableOverflow() || isMasonry() || isSubgridRows() || isSubgridColumns())
        return;

    // FIXME: We should handle RTL and other writing modes also.
    if (writingMode().isBidiLTR() && isHorizontalWritingMode()) {
        auto gridAreaSize = LayoutSize { m_columnPositions.last(), m_rowPositions.last() };
        gridAreaSize += { paddingEnd(), paddingAfter() };
        addLayoutOverflow({ { }, gridAreaSize });
    }
}

void RenderGrid::updateIntrinsicLogicalHeightsForRowSizingFirstPassCacheAvailability()
{
    auto canCreateIntrinsicLogicalHeightsCacheForRowSizingFirstPass = this->canCreateIntrinsicLogicalHeightsForRowSizingFirstPassCache();

    if (canCreateIntrinsicLogicalHeightsCacheForRowSizingFirstPass && m_intrinsicLogicalHeightsForRowSizingFirstPass) {
        for (auto& gridItem : childrenOfType<RenderBox>(*this)) {
            if (gridItem.needsLayout())
                m_intrinsicLogicalHeightsForRowSizingFirstPass->invalidateSizeForItem(gridItem);
        }
    } else if (canCreateIntrinsicLogicalHeightsCacheForRowSizingFirstPass)
        m_intrinsicLogicalHeightsForRowSizingFirstPass.emplace();
    else
        m_intrinsicLogicalHeightsForRowSizingFirstPass.reset();
}

std::optional<GridItemSizeCache>& RenderGrid::intrinsicLogicalHeightsForRowSizingFirstPass() const
{
    ASSERT_IMPLIES(m_intrinsicLogicalHeightsForRowSizingFirstPass, canCreateIntrinsicLogicalHeightsForRowSizingFirstPassCache());
    return m_intrinsicLogicalHeightsForRowSizingFirstPass;
}

bool RenderGrid::canCreateIntrinsicLogicalHeightsForRowSizingFirstPassCache() const
{
    if (isMasonry())
        return false;

    if (isSubgridRows())
        return false;

    if (enclosingFragmentedFlow())
        return false;

    for (auto& gridItem : childrenOfType<RenderBox>(*this)) {
        if (auto* renderGrid = dynamicDowncast<RenderGrid>(gridItem)) {
            if (renderGrid->isSubgridRows())
                return false;

            if (renderGrid->isSubgridColumns() && GridLayoutFunctions::isOrthogonalGridItem(*this, *renderGrid))
                return false;
        }

        if (isBaselineAlignmentForGridItem(gridItem))
            return false;
    }
    return true;
}

void GridItemSizeCache::setSizeForGridItem(const RenderBox& gridItem, LayoutUnit size)
{
    m_sizes.set(gridItem, size);
}

std::optional<LayoutUnit> GridItemSizeCache::sizeForItem(const RenderBox& gridItem) const
{
    return m_sizes.get(gridItem);
}

void GridItemSizeCache::invalidateSizeForItem(const RenderBox& gridItem)
{
    m_sizes.remove(gridItem);
}

} // namespace WebCore
