LayoutMultiColumnSet* LayoutMultiColumnFlowThread::mapDescendantToColumnSet(LayoutObject* layoutObject) const { ASSERT(!containingColumnSpannerPlaceholder(layoutObject)); // should not be used for spanners or content inside them. ASSERT(layoutObject != this); ASSERT(layoutObject->isDescendantOf(this)); ASSERT(layoutObject->containingBlock()->isDescendantOf(this)); // Out-of-flow objects don't belong in column sets. ASSERT(layoutObject->flowThreadContainingBlock() == this); ASSERT(!layoutObject->isLayoutMultiColumnSet()); ASSERT(!layoutObject->isLayoutMultiColumnSpannerPlaceholder()); LayoutMultiColumnSet* multicolSet = firstMultiColumnSet(); if (!multicolSet) return nullptr; if (!multicolSet->nextSiblingMultiColumnSet()) return multicolSet; // This is potentially SLOW! But luckily very uncommon. You would have to dynamically insert a // spanner into the middle of column contents to need this. for (; multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) { LayoutObject* firstLayoutObject = firstLayoutObjectInSet(multicolSet); LayoutObject* lastLayoutObject = lastLayoutObjectInSet(multicolSet); ASSERT(firstLayoutObject); for (LayoutObject* walker = firstLayoutObject; walker; walker = walker->nextInPreOrder(this)) { if (walker == layoutObject) return multicolSet; if (walker == lastLayoutObject) break; } } 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(); }
void LayoutMultiColumnFlowThread::willBeRemovedFromTree() { // Detach all column sets from the flow thread. Cannot destroy them at this point, since they // are siblings of this object, and there may be pointers to this object's sibling somewhere // further up on the call stack. for (LayoutMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) columnSet->detachFromFlowThread(); multiColumnBlockFlow()->resetMultiColumnFlowThread(); LayoutFlowThread::willBeRemovedFromTree(); }
void LayoutMultiColumnFlowThread::columnRuleStyleDidChange() { for (LayoutMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) columnSet->setShouldDoFullPaintInvalidation(PaintInvalidationStyleChange); }
bool LayoutMultiColumnFlowThread::recalculateColumnHeights() { // All column sets that needed layout have now been laid out, so we can finally validate them. validateColumnSets(); if (!m_needsColumnHeightsRecalculation) return false; // Column heights may change here because of balancing. We may have to do multiple layout // passes, depending on how the contents is fitted to the changed column heights. In most // cases, laying out again twice or even just once will suffice. Sometimes we need more // passes than that, though, but the number of retries should not exceed the number of // columns, unless we have a bug. bool needsRelayout = false; for (LayoutMultiColumnSet* multicolSet = firstMultiColumnSet(); multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) needsRelayout |= multicolSet->recalculateColumnHeight(); if (needsRelayout) setChildNeedsLayout(MarkOnlyThis); m_inBalancingPass = needsRelayout; return needsRelayout; }