/*
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
 * Copyright (C) 2003-2025 Apple Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#include "config.h"
#include "RenderDeprecatedFlexibleBox.h"

#include "FontCascade.h"
#include "InlineIteratorLineBox.h"
#include "LayoutIntegrationLineLayout.h"
#include "LayoutRepainter.h"
#include "RenderBlockFlowInlines.h"
#include "RenderBoxInlines.h"
#include "RenderBoxModelObjectInlines.h"
#include "RenderDescendantIterator.h"
#include "RenderElementStyleInlines.h"
#include "RenderElementInlines.h"
#include "RenderIterator.h"
#include "RenderLayer.h"
#include "RenderLayoutState.h"
#include "RenderObjectInlines.h"
#include "RenderView.h"
#include "StyleComputedStyle+GettersInlines.h"
#include "StyleComputedStyle+InitialInlines.h"
#include <ranges>
#include <wtf/Scope.h>
#include <wtf/StdLibExtras.h>
#include <wtf/TZoneMallocInlines.h>
#include <wtf/unicode/CharacterNames.h>

namespace WebCore {

WTF_MAKE_TZONE_ALLOCATED_IMPL(RenderDeprecatedFlexibleBox);

class FlexBoxIterator {
public:
    FlexBoxIterator(RenderDeprecatedFlexibleBox* parent)
        : m_box(parent)
        , m_largestOrdinal(1)
    {
        if (m_box->style().boxOrient() == BoxOrient::Horizontal && !m_box->style().writingMode().deprecatedIsLeftToRightDirection())
            m_forward = m_box->style().boxDirection() != BoxDirection::Normal;
        else
            m_forward = m_box->style().boxDirection() == BoxDirection::Normal;
        if (!m_forward) {
            // No choice, since we're going backwards, we have to find out the highest ordinal up front.
            RenderBox* child = m_box->firstChildBox();
            while (child) {
                if (child->style().boxOrdinalGroup() > m_largestOrdinal)
                    m_largestOrdinal = child->style().boxOrdinalGroup().value;
                child = child->nextSiblingBox();
            }
        }

        reset();
    }

    void NODELETE reset()
    {
        m_currentChild = nullptr;
        m_ordinalIteration = std::numeric_limits<unsigned>::max();
    }

    RenderBox* first()
    {
        reset();
        return next();
    }

    RenderBox* next()
    {
        do {
            if (!m_currentChild) {
                ++m_ordinalIteration;

                if (!m_ordinalIteration)
                    m_currentOrdinal = m_forward ? 1 : m_largestOrdinal;
                else {
                    if (m_ordinalIteration > m_ordinalValues.size())
                        return nullptr;

                    // Only copy+sort the values once per layout even if the iterator is reset.
                    if (static_cast<size_t>(m_ordinalValues.size()) != m_sortedOrdinalValues.size()) {
                        m_sortedOrdinalValues = copyToVector(m_ordinalValues);
                        std::ranges::sort(m_sortedOrdinalValues);
                    }
                    m_currentOrdinal = m_forward ? m_sortedOrdinalValues[m_ordinalIteration - 1] : m_sortedOrdinalValues[m_sortedOrdinalValues.size() - m_ordinalIteration];
                }

                m_currentChild = m_forward ? m_box->firstChildBox() : m_box->lastChildBox();
            } else
                m_currentChild = m_forward ? m_currentChild->nextSiblingBox() : m_currentChild->previousSiblingBox();

            if (m_currentChild && notFirstOrdinalValue())
                m_ordinalValues.add(m_currentChild->style().boxOrdinalGroup().value);
        } while (!m_currentChild || m_currentChild->isExcludedFromNormalLayout() || (!m_currentChild->isAnonymous()
                 && m_currentChild->style().boxOrdinalGroup() != m_currentOrdinal));
        return m_currentChild;
    }

private:
    bool NODELETE notFirstOrdinalValue()
    {
        unsigned int firstOrdinalValue = m_forward ? 1 : m_largestOrdinal;
        return m_currentOrdinal == firstOrdinalValue && m_currentChild->style().boxOrdinalGroup() != firstOrdinalValue;
    }

    RenderDeprecatedFlexibleBox* m_box;
    RenderBox* m_currentChild;
    bool m_forward;
    unsigned m_currentOrdinal;
    unsigned m_largestOrdinal;
    HashSet<unsigned> m_ordinalValues;
    Vector<unsigned> m_sortedOrdinalValues;
    unsigned m_ordinalIteration;
};

RenderDeprecatedFlexibleBox::RenderDeprecatedFlexibleBox(Element& element, Style::ComputedStyle&& style)
    : RenderBlock(RenderObject::Type::DeprecatedFlexibleBox, element, WTF::move(style), { })
{
    setChildrenInline(false); // All of our children must be block-level
    m_stretchingChildren = false;
}

RenderDeprecatedFlexibleBox::~RenderDeprecatedFlexibleBox() = default;

static LayoutUnit NODELETE marginWidthForChild(RenderBox* child)
{
    // A margin basically has three types: fixed, percentage, and auto (variable).
    // Auto and percentage margins simply become 0 when computing min/max width.
    // Fixed margins can be added in as is.
    const auto& zoomFactor = child->style().usedZoomForLength();
    LayoutUnit margin;
    if (auto fixedMarginLeft = child->style().marginLeft().tryFixed())
        margin += fixedMarginLeft->resolveZoom(zoomFactor);
    if (auto fixedMarginRight = child->style().marginRight().tryFixed())
        margin += fixedMarginRight->resolveZoom(zoomFactor);
    return margin;
}

static bool NODELETE childDoesNotAffectWidthOrFlexing(RenderObject* child)
{
    // Positioned children don't affect the min/max width.
    return child->isOutOfFlowPositioned();
}

static LayoutUnit NODELETE widthForChild(RenderBox* child)
{
    if (auto overridingLogicalWidth = child->overridingBorderBoxLogicalWidth())
        return *overridingLogicalWidth;
    return child->logicalWidth();
}

static LayoutUnit NODELETE heightForChild(RenderBox* child)
{
    if (auto overridingLogicalHeight = child->overridingBorderBoxLogicalHeight())
        return *overridingLogicalHeight;
    return child->logicalHeight();
}

static LayoutUnit contentWidthForChild(RenderBox* child)
{
    return std::max<LayoutUnit>(0, widthForChild(child) - child->borderAndPaddingLogicalWidth());
}

static LayoutUnit contentHeightForChild(RenderBox* child)
{
    return std::max<LayoutUnit>(0, heightForChild(child) - child->borderAndPaddingLogicalHeight());
}

static LayoutUnit mainAxisExtentForChild(RenderBox* child, bool isVerticalBox)
{
    bool useLogicalHeight = isVerticalBox == child->isHorizontalWritingMode();
    return useLogicalHeight ? heightForChild(child) : widthForChild(child);
}

static LayoutUnit mainAxisContentExtentForChild(RenderBox* child, bool isVerticalBox)
{
    bool useLogicalHeight = isVerticalBox == child->isHorizontalWritingMode();
    return useLogicalHeight ? contentHeightForChild(child) : contentWidthForChild(child);
}

static void setOverridingMainAxisExtent(RenderBox* child, bool isVerticalBox, LayoutUnit extent)
{
    bool useLogicalHeight = isVerticalBox == child->isHorizontalWritingMode();
    if (useLogicalHeight)
        child->setOverridingBorderBoxLogicalHeight(extent);
    else
        child->setOverridingBorderBoxLogicalWidth(extent);
}

void RenderDeprecatedFlexibleBox::styleWillChange(Style::Difference diff, const Style::ComputedStyle& newStyle)
{
    auto shouldClearLineClamp = [&] {
        auto* oldStyle = hasInitializedStyle() ? &style() : nullptr;
        if (!oldStyle || oldStyle->lineClamp().isNone())
            return false;
        if (newStyle.lineClamp().isNone())
            return true;
        return newStyle.boxOrient() == BoxOrient::Horizontal;
    };
    if (shouldClearLineClamp())
        clearLineClamp();
    RenderBlock::styleWillChange(diff, newStyle);
}

std::pair<LayoutUnit, LayoutUnit> RenderDeprecatedFlexibleBox::computeIntrinsicLogicalWidths() const
{
    auto minLogicalWidth = LayoutUnit { };
    auto maxLogicalWidth = LayoutUnit { };

    auto scrollbarWidth = intrinsicScrollbarLogicalWidthIncludingGutter();
    if (shouldApplySizeOrInlineSizeContainment()) {
        if (auto width = explicitIntrinsicInnerLogicalWidth()) {
            minLogicalWidth = width.value();
            maxLogicalWidth = width.value();
        }
        return { minLogicalWidth + scrollbarWidth, maxLogicalWidth + scrollbarWidth };
    }

    if (hasMultipleLines() || isVertical()) {
        for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
            if (childDoesNotAffectWidthOrFlexing(child))
                continue;

            LayoutUnit margin = marginWidthForChild(child);
            LayoutUnit width = child->minContentLogicalWidthContribution() + margin;
            minLogicalWidth = std::max(width, minLogicalWidth);

            width = child->maxContentLogicalWidthContribution() + margin;
            maxLogicalWidth = std::max(width, maxLogicalWidth);
        }
    } else {
        for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
            if (childDoesNotAffectWidthOrFlexing(child))
                continue;

            LayoutUnit margin = marginWidthForChild(child);
            minLogicalWidth += child->minContentLogicalWidthContribution() + margin;
            maxLogicalWidth += child->maxContentLogicalWidthContribution() + margin;
        }
    }

    maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth);
    return { minLogicalWidth + scrollbarWidth, maxLogicalWidth + scrollbarWidth };
}

void RenderDeprecatedFlexibleBox::computeIntrinsicLogicalWidthContributions()
{
    ASSERT(hasInvalidContentLogicalWidths());

    m_minContentLogicalWidthContribution = 0_lu;
    m_maxContentLogicalWidthContribution = 0_lu;
    if (auto fixedWidth = style().width().tryFixed(); fixedWidth && fixedWidth->isPositive()) {
        m_maxContentLogicalWidthContribution = adjustContentBoxLogicalWidthForBoxSizing(*fixedWidth);
        m_minContentLogicalWidthContribution = m_maxContentLogicalWidthContribution;
    } else
        std::tie(m_minContentLogicalWidthContribution, m_maxContentLogicalWidthContribution) = computeIntrinsicLogicalWidths();

    constrainIntrinsicLogicalWidthsByMinMax(m_minContentLogicalWidthContribution, m_maxContentLogicalWidthContribution);

    clearContentLogicalWidthsInvalidation();
}

// Use an inline capacity of 8, since flexbox containers usually have less than 8 children.
typedef Vector<LayoutRect, 8> ChildBorderBoxRects;
typedef Vector<LayoutSize, 8> ChildLayoutDeltas;

static void appendChildBorderBoxRects(RenderDeprecatedFlexibleBox* box, ChildBorderBoxRects& childBorderBoxRects)
{
    FlexBoxIterator iterator(box);
    for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
        if (!child->isOutOfFlowPositioned())
            childBorderBoxRects.append(child->borderBoxRectInContainer());
    }
}

static void appendChildLayoutDeltas(RenderDeprecatedFlexibleBox* box, ChildLayoutDeltas& childLayoutDeltas)
{
    FlexBoxIterator iterator(box);
    for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
        if (!child->isOutOfFlowPositioned())
            childLayoutDeltas.append(LayoutSize());
    }
}

static void repaintChildrenDuringLayoutIfMoved(RenderDeprecatedFlexibleBox* box, const ChildBorderBoxRects& oldChildRects)
{
    size_t childIndex = 0;
    FlexBoxIterator iterator(box);
    for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
        if (child->isOutOfFlowPositioned())
            continue;

        // If the child 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 child) anyway.
        if (!box->selfNeedsLayout() && child->checkForRepaintDuringLayout())
            child->repaintDuringLayoutIfMoved(oldChildRects[childIndex]);
        
        ++childIndex;
    }
    ASSERT(childIndex == oldChildRects.size());
}

bool RenderDeprecatedFlexibleBox::hasClampingAndNoFlexing() const
{
    if (isHorizontal())
        return false;
    auto* firstChildBox = this->firstChildBox();
    if (!firstChildBox || firstChildBox != lastChildBox() || firstChildBox->firstChild() != firstChildBox->lastChild() || !is<RenderText>(firstChildBox->firstChild()))
        return false;
    if (firstChildBox->style().hasOutOfFlowPosition())
        return false;

    auto& style = this->style();
    if (style.lineClamp().isNone() || style.lineClamp().isPercentage())
        return false;
    if (!style.logicalHeight().isAuto() || !firstChildBox->style().logicalHeight().isAuto())
        return false;
    if (style.overflowX() != Overflow::Hidden || style.overflowY() != Overflow::Hidden)
        return false;
    if (style.boxAlign() != Style::ComputedStyle::initialBoxAlign() || !style.writingMode().deprecatedIsLeftToRightDirection())
        return false;
    return true;
}

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

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

    if (hasClampingAndNoFlexing())
        return layoutSingleClampedFlexItem();

    LayoutRepainter repainter(*this);
    {
        LayoutStateMaintainer statePusher(*this, locationOffset(), isTransformed() || hasReflection() || writingMode().isBlockFlipped());

        resetLogicalHeightBeforeLayoutIfNeeded();
        preparePaginationBeforeBlockLayout(relayoutChildren);

        LayoutSize previousSize = borderBoxSize();

        updateLogicalWidth();
        updateLogicalHeight();

        if (previousSize != borderBoxSize()
            || (parent()->isRenderDeprecatedFlexibleBox() && parent()->style().boxOrient() == BoxOrient::Horizontal
                && parent()->style().boxAlign() == BoxAlignment::Stretch))
            relayoutChildren = RelayoutChildren::Yes;

        setBorderBoxHeight(0);

        m_stretchingChildren = false;

#if ASSERT_ENABLED
        LayoutSize oldLayoutDelta = view().frameView().layoutContext().layoutDelta();
#endif

        // 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);

        ChildBorderBoxRects oldChildRects;
        appendChildBorderBoxRects(this, oldChildRects);

        if (isHorizontal())
            layoutHorizontalBox(relayoutChildren);
        else
            layoutVerticalBox(relayoutChildren);

        repaintChildrenDuringLayoutIfMoved(this, oldChildRects);
        ASSERT(view().frameView().layoutContext().layoutDeltaMatches(oldLayoutDelta));

        auto contentArea = flippedContentBoxRect();
        updateLogicalHeight();
        updateInFlowDescendantTransformsAfterLayout();
        computeInFlowOverflow(contentArea);

        if (isDocumentElementRenderer() || previousSize.height() != borderBoxHeight())
            layoutOutOfFlowBoxes(RelayoutChildren::Yes);
        else
            layoutOutOfFlowBoxes(relayoutChildren);
        updateOutOfFlowDescendantTransformsAfterLayout();
        addOverflowFromOutOfFlowBoxes();
    }

    updateLayerTransform();

    auto* layoutState = view().frameView().layoutContext().layoutState();
    if (layoutState && layoutState->pageLogicalHeight())
        setPageLogicalOffset(layoutState->pageLogicalOffset(this, logicalTop()));

    // Repaint with our new bounds if they are different from our old bounds.
    repainter.repaintAfterLayout();
}

// The first walk over our kids is to find out if we have any flexible children.
static void gatherFlexChildrenInfo(FlexBoxIterator& iterator, RelayoutChildren relayoutChildren, unsigned& highestFlexGroup, unsigned& lowestFlexGroup, bool& haveFlex)
{
    for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
        // Check to see if this child flexes.
        if (!childDoesNotAffectWidthOrFlexing(child) && child->style().boxFlex() > 0.0f) {
            // We always have to lay out flexible objects again, since the flex distribution
            // may have changed, and we need to reallocate space.
            child->clearOverridingSize();
            if (relayoutChildren == RelayoutChildren::No)
                child->setChildNeedsLayout(MarkingBehavior::MarkOnlyThis);
            haveFlex = true;
            unsigned flexGroup = child->style().boxFlexGroup().value;
            if (lowestFlexGroup == 0)
                lowestFlexGroup = flexGroup;
            if (flexGroup < lowestFlexGroup)
                lowestFlexGroup = flexGroup;
            if (flexGroup > highestFlexGroup)
                highestFlexGroup = flexGroup;
        }
    }
}

static void issueFullRepaintOnFirstLayout(RenderBox& flexItem, bool everHadLayout)
{
    if (everHadLayout || !flexItem.checkForRepaintDuringLayout())
        return;

    flexItem.repaint();
    flexItem.repaintOverhangingFloats(true);
}

static void layoutChildIfNeededApplyingDelta(RenderBox* child, const LayoutSize& layoutDelta)
{
    if (!child->needsLayout())
        return;
    
    auto everHadLayout = child->everHadLayout();
    child->view().frameView().layoutContext().addLayoutDelta(layoutDelta);
    child->layoutIfNeeded();
    child->view().frameView().layoutContext().addLayoutDelta(-layoutDelta);
    issueFullRepaintOnFirstLayout(*child, everHadLayout);
}

void RenderDeprecatedFlexibleBox::layoutHorizontalBox(RelayoutChildren relayoutChildren)
{
    LayoutUnit toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight();
    LayoutUnit yPos = borderTop() + paddingTop();
    LayoutUnit xPos = borderLeft() + paddingLeft();
    bool heightSpecified = false;
    LayoutUnit oldHeight;

    LayoutUnit remainingSpace;

    FlexBoxIterator iterator(this);
    unsigned int highestFlexGroup = 0;
    unsigned int lowestFlexGroup = 0;
    bool haveFlex = false, flexingChildren = false; 
    gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex);

    beginUpdateScrollInfoAfterLayoutTransaction();

    ChildLayoutDeltas childLayoutDeltas;
    appendChildLayoutDeltas(this, childLayoutDeltas);

    // We do 2 passes.  The first pass is simply to lay everyone out at
    // their preferred widths. The subsequent passes handle flexing the children.
    // The first pass skips flexible objects completely.
    do {
        // Reset our height.
        setBorderBoxHeight(yPos);

        xPos = borderLeft() + paddingLeft();

        size_t childIndex = 0;

        // Our first pass is done without flexing.  We simply lay the children
        // out within the box.  We have to do a layout first in order to determine
        // our box's intrinsic height.
        LayoutUnit maxAscent, maxDescent;
        for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
            if (relayoutChildren == RelayoutChildren::Yes)
                child->setChildNeedsLayout(MarkingBehavior::MarkOnlyThis);

            if (child->isOutOfFlowPositioned())
                continue;
            
            LayoutSize& childLayoutDelta = childLayoutDeltas[childIndex++];
            
            // Compute the child's vertical margins.
            child->computeAndSetBlockDirectionMargins(*this);

            child->markForPaginationRelayoutIfNeeded();
            
            // Apply the child's current layout delta.
            layoutChildIfNeededApplyingDelta(child, childLayoutDelta);

            // Update our height and overflow height.
            if (style().boxAlign() == BoxAlignment::Baseline) {
                LayoutUnit ascent = child->firstLineBaseline().value_or(child->borderBoxHeight() + child->marginBottom());
                ascent += child->marginTop();
                LayoutUnit descent = (child->borderBoxHeight() + child->verticalMarginExtent()) - ascent;

                // Update our maximum ascent.
                maxAscent = std::max(maxAscent, ascent);

                // Update our maximum descent.
                maxDescent = std::max(maxDescent, descent);

                // Now update our height.
                setBorderBoxHeight(std::max(yPos + maxAscent + maxDescent, borderBoxHeight()));
            }
            else
                setBorderBoxHeight(std::max(borderBoxHeight(), yPos + child->borderBoxHeight() + child->verticalMarginExtent()));
        }
        ASSERT(childIndex == childLayoutDeltas.size());

        if (!iterator.first() && hasLineIfEmpty())
            setBorderBoxHeight(borderBoxHeight() + lineHeight());

        setBorderBoxHeight(borderBoxHeight() + toAdd);

        oldHeight = borderBoxHeight();
        updateLogicalHeight();

        relayoutChildren = RelayoutChildren::No;
        if (oldHeight != borderBoxHeight())
            heightSpecified = true;

        // Now that our height is actually known, we can place our boxes.
        childIndex = 0;
        m_stretchingChildren = (style().boxAlign() == BoxAlignment::Stretch);
        for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
            if (child->isOutOfFlowPositioned()) {
                child->containingBlock()->addOutOfFlowBox(*child);
                CheckedPtr childLayer = child->layer();
                childLayer->setStaticInlinePosition(xPos); // FIXME: Not right for regions.
                if (childLayer->staticBlockPosition() != yPos) {
                    childLayer->setStaticBlockPosition(yPos);
                    if (child->style().hasStaticBlockPosition(writingMode().isHorizontal()))
                        child->setChildNeedsLayout(MarkingBehavior::MarkOnlyThis);
                }
                continue;
            }
            
            LayoutSize& childLayoutDelta = childLayoutDeltas[childIndex++];

            // We need to see if this child's height has changed, since we make block elements
            // fill the height of a containing box by default.
            // Now do a layout.
            LayoutUnit oldChildHeight = child->borderBoxHeight();
            child->updateLogicalHeight();
            if (oldChildHeight != child->borderBoxHeight())
                child->setChildNeedsLayout(MarkingBehavior::MarkOnlyThis);

            child->markForPaginationRelayoutIfNeeded();

            layoutChildIfNeededApplyingDelta(child, childLayoutDelta);

            // We can place the child now, using our value of box-align.
            xPos += child->marginLeft();
            LayoutUnit childY = yPos;
            switch (style().boxAlign()) {
            case BoxAlignment::Center:
                childY += child->marginTop() + std::max<LayoutUnit>(0, (contentBoxHeight() - (child->borderBoxHeight() + child->verticalMarginExtent())) / 2);
                break;
            case BoxAlignment::Baseline: {
                LayoutUnit ascent = child->firstLineBaseline().value_or(child->borderBoxHeight() + child->marginBottom());
                ascent += child->marginTop();
                childY += child->marginTop() + (maxAscent - ascent);
                break;
            }
            case BoxAlignment::End:
                childY += contentBoxHeight() - child->marginBottom() - child->borderBoxHeight();
                break;
            default: // BoxAlignment::Start
                childY += child->marginTop();
                break;
            }

            placeChild(child, LayoutPoint(xPos, childY), &childLayoutDelta);

            xPos += child->borderBoxWidth() + child->marginRight();
        }
        ASSERT(childIndex == childLayoutDeltas.size());

        remainingSpace = borderLeft() + paddingLeft() + contentBoxWidth() - xPos;

        m_stretchingChildren = false;
        if (flexingChildren)
            haveFlex = false; // We're done.
        else if (haveFlex) {
            // We have some flexible objects.  See if we need to grow/shrink them at all.
            if (!remainingSpace)
                break;

            // Allocate the remaining space among the flexible objects.  If we are trying to
            // grow, then we go from the lowest flex group to the highest flex group.  For shrinking,
            // we go from the highest flex group to the lowest group.
            bool expanding = remainingSpace > 0;
            unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup;
            unsigned int end = expanding? highestFlexGroup : lowestFlexGroup;
            for (unsigned int i = start; i <= end && remainingSpace; i++) {
                // Always start off by assuming the group can get all the remaining space.
                LayoutUnit groupRemainingSpace = remainingSpace;
                do {
                    // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width
                    // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and
                    // computing the allowed growth before an object hits its min/max width (and thus
                    // forces a totalFlex recomputation).
                    LayoutUnit groupRemainingSpaceAtBeginning = groupRemainingSpace;
                    float totalFlex = 0.0f;
                    for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
                        if (allowedChildFlex(child, expanding, i))
                            totalFlex += child->style().boxFlex().value;
                    }
                    LayoutUnit spaceAvailableThisPass = groupRemainingSpace;
                    for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
                        LayoutUnit allowedFlex = allowedChildFlex(child, expanding, i);
                        if (allowedFlex) {
                            LayoutUnit projectedFlex = (allowedFlex == LayoutUnit::max()) ? allowedFlex : LayoutUnit(allowedFlex * (totalFlex / child->style().boxFlex().value));
                            spaceAvailableThisPass = expanding ? std::min(spaceAvailableThisPass, projectedFlex) : std::max(spaceAvailableThisPass, projectedFlex);
                        }
                    }

                    // The flex groups may not have any flexible objects this time around.
                    if (!spaceAvailableThisPass || totalFlex == 0.0f) {
                        // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group.
                        groupRemainingSpace = 0;
                        continue;
                    }

                    // Now distribute the space to objects.
                    for (RenderBox* child = iterator.first(); child && spaceAvailableThisPass && totalFlex; child = iterator.next()) {
                        if (allowedChildFlex(child, expanding, i)) {
                            LayoutUnit spaceAdd = LayoutUnit(spaceAvailableThisPass * (child->style().boxFlex().value / totalFlex));
                            if (spaceAdd) {
                                setOverridingMainAxisExtent(child, false, mainAxisExtentForChild(child, false) + spaceAdd);
                                flexingChildren = true;
                                relayoutChildren = RelayoutChildren::Yes;
                            }

                            spaceAvailableThisPass -= spaceAdd;
                            remainingSpace -= spaceAdd;
                            groupRemainingSpace -= spaceAdd;

                            totalFlex -= child->style().boxFlex().value;
                        }
                    }
                    if (groupRemainingSpace == groupRemainingSpaceAtBeginning) {
                        // This is not advancing, avoid getting stuck by distributing the remaining pixels.
                        LayoutUnit spaceAdd = groupRemainingSpace > 0 ? 1 : -1;
                        for (RenderBox* child = iterator.first(); child && groupRemainingSpace; child = iterator.next()) {
                            if (allowedChildFlex(child, expanding, i)) {
                                setOverridingMainAxisExtent(child, false, mainAxisExtentForChild(child, false) + spaceAdd);
                                flexingChildren = true;
                                relayoutChildren = RelayoutChildren::Yes;
                                remainingSpace -= spaceAdd;
                                groupRemainingSpace -= spaceAdd;
                            }
                        }
                    }
                } while (absoluteValue(groupRemainingSpace) >= 1);
            }

            // We didn't find any children that could grow.
            if (haveFlex && !flexingChildren)
                haveFlex = false;
        }
    } while (haveFlex);

    endAndCommitUpdateScrollInfoAfterLayoutTransaction();

    // Account for box-direction: reverse flipping the effective main-axis direction.
    bool isEffectiveLTR = style().writingMode().deprecatedIsLeftToRightDirection();
    if (style().boxOrient() == BoxOrient::Horizontal && style().boxDirection() == BoxDirection::Reverse)
        isEffectiveLTR = !isEffectiveLTR;

    auto forEachFlexChild = [&](auto callback) {
        for (auto* child = iterator.first(); child; child = iterator.next()) {
            if (!childDoesNotAffectWidthOrFlexing(child))
                callback(*child);
        }
    };

    auto offsetChildren = [&](LayoutUnit offset) {
        if (!offset)
            return;
        forEachFlexChild([&](RenderBox& child) {
            placeChild(&child, child.location() + LayoutSize(offset, 0_lu));
        });
    };

    if (style().boxPack() == BoxPack::Justify && remainingSpace > 0) {
        // Children must be repositioned.
        // Determine the total number of children.
        int totalChildren = 0;
        forEachFlexChild([&](auto&) { ++totalChildren; });

        // Iterate over the children and space them out according to the
        // justification level.
        if (totalChildren > 1) {
            --totalChildren;
            LayoutUnit offset;
            bool firstChild = true;
            forEachFlexChild([&](RenderBox& child) {
                if (firstChild) {
                    firstChild = false;
                    return;
                }
                offset += remainingSpace / totalChildren;
                remainingSpace -= (remainingSpace / totalChildren);
                --totalChildren;
                placeChild(&child, child.location() + LayoutSize(offset, 0_lu));
            });
        }
    } else {
        LayoutUnit offset;
        if (style().boxPack() == BoxPack::Center)
            offset += remainingSpace / 2;
        else if ((isEffectiveLTR && style().boxPack() == BoxPack::End)
            || (!isEffectiveLTR && style().boxPack() != BoxPack::End))
            offset += remainingSpace;
        offsetChildren(offset);
    }

    // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of
    // a height change, we revert our height back to the intrinsic height before returning.
    if (heightSpecified)
        setBorderBoxHeight(oldHeight);
}

void RenderDeprecatedFlexibleBox::layoutSingleClampedFlexItem()
{
    auto repainter = LayoutRepainter { *this };

    updateLogicalWidth();
    updateLogicalHeight();

    beginUpdateScrollInfoAfterLayoutTransaction();

    auto& clampedRendererCandidate = *firstChildBox();
    auto everHadLayout = clampedRendererCandidate.everHadLayout();
    clampedRendererCandidate.setLocation({ borderLeft() + paddingLeft(), borderTop() + paddingTop() });

    auto iterator = FlexBoxIterator { this };
    auto clampedContent = applyLineClamp(iterator, { });
    issueFullRepaintOnFirstLayout(clampedRendererCandidate, everHadLayout);

    clampedRendererCandidate.move(clampedRendererCandidate.marginLeft(), clampedRendererCandidate.marginTop());
    auto childBoxBottom = clampedRendererCandidate.logicalTop() + clampedRendererCandidate.borderAndPaddingBefore() + clampedRendererCandidate.borderAndPaddingAfter();
    if (clampedContent.renderer) {
        ASSERT(&clampedRendererCandidate == clampedContent.renderer.get());
        childBoxBottom += clampedContent.contentHeight;
    } else
        childBoxBottom += clampedRendererCandidate.contentBoxRect().height() + clampedRendererCandidate.marginBottom();

    setBorderBoxHeight(childBoxBottom + paddingBottom() + borderBottom());
    updateLogicalHeight();

    computeInFlowOverflow(flippedContentBoxRect());

    endAndCommitUpdateScrollInfoAfterLayoutTransaction();

    updateLayerTransform();

    repainter.repaintAfterLayout();
}

void RenderDeprecatedFlexibleBox::layoutVerticalBox(RelayoutChildren relayoutChildren)
{
    LayoutUnit yPos = borderTop() + paddingTop();
    LayoutUnit toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight();
    bool heightSpecified = false;
    LayoutUnit oldHeight;

    LayoutUnit remainingSpace;

    FlexBoxIterator iterator(this);
    unsigned int highestFlexGroup = 0;
    unsigned int lowestFlexGroup = 0;
    bool haveFlex = false, flexingChildren = false; 
    gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex);

    // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of
    // mainstream block layout); this is not really part of the XUL box model.
    bool haveLineClamp = !style().lineClamp().isNone();
    auto clampedContent = ClampedContent { };
    if (haveLineClamp)
        clampedContent = applyLineClamp(iterator, relayoutChildren);

    beginUpdateScrollInfoAfterLayoutTransaction();

    // We do 2 passes.  The first pass is simply to lay everyone out at
    // their preferred widths.  The second pass handles flexing the children.
    // Our first pass is done without flexing.  We simply lay the children
    // out within the box.
    do {
        setBorderBoxHeight(borderTop() + paddingTop());
        LayoutUnit minHeight = borderBoxHeight() + toAdd;

        for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
            // Make sure we relayout children if we need it.
            if (!haveLineClamp && relayoutChildren == RelayoutChildren::Yes)
                child->setChildNeedsLayout(MarkingBehavior::MarkOnlyThis);

            if (child->isOutOfFlowPositioned()) {
                child->containingBlock()->addOutOfFlowBox(*child);
                CheckedPtr childLayer = child->layer();
                childLayer->setStaticInlinePosition(borderAndPaddingStart()); // FIXME: Not right for regions.
                if (childLayer->staticBlockPosition() != borderBoxHeight()) {
                    childLayer->setStaticBlockPosition(borderBoxHeight());
                    if (child->style().hasStaticBlockPosition(writingMode().isHorizontal()))
                        child->setChildNeedsLayout(MarkingBehavior::MarkOnlyThis);
                }
                continue;
            }

            // Compute the child's vertical margins.
            child->computeAndSetBlockDirectionMargins(*this);

            // Add in the child's marginTop to our height.
            setBorderBoxHeight(borderBoxHeight() + child->marginTop());

            if (!haveLineClamp)
                child->markForPaginationRelayoutIfNeeded();

            // Now do a layout.
            auto everHadLayout = child->everHadLayout();
            child->layoutIfNeeded();
            issueFullRepaintOnFirstLayout(*child, everHadLayout);

            // We can place the child now, using our value of box-align.
            LayoutUnit childX = borderLeft() + paddingLeft();
            switch (style().boxAlign()) {
            case BoxAlignment::Center:
            case BoxAlignment::Baseline: // Baseline just maps to center for vertical boxes
                childX += child->marginLeft() + std::max<LayoutUnit>(0, (contentBoxWidth() - (child->borderBoxWidth() + child->horizontalMarginExtent())) / 2);
                break;
            case BoxAlignment::End:
                if (!style().writingMode().deprecatedIsLeftToRightDirection())
                    childX += child->marginLeft();
                else
                    childX += contentBoxWidth() - child->marginRight() - child->borderBoxWidth();
                break;
            default: // BoxAlignment::Start/BoxAlignment::Stretch
                if (style().writingMode().deprecatedIsLeftToRightDirection())
                    childX += child->marginLeft();
                else
                    childX += contentBoxWidth() - child->marginRight() - child->borderBoxWidth();
                break;
            }

            // Place the child.
            placeChild(child, LayoutPoint(childX, borderBoxHeight()));
            setBorderBoxHeight(borderBoxHeight() + child->borderBoxHeight() + child->marginBottom());
        }

        yPos = borderBoxHeight();

        if (!iterator.first() && hasLineIfEmpty())
            setBorderBoxHeight(borderBoxHeight() + lineHeight());

        setBorderBoxHeight(borderBoxHeight() + toAdd);

        // Negative margins can cause our height to shrink below our minimal height (border/padding).
        // If this happens, ensure that the computed height is increased to the minimal height.
        if (borderBoxHeight() < minHeight)
            setBorderBoxHeight(minHeight);

        // Now we have to calc our height, so we know how much space we have remaining.
        oldHeight = borderBoxHeight();
        updateLogicalHeight();
        if (oldHeight != borderBoxHeight())
            heightSpecified = true;

        remainingSpace = borderTop() + paddingTop() + contentBoxHeight() - yPos;

        if (flexingChildren)
            haveFlex = false; // We're done.
        else if (haveFlex) {
            // We have some flexible objects.  See if we need to grow/shrink them at all.
            if (!remainingSpace)
                break;

            // Allocate the remaining space among the flexible objects.  If we are trying to
            // grow, then we go from the lowest flex group to the highest flex group.  For shrinking,
            // we go from the highest flex group to the lowest group.
            bool expanding = remainingSpace > 0;
            unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup;
            unsigned int end = expanding? highestFlexGroup : lowestFlexGroup;
            for (unsigned int i = start; i <= end && remainingSpace; i++) {
                // Always start off by assuming the group can get all the remaining space.
                LayoutUnit groupRemainingSpace = remainingSpace;
                do {
                    // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width
                    // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and
                    // computing the allowed growth before an object hits its min/max width (and thus
                    // forces a totalFlex recomputation).
                    LayoutUnit groupRemainingSpaceAtBeginning = groupRemainingSpace;
                    float totalFlex = 0.0f;
                    for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
                        if (allowedChildFlex(child, expanding, i))
                            totalFlex += child->style().boxFlex().value;
                    }
                    LayoutUnit spaceAvailableThisPass = groupRemainingSpace;
                    for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
                        LayoutUnit allowedFlex = allowedChildFlex(child, expanding, i);
                        if (allowedFlex) {
                            LayoutUnit projectedFlex = (allowedFlex == LayoutUnit::max()) ? allowedFlex : LayoutUnit(allowedFlex * (totalFlex / child->style().boxFlex().value));
                            spaceAvailableThisPass = expanding ? std::min(spaceAvailableThisPass, projectedFlex) : std::max(spaceAvailableThisPass, projectedFlex);
                        }
                    }

                    // The flex groups may not have any flexible objects this time around.
                    if (!spaceAvailableThisPass || totalFlex == 0.0f) {
                        // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group.
                        groupRemainingSpace = 0;
                        continue;
                    }

                    // Now distribute the space to objects.
                    for (RenderBox* child = iterator.first(); child && spaceAvailableThisPass && totalFlex; child = iterator.next()) {
                        if (allowedChildFlex(child, expanding, i)) {
                            LayoutUnit spaceAdd { spaceAvailableThisPass * (child->style().boxFlex().value / totalFlex) };
                            if (spaceAdd) {
                                setOverridingMainAxisExtent(child, true, mainAxisExtentForChild(child, true) + spaceAdd);
                                flexingChildren = true;
                                relayoutChildren = RelayoutChildren::Yes;
                            }

                            spaceAvailableThisPass -= spaceAdd;
                            remainingSpace -= spaceAdd;
                            groupRemainingSpace -= spaceAdd;

                            totalFlex -= child->style().boxFlex().value;
                        }
                    }
                    if (groupRemainingSpace == groupRemainingSpaceAtBeginning) {
                        // This is not advancing, avoid getting stuck by distributing the remaining pixels.
                        LayoutUnit spaceAdd = groupRemainingSpace > 0 ? 1 : -1;
                        for (RenderBox* child = iterator.first(); child && groupRemainingSpace; child = iterator.next()) {
                            if (allowedChildFlex(child, expanding, i)) {
                                setOverridingMainAxisExtent(child, true, mainAxisExtentForChild(child, true) + spaceAdd);
                                flexingChildren = true;
                                relayoutChildren = RelayoutChildren::Yes;
                                remainingSpace -= spaceAdd;
                                groupRemainingSpace -= spaceAdd;
                            }
                        }
                    }
                } while (absoluteValue(groupRemainingSpace) >= 1);
            }

            // We didn't find any children that could grow.
            if (haveFlex && !flexingChildren)
                haveFlex = false;
        }
    } while (haveFlex);

    endAndCommitUpdateScrollInfoAfterLayoutTransaction();

    if (style().boxPack() != BoxPack::Start && remainingSpace > 0) {
        // Children must be repositioned.
        LayoutUnit offset;
        if (style().boxPack() == BoxPack::Justify) {
            // Determine the total number of children.
            int totalChildren = 0;
            for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
                if (childDoesNotAffectWidthOrFlexing(child))
                    continue;

                ++totalChildren;
            }

            // Iterate over the children and space them out according to the
            // justification level.
            if (totalChildren > 1) {
                --totalChildren;
                bool firstChild = true;
                for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
                    if (childDoesNotAffectWidthOrFlexing(child))
                        continue;

                    if (firstChild) {
                        firstChild = false;
                        continue;
                    }

                    offset += remainingSpace/totalChildren;
                    remainingSpace -= (remainingSpace/totalChildren);
                    --totalChildren;
                    placeChild(child, child->location() + LayoutSize(0_lu, offset));
                }
            }
        } else {
            if (style().boxPack() == BoxPack::Center)
                offset += remainingSpace / 2;
            else // BoxPack::End
                offset += remainingSpace;
            for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
                if (childDoesNotAffectWidthOrFlexing(child))
                    continue;
                placeChild(child, child->location() + LayoutSize(0_lu, offset));
            }
        }
    }

    // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of
    // a height change, we revert our height back to the intrinsic height before returning.
    if (haveLineClamp && clampedContent.renderer) {
        auto contentOffset = [&] {
            auto* clampedRenderer = clampedContent.renderer.get();
            auto contentLogicalTop = clampedRenderer->logicalTop() + clampedRenderer->contentBoxLocation().y();
            for (auto* ancestor = clampedRenderer->containingBlock(); ancestor; ancestor = ancestor->containingBlock()) {
                if (ancestor == this)
                    return contentLogicalTop;
                contentLogicalTop += ancestor->logicalTop();
            }
            ASSERT_NOT_REACHED();
            return contentBoxLocation().y();
        };
        auto usedHeight = borderBoxHeight();
        auto clampedHeight = contentOffset() + clampedContent.contentHeight + borderBottom() + paddingBottom();
        setBorderBoxHeight(clampedHeight);
        updateLogicalHeight();
        if (clampedHeight != borderBoxHeight())
            setBorderBoxHeight(heightSpecified ? oldHeight : usedHeight);
    } else if (heightSpecified)
        setBorderBoxHeight(oldHeight);
}

static size_t lineCountFor(const RenderBlockFlow& blockFlow)
{
    if (blockFlow.childrenInline())
        return blockFlow.lineCount();

    size_t count = 0;
    for (auto& child : childrenOfType<RenderBlockFlow>(blockFlow)) {
        if (blockFlow.isFloatingOrOutOfFlowPositioned() || !blockFlow.style().height().isAuto())
            continue;
        count += lineCountFor(child);
    }
    return count;
}

static RenderBlockFlow* blockContainerForLastFormattedLine(const RenderBlock& enclosingBlockContainer)
{
    for (auto* child = enclosingBlockContainer.lastChild(); child; child = child->previousSibling()) {
        CheckedPtr blockContainer = dynamicDowncast<RenderBlock>(*child);
        if (!blockContainer)
            continue;
        if (auto* descendantRoot = blockContainerForLastFormattedLine(*blockContainer))
            return descendantRoot;
        if (CheckedPtr blockFlow = dynamicDowncast<RenderBlockFlow>(*blockContainer); blockFlow && blockFlow->hasContentfulInlineLine())
            return blockFlow.unsafeGet();
    }
    return { };
}

RenderDeprecatedFlexibleBox::ClampedContent RenderDeprecatedFlexibleBox::applyLineClamp(FlexBoxIterator& iterator, RelayoutChildren relayoutChildren)
{
    auto initialize = [&] {
        for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
            if (childDoesNotAffectWidthOrFlexing(child))
                continue;

            child->clearOverridingSize();
            if (relayoutChildren == RelayoutChildren::Yes || (child->isBlockLevelReplacedOrAtomicInline() && (child->style().width().isPercentOrCalculated() || child->style().height().isPercentOrCalculated()))
                || (child->style().height().isAuto() && is<RenderBlockFlow>(*child))) {
                child->setChildNeedsLayout(MarkingBehavior::MarkOnlyThis);

                // Dirty all the positioned objects.
                if (CheckedPtr blockFlow = dynamicDowncast<RenderBlockFlow>(*child))
                    blockFlow->markOutOfFlowBoxesForLayout();
            }
        }
    };
    initialize();

    auto& layoutState = *view().frameView().layoutContext().layoutState();
    auto ancestorLineClamp = layoutState.legacyLineClamp();
    auto restoreAncestorLineClamp = makeScopeExit([&] {
        layoutState.setLegacyLineClamp(ancestorLineClamp);
    });

    auto lineCountForLineClamp = WTF::switchOn(style().lineClamp(),
        [](const CSS::Keyword::None&) -> size_t {
            ASSERT_NOT_REACHED();
            return 1;
        },
        [](const Style::WebkitLineClamp::Integer& integer) -> size_t {
            return integer.value;
        },
        [&](const Style::WebkitLineClamp::Percentage& percentage) -> size_t {
            size_t numberOfLines = 0;
            for (auto* child = iterator.first(); child; child = iterator.next()) {
                if (childDoesNotAffectWidthOrFlexing(child))
                    continue;

                child->layoutIfNeeded();
                if (auto* blockFlow = dynamicDowncast<RenderBlockFlow>(*child))
                    numberOfLines += lineCountFor(*blockFlow);
                // FIXME: This should be turned into a partial damage.
                child->setChildNeedsLayout(MarkingBehavior::MarkOnlyThis);
            }
            return std::max<size_t>(1, (numberOfLines + 1) * percentage.value / 100.f);
        }
    );

    layoutState.setLegacyLineClamp(RenderLayoutState::LegacyLineClamp { lineCountForLineClamp, { }, { }, { } });
    for (auto* child = iterator.first(); child; child = iterator.next()) {
        if (child->isOutOfFlowPositioned())
            continue;

        child->markForPaginationRelayoutIfNeeded();
        child->layoutIfNeeded();
    }
    if (auto* lastRoot = blockContainerForLastFormattedLine(*this)) {
        if (auto* inlineLayout = lastRoot->inlineLayout(); inlineLayout && inlineLayout->hasEllipsisInBlockDirectionOnLastFormattedLine()) {
            auto currentLineClamp = layoutState.legacyLineClamp();

            // Let line-clamp logic run but make sure no clamping happens (it's needed to make sure certain features are disabled like ellipsis in inline direction).
            layoutState.setLegacyLineClamp(RenderLayoutState::LegacyLineClamp { inlineLayout->lineCount() + 1, { }, { }, { } });
            lastRoot->setChildNeedsLayout(MarkingBehavior::MarkOnlyThis);
            lastRoot->layoutIfNeeded();

            layoutState.setLegacyLineClamp(currentLineClamp);
        }
    }

    auto lineClamp = *layoutState.legacyLineClamp();
    if (!lineClamp.clampedContentLogicalHeight) {
        // We've managed to run line clamping but it came back with no clamped content (i.e. there are fewer lines than the line-clamp limit).
        return { };
    }
    return { *lineClamp.clampedContentLogicalHeight, lineClamp.clampedRenderer };
}

void RenderDeprecatedFlexibleBox::clearLineClamp()
{
    FlexBoxIterator iterator(this);
    for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
        if (childDoesNotAffectWidthOrFlexing(child))
            continue;

        child->clearOverridingSize();
        if ((child->isBlockLevelReplacedOrAtomicInline() && (child->style().width().isPercentOrCalculated() || child->style().height().isPercentOrCalculated()))
            || (child->style().height().isAuto() && is<RenderBlockFlow>(*child))) {
            child->setChildNeedsLayout();

            if (CheckedPtr blockFlow = dynamicDowncast<RenderBlockFlow>(*child))
                blockFlow->markOutOfFlowBoxesForLayout();
        }
    }
}

void RenderDeprecatedFlexibleBox::placeChild(RenderBox* child, const LayoutPoint& location, LayoutSize* childLayoutDelta)
{
    // Place the child and track the layout delta so we can apply it if we do another layout.
    if (childLayoutDelta)
        *childLayoutDelta += LayoutSize(child->x() - location.x(), child->y() - location.y());
    child->setLocation(location);
}

LayoutUnit RenderDeprecatedFlexibleBox::allowedChildFlex(RenderBox* child, bool expanding, unsigned int group)
{
    if (childDoesNotAffectWidthOrFlexing(child) || child->style().boxFlex() == 0.0f || child->style().boxFlexGroup() != group)
        return 0;

    if (expanding) {
        if (isHorizontal()) {
            // FIXME: For now just handle fixed values.
            LayoutUnit maxWidth = LayoutUnit::max();
            LayoutUnit width = mainAxisContentExtentForChild(child, false);
            if (auto fixedMaxWidth = child->style().maxWidth().tryFixed())
                maxWidth = fixedMaxWidth->resolveZoom(child->style().usedZoomForLength());
            else if (child->style().maxWidth().isIntrinsicKeyword())
                maxWidth = child->maxContentLogicalWidthContribution();
            else if (child->style().maxWidth().isMinIntrinsic())
                maxWidth = child->minContentLogicalWidthContribution();
            if (maxWidth == LayoutUnit::max())
                return maxWidth;
            return std::max<LayoutUnit>(0, maxWidth - width);
        } else {
            // FIXME: For now just handle fixed values.
            LayoutUnit maxHeight = LayoutUnit::max();
            LayoutUnit height = mainAxisContentExtentForChild(child, true);
            if (auto fixedMaxHeight = child->style().maxHeight().tryFixed())
                maxHeight = fixedMaxHeight->resolveZoom(child->style().usedZoomForLength());
            if (maxHeight == LayoutUnit::max())
                return maxHeight;
            return std::max<LayoutUnit>(0, maxHeight - height);
        }
    }

    // FIXME: For now just handle fixed values.
    if (isHorizontal()) {
        LayoutUnit minWidth = child->minContentLogicalWidthContribution();
        LayoutUnit width = mainAxisContentExtentForChild(child, false);
        if (auto fixedMinWidth = child->style().minWidth().tryFixed())
            minWidth = fixedMinWidth->resolveZoom(child->style().usedZoomForLength());
        else if (child->style().minWidth().isIntrinsicKeyword())
            minWidth = child->maxContentLogicalWidthContribution();
        else if (child->style().minWidth().isMinIntrinsic())
            minWidth = child->minContentLogicalWidthContribution();
        else if (child->style().minWidth().isAuto())
            minWidth = 0;

        LayoutUnit allowedShrinkage = std::min<LayoutUnit>(0, minWidth - width);
        return allowedShrinkage;
    } else {
        auto& minHeight = child->style().minHeight();
        if (auto fixedMinHeight = minHeight.tryFixed()) {
            LayoutUnit minHeight { fixedMinHeight->resolveZoom(child->style().usedZoomForLength()) };
            LayoutUnit height = mainAxisContentExtentForChild(child, true);
            LayoutUnit allowedShrinkage = std::min<LayoutUnit>(0, minHeight - height);
            return allowedShrinkage;
        }
        if (minHeight.isAuto()) {
            LayoutUnit minHeight { 0 };
            LayoutUnit height = mainAxisContentExtentForChild(child, true);
            LayoutUnit allowedShrinkage = std::min<LayoutUnit>(0, minHeight - height);
            return allowedShrinkage;
        }
    }

    return 0;
}

ASCIILiteral RenderDeprecatedFlexibleBox::renderName() const
{
    if (isFloating())
        return "RenderDeprecatedFlexibleBox (floating)"_s;
    if (isOutOfFlowPositioned())
        return "RenderDeprecatedFlexibleBox (positioned)"_s;
    // FIXME: Temporary hack while the new generated content system is being implemented.
    if (isPseudoElement())
        return "RenderDeprecatedFlexibleBox (generated)"_s;
    if (isAnonymous())
        return "RenderDeprecatedFlexibleBox (generated)"_s;
    if (isRelativelyPositioned())
        return "RenderDeprecatedFlexibleBox (relative positioned)"_s;
    return "RenderDeprecatedFlexibleBox"_s;
}

} // namespace WebCore
