Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / RenderMultiColumnSet.cpp
index 4490737..a38addb 100644 (file)
@@ -40,12 +40,9 @@ RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread)
     , m_computedColumnCount(1)
     , m_computedColumnWidth(0)
     , m_computedColumnHeight(0)
-    , m_maxColumnHeight(LayoutUnit::max())
-    , m_minSpaceShortage(LayoutUnit::max())
+    , m_maxColumnHeight(RenderFlowThread::maxLogicalHeight())
+    , m_minSpaceShortage(RenderFlowThread::maxLogicalHeight())
     , m_minimumColumnHeight(0)
-    , m_forcedBreaksCount(0)
-    , m_maximumDistanceBetweenForcedBreaks(0)
-    , m_forcedBreakOffset(0)
 {
 }
 
@@ -81,46 +78,122 @@ void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight)
     // FIXME: the height may also be affected by the enclosing pagination context, if any.
 }
 
-bool RenderMultiColumnSet::calculateBalancedHeight(bool initial)
+unsigned RenderMultiColumnSet::findRunWithTallestColumns() const
 {
-    ASSERT(toRenderMultiColumnBlock(parent())->requiresBalancing());
-    LayoutUnit oldColumnHeight = m_computedColumnHeight;
-    LayoutUnit currentMinSpaceShortage = m_minSpaceShortage;
-    m_minSpaceShortage = LayoutUnit::max();
+    unsigned indexWithLargestHeight = 0;
+    LayoutUnit largestHeight;
+    LayoutUnit previousOffset;
+    size_t runCount = m_contentRuns.size();
+    ASSERT(runCount);
+    for (size_t i = 0; i < runCount; i++) {
+        const ContentRun& run = m_contentRuns[i];
+        LayoutUnit height = run.columnLogicalHeight(previousOffset);
+        if (largestHeight < height) {
+            largestHeight = height;
+            indexWithLargestHeight = i;
+        }
+        previousOffset = run.breakOffset();
+    }
+    return indexWithLargestHeight;
+}
+
+void RenderMultiColumnSet::distributeImplicitBreaks()
+{
+    unsigned breakCount = forcedBreaksCount();
+
+#ifndef NDEBUG
+    // There should be no implicit breaks assumed at this point.
+    for (unsigned i = 0; i < breakCount; i++)
+        ASSERT(!m_contentRuns[i].assumedImplicitBreaks());
+#endif // NDEBUG
+
+    // There will always be at least one break, since the flow thread reports a "forced break" at
+    // end of content.
+    ASSERT(breakCount >= 1);
+
+    // 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 (breakCount < m_computedColumnCount) {
+        unsigned index = findRunWithTallestColumns();
+        m_contentRuns[index].assumeAnotherImplicitBreak();
+        breakCount++;
+    }
+}
 
+LayoutUnit RenderMultiColumnSet::calculateBalancedHeight(bool initial) const
+{
     if (initial) {
         // Start with the lowest imaginable column height.
-        LayoutUnit logicalHeightGuess = ceilf(float(flowThread()->logicalHeight()) / float(m_computedColumnCount));
-        logicalHeightGuess = max(logicalHeightGuess, m_minimumColumnHeight);
-        setAndConstrainColumnHeight(logicalHeightGuess);
-
-        // The multicol container now typically needs at least one more layout pass with a new
-        // column height, but if height was specified, we only need to do this if we found that we
-        // might need less space than that. On the other hand, if we determined that the columns
-        // need to be as tall as the specified height of the container, we have already laid it out
-        // correctly, and there's no need for another pass.
-        return m_computedColumnHeight != oldColumnHeight;
+        unsigned index = findRunWithTallestColumns();
+        LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() : LayoutUnit();
+        return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(startOffset), m_minimumColumnHeight);
     }
 
     if (columnCount() <= computedColumnCount()) {
         // With the current column height, the content fits without creating overflowing columns. We're done.
-        return false;
+        return m_computedColumnHeight;
+    }
+
+    if (forcedBreaksCount() > 1 && forcedBreaksCount() >= computedColumnCount()) {
+        // 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_computedColumnHeight;
     }
 
     // If the initial guessed column height wasn't enough, stretch it now. Stretch by the lowest
     // amount of space shortage found during layout.
 
-    ASSERT(currentMinSpaceShortage != LayoutUnit::max()); // If this can actually happen, we probably have a bug.
-    if (currentMinSpaceShortage == LayoutUnit::max())
-        return false; // So bail out rather than looping infinitely.
+    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_computedColumnHeight; // So bail out rather than looping infinitely.
 
-    setAndConstrainColumnHeight(m_computedColumnHeight + currentMinSpaceShortage);
+    return m_computedColumnHeight + m_minSpaceShortage;
+}
 
-    // If we reach the maximum column height (typically set by the height or max-height property),
-    // we may not be allowed to stretch further. Return true only if stretching
-    // succeeded. Otherwise, we're done.
-    ASSERT(m_computedColumnHeight >= oldColumnHeight); // We shouldn't be able to shrink the height!
-    return m_computedColumnHeight > oldColumnHeight;
+void RenderMultiColumnSet::clearForcedBreaks()
+{
+    m_contentRuns.clear();
+}
+
+void RenderMultiColumnSet::addForcedBreak(LayoutUnit offsetFromFirstPage)
+{
+    if (!toRenderMultiColumnBlock(parent())->requiresBalancing())
+        return;
+    if (!m_contentRuns.isEmpty() && offsetFromFirstPage <= 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() < m_computedColumnCount)
+        m_contentRuns.append(ContentRun(offsetFromFirstPage));
+}
+
+bool RenderMultiColumnSet::recalculateBalancedHeight(bool initial)
+{
+    ASSERT(toRenderMultiColumnBlock(parent())->requiresBalancing());
+
+    LayoutUnit oldColumnHeight = m_computedColumnHeight;
+    if (initial)
+        distributeImplicitBreaks();
+    LayoutUnit newColumnHeight = calculateBalancedHeight(initial);
+    setAndConstrainColumnHeight(newColumnHeight);
+
+    // After having calculated an initial column height, the multicol container typically needs at
+    // least one more layout pass with a new column height, but if a height was specified, we only
+    // need to do this if we think that we need less space than specified. Conversely, if we
+    // determined that the columns need to be as tall as the specified height of the container, we
+    // have already laid it out correctly, and there's no need for another pass.
+
+    if (m_computedColumnHeight == oldColumnHeight)
+        return false; // No change. We're done.
+
+    m_minSpaceShortage = RenderFlowThread::maxLogicalHeight();
+    clearForcedBreaks();
+    return true; // Need another pass.
 }
 
 void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage)
@@ -171,12 +244,15 @@ void RenderMultiColumnSet::prepareForLayout()
 
     if (multicolBlock->requiresBalancing()) {
         // Set maximum column height. We will not stretch beyond this.
-        m_maxColumnHeight = LayoutUnit::max();
-        if (!multicolStyle->logicalHeight().isAuto())
+        m_maxColumnHeight = RenderFlowThread::maxLogicalHeight();
+        if (!multicolStyle->logicalHeight().isAuto()) {
             m_maxColumnHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalHeight(), -1);
+            if (m_maxColumnHeight == -1)
+                m_maxColumnHeight = RenderFlowThread::maxLogicalHeight();
+        }
         if (!multicolStyle->logicalMaxHeight().isUndefined()) {
             LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalMaxHeight(), -1);
-            if (m_maxColumnHeight > logicalMaxHeight)
+            if (logicalMaxHeight != -1 && m_maxColumnHeight > logicalMaxHeight)
                 m_maxColumnHeight = logicalMaxHeight;
         }
         m_maxColumnHeight = heightAdjustedForSetOffset(m_maxColumnHeight);
@@ -185,6 +261,8 @@ void RenderMultiColumnSet::prepareForLayout()
         setAndConstrainColumnHeight(heightAdjustedForSetOffset(multicolBlock->columnHeightAvailable()));
     }
 
+    clearForcedBreaks();
+
     // Nuke previously stored minimum column height. Contents may have changed for all we know.
     m_minimumColumnHeight = 0;
 }
@@ -308,7 +386,7 @@ void RenderMultiColumnSet::paintObject(PaintInfo& paintInfo, const LayoutPoint&
     if (style()->visibility() != VISIBLE)
         return;
 
-    RenderBlock::paintObject(paintInfo, paintOffset);
+    RenderBlockFlow::paintObject(paintInfo, paintOffset);
 
     // FIXME: Right now we're only painting in the foreground phase.
     // Columns should technically respect phases and allow for background/float/foreground overlap etc., just like