Index: Source/core/rendering/RenderMultiColumnFlowThread.cpp |
diff --git a/Source/core/rendering/RenderMultiColumnFlowThread.cpp b/Source/core/rendering/RenderMultiColumnFlowThread.cpp |
index 6eaed6f174181af1b3308840f9bf3bb3b990256a..8e69a72a4677ab8eb94bb69356de38d2b6018288 100644 |
--- a/Source/core/rendering/RenderMultiColumnFlowThread.cpp |
+++ b/Source/core/rendering/RenderMultiColumnFlowThread.cpp |
@@ -32,11 +32,13 @@ |
namespace blink { |
RenderMultiColumnFlowThread::RenderMultiColumnFlowThread() |
- : m_columnCount(1) |
+ : m_lastSetWorkedOn(0) |
+ , m_columnCount(1) |
, m_columnHeightAvailable(0) |
, m_inBalancingPass(false) |
, m_needsColumnHeightsRecalculation(false) |
, m_progressionIsInline(true) |
+ , m_isBeingEvacuated(false) |
{ |
setFlowThreadState(InsideInFlowThread); |
} |
@@ -102,6 +104,7 @@ void RenderMultiColumnFlowThread::populate() |
void RenderMultiColumnFlowThread::evacuateAndDestroy() |
{ |
RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); |
+ m_isBeingEvacuated = true; |
// Remove all sets. |
while (RenderMultiColumnSet* columnSet = firstMultiColumnSet()) |
@@ -146,6 +149,27 @@ bool RenderMultiColumnFlowThread::needsNewWidth() const |
return newWidth != logicalWidth(); |
} |
+RenderMultiColumnSet* RenderMultiColumnFlowThread::columnSetAtBlockOffset(LayoutUnit offset) const |
+{ |
+ if (m_lastSetWorkedOn) { |
Julien - ping for review
2014/11/19 17:54:34
This is the only real use of |m_lastSetWorkedOn|,
mstensho (USE GERRIT)
2014/11/21 15:39:24
This is called a lot mid-layout (and intentionally
|
+ // Layout in progress. We are calculating the set heights as we speak, so the column set range |
+ // information is not up-to-date. |
+ return m_lastSetWorkedOn; |
+ } |
+ |
+ ASSERT(!m_regionsInvalidated); |
+ if (offset <= 0) |
+ return m_multiColumnSetList.isEmpty() ? 0 : m_multiColumnSetList.first(); |
+ |
+ MultiColumnSetSearchAdapter adapter(offset); |
+ m_multiColumnSetIntervalTree.allOverlapsWithAdapter<MultiColumnSetSearchAdapter>(adapter); |
+ |
+ // If no set was found, the offset is in the flow thread overflow. |
+ if (!adapter.result() && !m_multiColumnSetList.isEmpty()) |
+ return m_multiColumnSetList.last(); |
+ return adapter.result(); |
+} |
+ |
void RenderMultiColumnFlowThread::layoutColumns(bool relayoutChildren, SubtreeLayoutScope& layoutScope) |
{ |
if (relayoutChildren) |
@@ -162,16 +186,19 @@ void RenderMultiColumnFlowThread::layoutColumns(bool relayoutChildren, SubtreeLa |
return; |
} |
+ bool hasSpannerOrAutoHeightColumnSet = false; |
for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) { |
if (!m_inBalancingPass) { |
// This is the initial layout pass. We need to reset the column height, because contents |
// typically have changed. |
columnSet->resetColumnHeight(); |
} |
+ if (!hasSpannerOrAutoHeightColumnSet) |
+ hasSpannerOrAutoHeightColumnSet = columnSet->isRenderMultiColumnSpannerSet() || columnSet->heightIsAuto(); |
} |
invalidateRegions(); |
- m_needsColumnHeightsRecalculation = heightIsAuto(); |
+ m_needsColumnHeightsRecalculation = hasSpannerOrAutoHeightColumnSet; |
layout(); |
} |
@@ -316,8 +343,74 @@ void RenderMultiColumnFlowThread::willBeRemovedFromTree() |
RenderFlowThread::willBeRemovedFromTree(); |
} |
+bool RenderMultiColumnFlowThread::isColumnSpanner(const RenderObject* descendant) const |
+{ |
+ ASSERT(descendant->isDescendantOf(this)); |
+ return m_spannerMap.get(descendant); |
+} |
+ |
+bool RenderMultiColumnFlowThread::isInsideColumnSpanner(const RenderObject* descendant) const |
+{ |
+ ASSERT(descendant->isDescendantOf(this)); |
+ return containingColumnSpannerSet(descendant); |
+} |
+ |
+LayoutUnit RenderMultiColumnFlowThread::enterColumnSpannerBeforeLayout(RenderBox* renderer, LayoutUnit logicalTop, SubtreeLayoutScope& layoutScope) |
+{ |
+ ASSERT(isColumnSpanner(renderer)); |
+ RenderMultiColumnSpannerSet* spannerSet = m_spannerMap.get(renderer); |
+ |
+ // FIXME: it's really only necessary to mark the spanner set for layout when the height of |
+ // |renderer| changes. |
+ spannerSet->setChildNeedsLayout(MarkOnlyThis, &layoutScope); |
+ |
+ RenderMultiColumnSet* previousSet = spannerSet->previousSiblingMultiColumnSet(); |
+ if (!previousSet) { |
+ // The first set is entered at the beginning of flow thread layout. If the first set happens |
+ // to be a spanner, we have nothing more to do here. |
+ return LayoutUnit(); |
+ } |
+ |
+ RenderBlock* cb = toRenderBlock(renderer->container()); |
+ LayoutUnit logicalTopInFlowThread = cb->offsetFromLogicalTopOfFirstPage() + logicalTop; |
+ LayoutUnit adjustment; |
+ if (!previousSet->isRenderMultiColumnSpannerSet() && previousSet->pageLogicalHeight()) { |
+ // Pad flow thread offset to a column boundary, so that contents that's supposed to come |
+ // after the spanner (or the spanner itself) don't bleed into the column preceding the |
+ // spanner. |
+ LayoutUnit columnLogicalTopInFlowThread = previousSet->pageLogicalTopForOffset(logicalTopInFlowThread); |
+ if (columnLogicalTopInFlowThread != logicalTopInFlowThread) { |
+ adjustment = columnLogicalTopInFlowThread + previousSet->pageLogicalHeight() - logicalTopInFlowThread; |
+ logicalTopInFlowThread += adjustment; |
+ } |
+ } |
+ |
+ if (!previousSet->isRenderMultiColumnSpannerSet()) |
+ previousSet->endFlow(logicalTopInFlowThread); |
+ spannerSet->beginFlow(logicalTopInFlowThread); |
+ |
+ m_lastSetWorkedOn = spannerSet; |
+ return adjustment; |
+} |
+ |
+void RenderMultiColumnFlowThread::leaveColumnSpannerAfterLayout(RenderBox* renderer, LayoutUnit logicalBottom) |
+{ |
+ ASSERT(m_lastSetWorkedOn == m_spannerMap.get(renderer)); |
+ |
+ RenderBlock* cb = toRenderBlock(renderer->container()); |
+ LayoutUnit logicalBottomInFlowThread = cb->offsetFromLogicalTopOfFirstPage() + logicalBottom; |
+ m_lastSetWorkedOn->endFlow(logicalBottomInFlowThread); |
+ RenderMultiColumnSet* nextSet = m_lastSetWorkedOn->nextSiblingMultiColumnSet(); |
+ if (nextSet) { |
Julien - ping for review
2014/11/19 17:54:34
This should be a single line:
if (RenderMultiColu
mstensho (USE GERRIT)
2014/11/21 15:39:24
Done.
|
+ m_lastSetWorkedOn = nextSet; |
+ if (!m_lastSetWorkedOn->isRenderMultiColumnSpannerSet()) |
+ m_lastSetWorkedOn->beginFlow(logicalBottomInFlowThread); |
+ } |
+} |
+ |
void RenderMultiColumnFlowThread::flowThreadDescendantWasInserted(RenderObject* descendant) |
{ |
+ ASSERT(!m_isBeingEvacuated); |
// Go through the subtree that was just inserted and create column sets (needed by regular |
// column content) and spanner sets (one needed by each spanner). |
for (RenderObject* renderer = descendant; renderer; renderer = renderer->nextInPreOrder(descendant)) { |
@@ -335,6 +428,34 @@ void RenderMultiColumnFlowThread::flowThreadDescendantWasInserted(RenderObject* |
} |
} |
+void RenderMultiColumnFlowThread::flowThreadDescendantWillBeRemoved(RenderObject* descendant) |
+{ |
+ if (m_isBeingEvacuated) |
+ return; |
+ RenderObject* next; |
+ // Remove spanner sets that are no longer needed, and merge column sets around them. |
+ for (RenderObject* renderer = descendant; renderer; renderer = next) { |
+ RenderMultiColumnSpannerSet* spanner = m_spannerMap.get(renderer); |
+ if (!spanner) { |
+ next = renderer->nextInPreOrder(descendant); |
+ continue; |
+ } |
+ next = renderer->nextInPreOrderAfterChildren(descendant); // It's a spanner. Its children are of no interest to us. |
+ if (RenderMultiColumnSet* nextSet = spanner->nextSiblingMultiColumnSet()) { |
+ RenderMultiColumnSet* previousSet = spanner->previousSiblingMultiColumnSet(); |
+ if (nextSet && !nextSet->isRenderMultiColumnSpannerSet() |
+ && previousSet && !previousSet->isRenderMultiColumnSpannerSet()) { |
+ // Need to merge two column sets. |
+ nextSet->destroy(); |
+ previousSet->setNeedsLayout(); |
+ invalidateRegions(); |
+ } |
+ } |
+ m_spannerMap.remove(renderer); |
+ spanner->destroy(); |
+ } |
+} |
+ |
void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const |
{ |
// We simply remain at our intrinsic height. |
@@ -351,9 +472,17 @@ void RenderMultiColumnFlowThread::updateLogicalWidth() |
void RenderMultiColumnFlowThread::layout() |
{ |
+ ASSERT(!m_lastSetWorkedOn); |
+ m_lastSetWorkedOn = firstMultiColumnSet(); |
+ if (m_lastSetWorkedOn) |
+ m_lastSetWorkedOn->beginFlow(LayoutUnit()); |
RenderFlowThread::layout(); |
- if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) |
+ if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) { |
+ ASSERT(lastSet == m_lastSetWorkedOn); |
+ lastSet->endFlow(logicalHeight()); |
lastSet->expandToEncompassFlowThreadContentsIfNeeded(); |
+ } |
+ m_lastSetWorkedOn = 0; |
} |
void RenderMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage) |
@@ -375,12 +504,6 @@ void RenderMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, Lay |
multicolSet->updateMinimumColumnHeight(minHeight); |
} |
-RenderMultiColumnSet* RenderMultiColumnFlowThread::columnSetAtBlockOffset(LayoutUnit /*offset*/) const |
-{ |
- // For now there's only one column set, so this is easy: |
- return firstMultiColumnSet(); |
-} |
- |
bool RenderMultiColumnFlowThread::addForcedRegionBreak(LayoutUnit offset, RenderObject* /*breakChild*/, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment) |
{ |
if (RenderMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset)) { |