void LayoutMultiColumnFlowThread::skipColumnSpanner(LayoutBox* layoutObject, LayoutUnit logicalTopInFlowThread) { ASSERT(layoutObject->isColumnSpanAll()); LayoutMultiColumnSpannerPlaceholder* placeholder = layoutObject->spannerPlaceholder(); LayoutBox* previousColumnBox = placeholder->previousSiblingMultiColumnBox(); if (previousColumnBox && previousColumnBox->isLayoutMultiColumnSet()) { LayoutMultiColumnSet* columnSet = toLayoutMultiColumnSet(previousColumnBox); if (logicalTopInFlowThread < columnSet->logicalTopInFlowThread()) logicalTopInFlowThread = columnSet->logicalTopInFlowThread(); // Negative margins may cause this. columnSet->endFlow(logicalTopInFlowThread); } LayoutBox* nextColumnBox = placeholder->nextSiblingMultiColumnBox(); if (nextColumnBox && nextColumnBox->isLayoutMultiColumnSet()) { LayoutMultiColumnSet* nextSet = toLayoutMultiColumnSet(nextColumnBox); m_lastSetWorkedOn = nextSet; nextSet->beginFlow(logicalTopInFlowThread); } // We'll lay out of spanners after flow thread layout has finished (during layout of the spanner // placeholders). There may be containing blocks for out-of-flow positioned descendants of the // spanner in the flow thread, so that out-of-flow objects inside the spanner will be laid out // as part of flow thread layout (even if the spanner itself won't). We need to add such // out-of-flow positioned objects to their containing blocks now, or they'll never get laid // out. Since it's non-trivial to determine if we need this, and where such out-of-flow objects // might be, just go through the whole subtree. for (LayoutObject* descendant = layoutObject->slowFirstChild(); descendant; descendant = descendant->nextInPreOrder()) { if (descendant->isBox() && descendant->isOutOfFlowPositioned()) descendant->containingBlock()->insertPositionedObject(toLayoutBox(descendant)); } }
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(); }
LayoutMultiColumnSet* LayoutMultiColumnSet::nextSiblingMultiColumnSet() const { for (LayoutObject* sibling = nextSibling(); sibling; sibling = sibling->nextSibling()) { if (sibling->isLayoutMultiColumnSet()) return toLayoutMultiColumnSet(sibling); } return nullptr; }
LayoutMultiColumnSet* LayoutMultiColumnFlowThread::lastMultiColumnSet() const { for (LayoutObject* sibling = multiColumnBlockFlow()->lastChild(); sibling; sibling = sibling->previousSibling()) { if (sibling->isLayoutMultiColumnSet()) return toLayoutMultiColumnSet(sibling); } return nullptr; }
void LayoutMultiColumnFlowThread::flowThreadDescendantWillBeRemoved(LayoutObject* descendant) { // This method ensures that the list of column sets and spanner placeholders reflects the // multicol content that we'll be left with after removal of a descendant (or descendant // subtree). See the header file for more information. Removing content may mean that we need to // remove column sets and/or spanner placeholders. if (m_isBeingEvacuated) return; if (shouldSkipInsertedOrRemovedChild(this, *descendant)) return; bool hadContainingPlaceholder = containingColumnSpannerPlaceholder(descendant); bool processedSomething = false; LayoutObject* next; // Remove spanner placeholders that are no longer needed, and merge column sets around them. for (LayoutObject* layoutObject = descendant; layoutObject; layoutObject = next) { if (layoutObject != descendant && shouldSkipInsertedOrRemovedChild(this, *layoutObject)) { next = layoutObject->nextInPreOrderAfterChildren(descendant); continue; } processedSomething = true; LayoutMultiColumnSpannerPlaceholder* placeholder = layoutObject->spannerPlaceholder(); if (!placeholder) { next = layoutObject->nextInPreOrder(descendant); continue; } next = layoutObject->nextInPreOrderAfterChildren(descendant); // It's a spanner. Its children are of no interest to us. destroySpannerPlaceholder(placeholder); } if (hadContainingPlaceholder || !processedSomething) return; // No column content will be removed, so we can stop here. // Column content will be removed. Does this mean that we should destroy a column set? LayoutMultiColumnSpannerPlaceholder* adjacentPreviousSpannerPlaceholder = nullptr; LayoutObject* previousLayoutObject = previousInPreOrderSkippingOutOfFlow(this, descendant); if (previousLayoutObject && previousLayoutObject != this) { adjacentPreviousSpannerPlaceholder = containingColumnSpannerPlaceholder(previousLayoutObject); if (!adjacentPreviousSpannerPlaceholder) return; // Preceded by column content. Set still needed. } LayoutMultiColumnSpannerPlaceholder* adjacentNextSpannerPlaceholder = nullptr; LayoutObject* nextLayoutObject = nextInPreOrderAfterChildrenSkippingOutOfFlow(this, descendant); if (nextLayoutObject) { adjacentNextSpannerPlaceholder = containingColumnSpannerPlaceholder(nextLayoutObject); if (!adjacentNextSpannerPlaceholder) return; // Followed by column content. Set still needed. } // We have now determined that, with the removal of |descendant|, we should remove a column // set. Locate it and remove it. Do it without involving mapDescendantToColumnSet(), as that // might be very slow. Deduce the right set from the spanner placeholders that we've already // found. LayoutMultiColumnSet* columnSetToRemove; if (adjacentNextSpannerPlaceholder) { columnSetToRemove = toLayoutMultiColumnSet(adjacentNextSpannerPlaceholder->previousSiblingMultiColumnBox()); ASSERT(!adjacentPreviousSpannerPlaceholder || columnSetToRemove == adjacentPreviousSpannerPlaceholder->nextSiblingMultiColumnBox()); } else if (adjacentPreviousSpannerPlaceholder) { columnSetToRemove = toLayoutMultiColumnSet(adjacentPreviousSpannerPlaceholder->nextSiblingMultiColumnBox()); } else { // If there were no adjacent spanners, it has to mean that there's only one column set, // since it's only spanners that may cause creation of multiple sets. columnSetToRemove = firstMultiColumnSet(); ASSERT(columnSetToRemove); ASSERT(!columnSetToRemove->nextSiblingMultiColumnSet()); } ASSERT(columnSetToRemove); columnSetToRemove->destroy(); }