Chromium Code Reviews| 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)) { |