LayoutUnit LayoutMultiColumnSet::nextLogicalTopForUnbreakableContent(LayoutUnit flowThreadOffset, LayoutUnit contentLogicalHeight) const
{
    ASSERT(flowThreadOffset.mightBeSaturated() || pageLogicalTopForOffset(flowThreadOffset) == flowThreadOffset);
    FragmentationContext* enclosingFragmentationContext = multiColumnFlowThread()->enclosingFragmentationContext();
    if (!enclosingFragmentationContext) {
        // If there's no enclosing fragmentation context, there'll ever be only one row, and all
        // columns there will have the same height.
        return flowThreadOffset;
    }

    // Assert the problematic situation. If we have no problem with the column height, why are we
    // even here?
    ASSERT(pageLogicalHeightForOffset(flowThreadOffset) < contentLogicalHeight);

    // There's a likelihood for subsequent rows to be taller than the first one.
    // TODO(mstensho): if we're doubly nested (e.g. multicol in multicol in multicol), we need to
    // look beyond the first row here.
    const MultiColumnFragmentainerGroup& firstRow = firstFragmentainerGroup();
    LayoutUnit firstRowLogicalBottomInFlowThread = firstRow.logicalTopInFlowThread() + fragmentainerGroupCapacity(firstRow);
    if (flowThreadOffset >= firstRowLogicalBottomInFlowThread)
        return flowThreadOffset; // We're not in the first row. Give up.
    LayoutUnit newLogicalHeight = enclosingFragmentationContext->fragmentainerLogicalHeightAt(firstRow.blockOffsetInEnclosingFragmentationContext() + firstRow.logicalHeight());
    if (contentLogicalHeight > newLogicalHeight) {
        // The next outer column or page doesn't have enough space either. Give up and stay where
        // we are.
        return flowThreadOffset;
    }
    return firstRowLogicalBottomInFlowThread;
}
void LayoutMultiColumnFlowThread::appendNewFragmentainerGroupIfNeeded(LayoutUnit bottomOffsetInFlowThread)
{
    if (!isPageLogicalHeightKnown()) {
        // If we have no clue about the height of the multicol container, bail. This situation
        // occurs initially when an auto-height multicol container is nested inside another
        // auto-height multicol container. We need at least an estimated height of the outer
        // multicol container before we can check what an inner fragmentainer group has room for.
        // Its height is indefinite for now.
        return;
    }
    // TODO(mstensho): bottomOffsetInFlowThread is an endpoint-exclusive offset, i.e. the offset
    // just after the bottom of some object. So, ideally, columnSetAtBlockOffset() should be
    // informed about this (i.e. take a PageBoundaryRule argument). This is not the only place with
    // this issue; see also pageRemainingLogicalHeightForOffset().
    LayoutMultiColumnSet* columnSet = columnSetAtBlockOffset(bottomOffsetInFlowThread);
    if (columnSet->isInitialHeightCalculated()) {
        // We only insert additional fragmentainer groups in the initial layout pass. We only want
        // to balance columns in the last fragmentainer group (if we need to balance at all), so we
        // want that last fragmentainer group to be the same one in all layout passes that follow.
        return;
    }

    if (!columnSet->hasFragmentainerGroupForColumnAt(bottomOffsetInFlowThread)) {
        FragmentationContext* enclosingFragmentationContext = this->enclosingFragmentationContext();
        if (!enclosingFragmentationContext)
            return; // Not nested. We'll never need more rows than the one we already have then.
        ASSERT(!isLayoutPagedFlowThread());
        // We have run out of columns here, so we add another row to hold more columns. When we add
        // a new row, it implicitly means that we're inserting another column in our enclosing
        // multicol container. That in turn may mean that we've run out of columns there too.
        const MultiColumnFragmentainerGroup& newRow = columnSet->appendNewFragmentainerGroup();
        if (LayoutMultiColumnFlowThread* enclosingFlowThread = enclosingFragmentationContext->associatedFlowThread())
            enclosingFlowThread->appendNewFragmentainerGroupIfNeeded(newRow.blockOffsetInEnclosingFragmentationContext() + newRow.logicalHeight());
    }
}
void LayoutMultiColumnFlowThread::appendNewFragmentainerGroupIfNeeded(LayoutUnit offsetInFlowThread)
{
    if (!isPageLogicalHeightKnown()) {
        // If we have no clue about the height of the multicol container, bail. This situation
        // occurs initially when an auto-height multicol container is nested inside another
        // auto-height multicol container. We need at least an estimated height of the outer
        // multicol container before we can check what an inner fragmentainer group has room for.
        // Its height is indefinite for now.
        return;
    }
    LayoutMultiColumnSet* columnSet = columnSetAtBlockOffset(offsetInFlowThread);
    if (columnSet->isInitialHeightCalculated()) {
        // We only insert additional fragmentainer groups in the initial layout pass. We only want
        // to balance columns in the last fragmentainer group (if we need to balance at all), so we
        // want that last fragmentainer group to be the same one in all layout passes that follow.
        return;
    }

    if (!columnSet->hasFragmentainerGroupForColumnAt(offsetInFlowThread)) {
        FragmentationContext* enclosingFragmentationContext = this->enclosingFragmentationContext();
        if (!enclosingFragmentationContext)
            return; // Not nested. We'll never need more rows than the one we already have then.
        ASSERT(!isLayoutPagedFlowThread());
        // We have run out of columns here, so we add another row to hold more columns. When we add
        // a new row, it implicitly means that we're inserting another column in our enclosing
        // multicol container. That in turn may mean that we've run out of columns there too.
        const MultiColumnFragmentainerGroup& newRow = columnSet->appendNewFragmentainerGroup();
        if (LayoutMultiColumnFlowThread* enclosingFlowThread = enclosingFragmentationContext->associatedFlowThread())
            enclosingFlowThread->appendNewFragmentainerGroupIfNeeded(newRow.blockOffsetInEnclosingFragmentationContext());
    }
}
void MultiColumnFragmentainerGroup::resetColumnHeight()
{
    m_maxColumnHeight = calculateMaxColumnHeight();

    LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread();
    if (m_columnSet.heightIsAuto()) {
        FragmentationContext* enclosingFragmentationContext = flowThread->enclosingFragmentationContext();
        if (enclosingFragmentationContext && enclosingFragmentationContext->isFragmentainerLogicalHeightKnown()) {
            // Even if height is auto, we set an initial height, in order to tell how much content
            // this MultiColumnFragmentainerGroup can hold, and when we need to append a new one.
            m_columnHeight = m_maxColumnHeight;
        } else {
            m_columnHeight = LayoutUnit();
        }
    } else {
        setAndConstrainColumnHeight(heightAdjustedForRowOffset(flowThread->columnHeightAvailable()));
    }
}