LayoutUnit RenderMultiColumnSet::calculateColumnHeight(BalancedHeightCalculation calculationMode) const { if (calculationMode == GuessFromFlowThreadPortion) { // Initial balancing. Start with the lowest imaginable column height. We use the tallest // content run (after having "inserted" implicit breaks), and find its start offset (by // looking at the previous run's end offset, or, if there's no previous run, the set's start // offset in the flow thread). unsigned index = findRunWithTallestColumns(); LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() : logicalTopInFlowThread(); return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(startOffset), m_minimumColumnHeight); } if (actualColumnCount() <= usedColumnCount()) { // With the current column height, the content fits without creating overflowing columns. We're done. return m_columnHeight; } if (m_contentRuns.size() >= usedColumnCount()) { // Too many forced breaks to allow any implicit breaks. Initial balancing should already // have set a good height. There's nothing more we should do. return m_columnHeight; } // If the initial guessed column height wasn't enough, stretch it now. Stretch by the lowest // amount of space shortage found during layout. ASSERT(m_minSpaceShortage > 0); // We should never _shrink_ the height! ASSERT(m_minSpaceShortage != RenderFlowThread::maxLogicalHeight()); // If this happens, we probably have a bug. if (m_minSpaceShortage == RenderFlowThread::maxLogicalHeight()) return m_columnHeight; // So bail out rather than looping infinitely. return m_columnHeight + m_minSpaceShortage; }
void RenderMultiColumnSet::distributeImplicitBreaks() { #ifndef NDEBUG // There should be no implicit breaks assumed at this point. for (unsigned i = 0; i < m_contentRuns.size(); i++) ASSERT(!m_contentRuns[i].assumedImplicitBreaks()); #endif // NDEBUG // Insert a final content run to encompass all content. This will include overflow if this is // the last set. addContentRun(logicalBottomInFlowThread()); unsigned columnCount = m_contentRuns.size(); // If there is room for more breaks (to reach the used value of column-count), imagine that we // insert implicit breaks at suitable locations. At any given time, the content run with the // currently tallest columns will get another implicit break "inserted", which will increase its // column count by one and shrink its columns' height. Repeat until we have the desired total // number of breaks. The largest column height among the runs will then be the initial column // height for the balancer to use. while (columnCount < usedColumnCount()) { unsigned index = findRunWithTallestColumns(); m_contentRuns[index].assumeAnotherImplicitBreak(); columnCount++; } }
void RenderMultiColumnSet::addContentRun(LayoutUnit endOffsetFromFirstPage) { if (!multiColumnFlowThread()->requiresBalancing()) return; if (!m_contentRuns.isEmpty() && endOffsetFromFirstPage <= m_contentRuns.last().breakOffset()) return; // Append another item as long as we haven't exceeded used column count. What ends up in the // overflow area shouldn't affect column balancing. if (m_contentRuns.size() < usedColumnCount()) m_contentRuns.append(ContentRun(endOffsetFromFirstPage)); }
MultiColumnFragmentainerGroup& LayoutMultiColumnSet::appendNewFragmentainerGroup() { MultiColumnFragmentainerGroup newGroup(*this); { // Extra scope here for previousGroup; it's potentially invalid once we modify the m_fragmentainerGroups Vector. MultiColumnFragmentainerGroup& previousGroup = m_fragmentainerGroups.last(); // This is the flow thread block offset where |previousGroup| ends and |newGroup| takes over. LayoutUnit blockOffsetInFlowThread = previousGroup.logicalTopInFlowThread() + previousGroup.logicalHeight() * usedColumnCount(); previousGroup.setLogicalBottomInFlowThread(blockOffsetInFlowThread); newGroup.setLogicalTopInFlowThread(blockOffsetInFlowThread); newGroup.setLogicalTop(previousGroup.logicalTop() + previousGroup.logicalHeight()); newGroup.resetColumnHeight(); } m_fragmentainerGroups.append(newGroup); return m_fragmentainerGroups.last(); }
bool LayoutMultiColumnSet::hasFragmentainerGroupForColumnAt(LayoutUnit offsetInFlowThread) const { const MultiColumnFragmentainerGroup& lastRow = lastFragmentainerGroup(); if (lastRow.logicalTopInFlowThread() > offsetInFlowThread) return true; return offsetInFlowThread - lastRow.logicalTopInFlowThread() < lastRow.logicalHeight() * usedColumnCount(); }
LayoutUnit LayoutMultiColumnSet::nextLogicalTopForUnbreakableContent(LayoutUnit flowThreadOffset, LayoutUnit contentLogicalHeight) const { ASSERT(pageLogicalTopForOffset(flowThreadOffset) == flowThreadOffset); LayoutMultiColumnFlowThread* enclosingFlowThread = multiColumnFlowThread()->enclosingFlowThread(); if (!enclosingFlowThread) { // 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() + firstRow.logicalHeight() * usedColumnCount(); if (flowThreadOffset >= firstRowLogicalBottomInFlowThread) return flowThreadOffset; // We're not in the first row. Give up. LayoutUnit newLogicalHeight = enclosingFlowThread->pageLogicalHeightForOffset(firstRowLogicalBottomInFlowThread); 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; }
LayoutUnit LayoutMultiColumnSet::pageLogicalHeightForOffset(LayoutUnit offsetInFlowThread) const { const MultiColumnFragmentainerGroup &lastRow = lastFragmentainerGroup(); if (!lastRow.logicalHeight()) { // In the first layout pass of an auto-height multicol container, height isn't set. No need // to perform the series of complicated dance steps below to figure out that we should // simply return 0. Bail now. ASSERT(m_fragmentainerGroups.size() == 1); return LayoutUnit(); } if (offsetInFlowThread >= lastRow.logicalTopInFlowThread() + lastRow.logicalHeight() * usedColumnCount()) { // The offset is outside the bounds of the fragmentainer groups that we have established at // this point. If we're nested inside another fragmentation context, we need to calculate // the height on our own. const LayoutMultiColumnFlowThread* flowThread = multiColumnFlowThread(); if (FragmentationContext* enclosingFragmentationContext = flowThread->enclosingFragmentationContext()) { // We'd ideally like to translate |offsetInFlowThread| to an offset in the coordinate // space of the enclosing fragmentation context here, but that's hard, since the offset // is out of bounds. So just use the bottom we have found so far. LayoutUnit enclosingContextBottom = lastRow.blockOffsetInEnclosingFragmentationContext() + lastRow.logicalHeight(); LayoutUnit enclosingFragmentainerHeight = enclosingFragmentationContext->fragmentainerLogicalHeightAt(enclosingContextBottom); // Constrain against specified height / max-height. LayoutUnit currentMulticolHeight = logicalTopFromMulticolContentEdge() + lastRow.logicalTop() + lastRow.logicalHeight(); LayoutUnit multicolHeightWithExtraRow = currentMulticolHeight + enclosingFragmentainerHeight; multicolHeightWithExtraRow = std::min(multicolHeightWithExtraRow, flowThread->maxColumnLogicalHeight()); return std::max(LayoutUnit(1), multicolHeightWithExtraRow - currentMulticolHeight); } } return fragmentainerGroupAtFlowThreadOffset(offsetInFlowThread).logicalHeight(); }