void LayoutMultiColumnFlowThread::evacuateAndDestroy() { LayoutBlockFlow* multicolContainer = multiColumnBlockFlow(); m_isBeingEvacuated = true; // Remove all sets and spanners. while (LayoutBox* columnBox = firstMultiColumnBox()) { ASSERT(columnBox->isAnonymous()); columnBox->destroy(); } ASSERT(!previousSibling()); ASSERT(!nextSibling()); // Finally we can promote all flow thread's children. Before we move them to the flow thread's // container, we need to unregister the flow thread, so that they aren't just re-added again to // the flow thread that we're trying to empty. multicolContainer->resetMultiColumnFlowThread(); moveAllChildrenTo(multicolContainer, true); // We used to manually nuke the line box tree here, but that should happen automatically when // moving children around (the code above). ASSERT(!firstLineBox()); destroy(); }
void LayoutMultiColumnFlowThread::layoutColumns(SubtreeLayoutScope& layoutScope) { // Since we ended up here, it means that the multicol container (our parent) needed // layout. Since contents of the multicol container are diverted to the flow thread, the flow // thread needs layout as well. layoutScope.setChildNeedsLayout(this); m_blockOffsetInEnclosingFragmentationContext = enclosingFragmentationContext() ? multiColumnBlockFlow()->offsetFromLogicalTopOfFirstPage() : LayoutUnit(); for (LayoutBox* columnBox = firstMultiColumnBox(); columnBox; columnBox = columnBox->nextSiblingMultiColumnBox()) { if (!columnBox->isLayoutMultiColumnSet()) { ASSERT(columnBox->isLayoutMultiColumnSpannerPlaceholder()); // no other type is expected. continue; } LayoutMultiColumnSet* columnSet = toLayoutMultiColumnSet(columnBox); layoutScope.setChildNeedsLayout(columnSet); if (!m_columnHeightsChanged) { // This is the initial layout pass. We need to reset the column height, because contents // typically have changed. columnSet->resetColumnHeight(); } // Since column sets are regular block flow objects, and their position is changed in // regular block layout code (with no means for the multicol code to notice unless we add // hooks there), store the previous position now. If it changes in the imminent layout // pass, we may have to rebalance its columns. columnSet->storeOldPosition(); } m_columnHeightsChanged = false; invalidateColumnSets(); layout(); validateColumnSets(); }
LayoutMultiColumnSpannerPlaceholder* LayoutMultiColumnFlowThread::containingColumnSpannerPlaceholder(const LayoutObject* descendant) const { ASSERT(descendant->isDescendantOf(this)); // Before we spend time on searching the ancestry, see if there's a quick way to determine // whether there might be any spanners at all. LayoutBox* firstBox = firstMultiColumnBox(); if (!firstBox || (firstBox == lastMultiColumnBox() && firstBox->isLayoutMultiColumnSet())) return nullptr; // We have spanners. See if the layoutObject in question is one or inside of one then. for (const LayoutObject* ancestor = descendant; ancestor && ancestor != this; ancestor = ancestor->parent()) { if (LayoutMultiColumnSpannerPlaceholder* placeholder = ancestor->spannerPlaceholder()) return placeholder; } return nullptr; }
void LayoutMultiColumnFlowThread::createAndInsertSpannerPlaceholder(LayoutBox* spannerObjectInFlowThread, LayoutObject* insertedBeforeInFlowThread) { LayoutBox* insertBeforeColumnBox = nullptr; LayoutMultiColumnSet* setToSplit = nullptr; if (insertedBeforeInFlowThread) { // The spanner is inserted before something. Figure out what this entails. If the // next object is a spanner too, it means that we can simply insert a new spanner // placeholder in front of its placeholder. insertBeforeColumnBox = insertedBeforeInFlowThread->spannerPlaceholder(); if (!insertBeforeColumnBox) { // The next object isn't a spanner; it's regular column content. Examine what // comes right before us in the flow thread, then. LayoutObject* previousLayoutObject = previousInPreOrderSkippingOutOfFlow(this, spannerObjectInFlowThread); if (!previousLayoutObject || previousLayoutObject == this) { // The spanner is inserted as the first child of the multicol container, // which means that we simply insert a new spanner placeholder at the // beginning. insertBeforeColumnBox = firstMultiColumnBox(); } else if (LayoutMultiColumnSpannerPlaceholder* previousPlaceholder = containingColumnSpannerPlaceholder(previousLayoutObject)) { // Before us is another spanner. We belong right after it then. insertBeforeColumnBox = previousPlaceholder->nextSiblingMultiColumnBox(); } else { // We're inside regular column content with both feet. Find out which column // set this is. It needs to be split it into two sets, so that we can insert // a new spanner placeholder between them. setToSplit = mapDescendantToColumnSet(previousLayoutObject); ASSERT(setToSplit == mapDescendantToColumnSet(insertedBeforeInFlowThread)); insertBeforeColumnBox = setToSplit->nextSiblingMultiColumnBox(); // We've found out which set that needs to be split. Now proceed to // inserting the spanner placeholder, and then insert a second column set. } } ASSERT(setToSplit || insertBeforeColumnBox); } LayoutBlockFlow* multicolContainer = multiColumnBlockFlow(); LayoutMultiColumnSpannerPlaceholder* newPlaceholder = LayoutMultiColumnSpannerPlaceholder::createAnonymous(multicolContainer->styleRef(), *spannerObjectInFlowThread); ASSERT(!insertBeforeColumnBox || insertBeforeColumnBox->parent() == multicolContainer); multicolContainer->LayoutBlock::addChild(newPlaceholder, insertBeforeColumnBox); spannerObjectInFlowThread->setSpannerPlaceholder(*newPlaceholder); if (setToSplit) createAndInsertMultiColumnSet(insertBeforeColumnBox); }