Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / layout / LayoutMultiColumnFlowThread.cpp
blob8d6760f61ee097bbcff8461ea3ace23f15f8ed75
1 /*
2 * Copyright (C) 2012 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS IN..0TERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "config.h"
27 #include "core/layout/LayoutMultiColumnFlowThread.h"
29 #include "core/layout/LayoutMultiColumnSet.h"
30 #include "core/layout/LayoutMultiColumnSpannerPlaceholder.h"
31 #include "core/layout/MultiColumnFragmentainerGroup.h"
33 namespace blink {
35 LayoutMultiColumnFlowThread::LayoutMultiColumnFlowThread()
36 : m_lastSetWorkedOn(nullptr)
37 , m_columnCount(1)
38 , m_columnHeightAvailable(0)
39 , m_inBalancingPass(false)
40 , m_needsColumnHeightsRecalculation(false)
41 , m_progressionIsInline(true)
42 , m_isBeingEvacuated(false)
44 setIsInsideFlowThread(true);
47 LayoutMultiColumnFlowThread::~LayoutMultiColumnFlowThread()
51 LayoutMultiColumnFlowThread* LayoutMultiColumnFlowThread::createAnonymous(Document& document, const ComputedStyle& parentStyle)
53 LayoutMultiColumnFlowThread* layoutObject = new LayoutMultiColumnFlowThread();
54 layoutObject->setDocumentForAnonymous(&document);
55 layoutObject->setStyle(ComputedStyle::createAnonymousStyleWithDisplay(parentStyle, BLOCK));
56 return layoutObject;
59 LayoutMultiColumnSet* LayoutMultiColumnFlowThread::firstMultiColumnSet() const
61 for (LayoutObject* sibling = nextSibling(); sibling; sibling = sibling->nextSibling()) {
62 if (sibling->isLayoutMultiColumnSet())
63 return toLayoutMultiColumnSet(sibling);
65 return nullptr;
68 LayoutMultiColumnSet* LayoutMultiColumnFlowThread::lastMultiColumnSet() const
70 for (LayoutObject* sibling = multiColumnBlockFlow()->lastChild(); sibling; sibling = sibling->previousSibling()) {
71 if (sibling->isLayoutMultiColumnSet())
72 return toLayoutMultiColumnSet(sibling);
74 return nullptr;
77 static inline bool isMultiColumnContainer(const LayoutObject& object)
79 if (!object.isLayoutBlockFlow())
80 return false;
81 return toLayoutBlockFlow(object).multiColumnFlowThread();
84 // Find the next layout object that has the multicol container in its containing block chain, skipping nested multicol containers.
85 static LayoutObject* nextInPreOrderAfterChildrenSkippingOutOfFlow(LayoutMultiColumnFlowThread* flowThread, LayoutObject* descendant)
87 ASSERT(descendant->isDescendantOf(flowThread));
88 LayoutObject* object = descendant->nextInPreOrderAfterChildren(flowThread);
89 while (object) {
90 // Walk through the siblings and find the first one which is either in-flow or has this
91 // flow thread as its containing block flow thread.
92 if (!object->isOutOfFlowPositioned())
93 break;
94 if (object->containingBlock()->flowThreadContainingBlock() == flowThread) {
95 // This out-of-flow object is still part of the flow thread, because its containing
96 // block (probably relatively positioned) is part of the flow thread.
97 break;
99 object = object->nextInPreOrderAfterChildren(flowThread);
101 if (!object)
102 return nullptr;
103 #if ENABLE(ASSERT)
104 // Make sure that we didn't stumble into an inner multicol container.
105 for (LayoutObject* walker = object->parent(); walker && walker != flowThread; walker = walker->parent())
106 ASSERT(!isMultiColumnContainer(*walker));
107 #endif
108 return object;
111 // Find the previous layout object that has the multicol container in its containing block chain, skipping nested multicol containers.
112 static LayoutObject* previousInPreOrderSkippingOutOfFlow(LayoutMultiColumnFlowThread* flowThread, LayoutObject* descendant)
114 ASSERT(descendant->isDescendantOf(flowThread));
115 LayoutObject* object = descendant->previousInPreOrder(flowThread);
116 while (object && object != flowThread) {
117 if (object->isColumnSpanAll()) {
118 LayoutMultiColumnFlowThread* placeholderFlowThread = toLayoutBox(object)->spannerPlaceholder()->flowThread();
119 if (placeholderFlowThread == flowThread)
120 break;
121 // We're inside an inner multicol container. We have no business there. Continue on the outside.
122 object = placeholderFlowThread->parent();
123 ASSERT(object->isDescendantOf(flowThread));
124 continue;
126 if (object->flowThreadContainingBlock() == flowThread) {
127 LayoutObject* ancestor;
128 for (ancestor = object->parent(); ; ancestor = ancestor->parent()) {
129 if (ancestor == flowThread)
130 return object;
131 if (isMultiColumnContainer(*ancestor)) {
132 // We're inside an inner multicol container. We have no business there.
133 break;
136 object = ancestor;
137 ASSERT(ancestor->isDescendantOf(flowThread));
138 continue; // Continue on the outside of the inner flow thread.
140 // We're inside something that's out-of-flow. Keep looking upwards and backwards in the tree.
141 object = object->previousInPreOrder(flowThread);
143 if (!object || object == flowThread)
144 return nullptr;
145 #if ENABLE(ASSERT)
146 // Make sure that we didn't stumble into an inner multicol container.
147 for (LayoutObject* walker = object->parent(); walker && walker != flowThread; walker = walker->parent())
148 ASSERT(!isMultiColumnContainer(*walker));
149 #endif
150 return object;
153 static LayoutObject* firstLayoutObjectInSet(LayoutMultiColumnSet* multicolSet)
155 LayoutBox* sibling = multicolSet->previousSiblingMultiColumnBox();
156 if (!sibling)
157 return multicolSet->flowThread()->firstChild();
158 // Adjacent column content sets should not occur. We would have no way of figuring out what each
159 // of them contains then.
160 ASSERT(sibling->isLayoutMultiColumnSpannerPlaceholder());
161 LayoutBox* spanner = toLayoutMultiColumnSpannerPlaceholder(sibling)->layoutObjectInFlowThread();
162 return nextInPreOrderAfterChildrenSkippingOutOfFlow(multicolSet->multiColumnFlowThread(), spanner);
165 static LayoutObject* lastLayoutObjectInSet(LayoutMultiColumnSet* multicolSet)
167 LayoutBox* sibling = multicolSet->nextSiblingMultiColumnBox();
168 if (!sibling)
169 return nullptr; // By right we should return lastLeafChild() here, but the caller doesn't care, so just return 0.
170 // Adjacent column content sets should not occur. We would have no way of figuring out what each
171 // of them contains then.
172 ASSERT(sibling->isLayoutMultiColumnSpannerPlaceholder());
173 LayoutBox* spanner = toLayoutMultiColumnSpannerPlaceholder(sibling)->layoutObjectInFlowThread();
174 return previousInPreOrderSkippingOutOfFlow(multicolSet->multiColumnFlowThread(), spanner);
177 LayoutMultiColumnSet* LayoutMultiColumnFlowThread::mapDescendantToColumnSet(LayoutObject* layoutObject) const
179 ASSERT(!containingColumnSpannerPlaceholder(layoutObject)); // should not be used for spanners or content inside them.
180 ASSERT(layoutObject != this);
181 ASSERT(layoutObject->isDescendantOf(this));
182 ASSERT(layoutObject->containingBlock()->isDescendantOf(this)); // Out-of-flow objects don't belong in column sets.
183 ASSERT(layoutObject->flowThreadContainingBlock() == this);
184 ASSERT(!layoutObject->isLayoutMultiColumnSet());
185 ASSERT(!layoutObject->isLayoutMultiColumnSpannerPlaceholder());
186 LayoutMultiColumnSet* multicolSet = firstMultiColumnSet();
187 if (!multicolSet)
188 return nullptr;
189 if (!multicolSet->nextSiblingMultiColumnSet())
190 return multicolSet;
192 // This is potentially SLOW! But luckily very uncommon. You would have to dynamically insert a
193 // spanner into the middle of column contents to need this.
194 for (; multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) {
195 LayoutObject* firstLayoutObject = firstLayoutObjectInSet(multicolSet);
196 LayoutObject* lastLayoutObject = lastLayoutObjectInSet(multicolSet);
197 ASSERT(firstLayoutObject);
199 for (LayoutObject* walker = firstLayoutObject; walker; walker = walker->nextInPreOrder(this)) {
200 if (walker == layoutObject)
201 return multicolSet;
202 if (walker == lastLayoutObject)
203 break;
207 return nullptr;
210 LayoutMultiColumnSpannerPlaceholder* LayoutMultiColumnFlowThread::containingColumnSpannerPlaceholder(const LayoutObject* descendant) const
212 ASSERT(descendant->isDescendantOf(this));
214 // Before we spend time on searching the ancestry, see if there's a quick way to determine
215 // whether there might be any spanners at all.
216 LayoutBox* firstBox = firstMultiColumnBox();
217 if (!firstBox || (firstBox == lastMultiColumnBox() && firstBox->isLayoutMultiColumnSet()))
218 return nullptr;
220 // We have spanners. See if the layoutObject in question is one or inside of one then.
221 for (const LayoutObject* ancestor = descendant; ancestor && ancestor != this; ancestor = ancestor->parent()) {
222 if (LayoutMultiColumnSpannerPlaceholder* placeholder = ancestor->spannerPlaceholder())
223 return placeholder;
225 return nullptr;
228 void LayoutMultiColumnFlowThread::populate()
230 LayoutBlockFlow* multicolContainer = multiColumnBlockFlow();
231 ASSERT(!nextSibling());
232 // Reparent children preceding the flow thread into the flow thread. It's multicol content
233 // now. At this point there's obviously nothing after the flow thread, but layoutObjects (column
234 // sets and spanners) will be inserted there as we insert elements into the flow thread.
235 multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), this, true);
238 void LayoutMultiColumnFlowThread::evacuateAndDestroy()
240 LayoutBlockFlow* multicolContainer = multiColumnBlockFlow();
241 m_isBeingEvacuated = true;
243 // Remove all sets and spanners.
244 while (LayoutBox* columnBox = firstMultiColumnBox()) {
245 ASSERT(columnBox->isAnonymous());
246 columnBox->destroy();
249 ASSERT(!previousSibling());
250 ASSERT(!nextSibling());
252 // Finally we can promote all flow thread's children. Before we move them to the flow thread's
253 // container, we need to unregister the flow thread, so that they aren't just re-added again to
254 // the flow thread that we're trying to empty.
255 multicolContainer->resetMultiColumnFlowThread();
256 moveAllChildrenTo(multicolContainer, true);
258 // We used to manually nuke the line box tree here, but that should happen automatically when
259 // moving children around (the code above).
260 ASSERT(!firstLineBox());
262 destroy();
265 LayoutSize LayoutMultiColumnFlowThread::columnOffset(const LayoutPoint& point) const
267 if (!hasValidColumnSetInfo())
268 return LayoutSize(0, 0);
270 LayoutPoint flowThreadPoint = flipForWritingMode(point);
271 LayoutUnit blockOffset = isHorizontalWritingMode() ? flowThreadPoint.y() : flowThreadPoint.x();
272 return flowThreadTranslationAtOffset(blockOffset);
275 bool LayoutMultiColumnFlowThread::needsNewWidth() const
277 LayoutUnit newWidth;
278 unsigned dummyColumnCount; // We only care if used column-width changes.
279 calculateColumnCountAndWidth(newWidth, dummyColumnCount);
280 return newWidth != logicalWidth();
283 bool LayoutMultiColumnFlowThread::isPageLogicalHeightKnown() const
285 if (LayoutMultiColumnSet* columnSet = lastMultiColumnSet())
286 return columnSet->isPageLogicalHeightKnown();
287 return false;
290 LayoutSize LayoutMultiColumnFlowThread::flowThreadTranslationAtOffset(LayoutUnit offsetInFlowThread) const
292 LayoutMultiColumnSet* columnSet = columnSetAtBlockOffset(offsetInFlowThread);
293 if (!columnSet)
294 return LayoutSize(0, 0);
295 return columnSet->flowThreadTranslationAtOffset(offsetInFlowThread);
298 LayoutPoint LayoutMultiColumnFlowThread::visualPointToFlowThreadPoint(const LayoutPoint& visualPoint) const
300 LayoutUnit blockOffset = isHorizontalWritingMode() ? visualPoint.y() : visualPoint.x();
301 const LayoutMultiColumnSet* columnSet = nullptr;
302 for (const LayoutMultiColumnSet* candidate = firstMultiColumnSet(); candidate; candidate = candidate->nextSiblingMultiColumnSet()) {
303 columnSet = candidate;
304 if (candidate->logicalBottom() > blockOffset)
305 break;
307 return columnSet ? columnSet->visualPointToFlowThreadPoint(toLayoutPoint(visualPoint + location() - columnSet->location())) : visualPoint;
310 LayoutMultiColumnSet* LayoutMultiColumnFlowThread::columnSetAtBlockOffset(LayoutUnit offset) const
312 if (LayoutMultiColumnSet* columnSet = m_lastSetWorkedOn) {
313 // Layout in progress. We are calculating the set heights as we speak, so the column set range
314 // information is not up-to-date.
315 while (columnSet->logicalTopInFlowThread() > offset) {
316 // Sometimes we have to use a previous set. This happens when we're working with a block
317 // that contains a spanner (so that there's a column set both before and after the
318 // spanner, and both sets contain said block).
319 LayoutMultiColumnSet* previousSet = columnSet->previousSiblingMultiColumnSet();
320 if (!previousSet)
321 break;
322 columnSet = previousSet;
324 return columnSet;
327 ASSERT(!m_columnSetsInvalidated);
328 if (m_multiColumnSetList.isEmpty())
329 return nullptr;
330 if (offset <= 0)
331 return m_multiColumnSetList.first();
333 MultiColumnSetSearchAdapter adapter(offset);
334 m_multiColumnSetIntervalTree.allOverlapsWithAdapter<MultiColumnSetSearchAdapter>(adapter);
336 // If no set was found, the offset is in the flow thread overflow.
337 if (!adapter.result() && !m_multiColumnSetList.isEmpty())
338 return m_multiColumnSetList.last();
339 return adapter.result();
342 void LayoutMultiColumnFlowThread::layoutColumns(bool relayoutChildren, SubtreeLayoutScope& layoutScope)
344 if (relayoutChildren)
345 layoutScope.setChildNeedsLayout(this);
347 m_needsColumnHeightsRecalculation = false;
348 if (!needsLayout()) {
349 // Just before the multicol container (our parent LayoutBlockFlow) finishes laying out, it
350 // will call recalculateColumnHeights() on us unconditionally, but we only want that method
351 // to do any work if we actually laid out the flow thread. Otherwise, the balancing
352 // machinery would kick in needlessly, and trigger additional layout passes. Furthermore, we
353 // actually depend on a proper flowthread layout pass in order to do balancing, since it's
354 // flowthread layout that sets up content runs.
355 return;
358 m_blockOffsetInEnclosingFlowThread = enclosingFlowThread() ? multiColumnBlockFlow()->offsetFromLogicalTopOfFirstPage() : LayoutUnit();
360 for (LayoutBox* columnBox = firstMultiColumnBox(); columnBox; columnBox = columnBox->nextSiblingMultiColumnBox()) {
361 if (!columnBox->isLayoutMultiColumnSet()) {
362 ASSERT(columnBox->isLayoutMultiColumnSpannerPlaceholder()); // no other type is expected.
363 m_needsColumnHeightsRecalculation = true;
364 continue;
366 LayoutMultiColumnSet* columnSet = toLayoutMultiColumnSet(columnBox);
367 if (!m_inBalancingPass) {
368 // This is the initial layout pass. We need to reset the column height, because contents
369 // typically have changed.
370 columnSet->resetColumnHeight();
372 if (!m_needsColumnHeightsRecalculation)
373 m_needsColumnHeightsRecalculation = columnSet->heightIsAuto();
376 invalidateColumnSets();
377 layout();
380 bool LayoutMultiColumnFlowThread::recalculateColumnHeights()
382 // All column sets that needed layout have now been laid out, so we can finally validate them.
383 validateColumnSets();
385 if (!m_needsColumnHeightsRecalculation)
386 return false;
388 // Column heights may change here because of balancing. We may have to do multiple layout
389 // passes, depending on how the contents is fitted to the changed column heights. In most
390 // cases, laying out again twice or even just once will suffice. Sometimes we need more
391 // passes than that, though, but the number of retries should not exceed the number of
392 // columns, unless we have a bug.
393 bool needsRelayout = false;
394 for (LayoutMultiColumnSet* multicolSet = firstMultiColumnSet(); multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) {
395 needsRelayout |= multicolSet->recalculateColumnHeight(m_inBalancingPass ? StretchBySpaceShortage : GuessFromFlowThreadPortion);
396 if (needsRelayout) {
397 // Once a column set gets a new column height, that column set and all successive column
398 // sets need to be laid out over again, since their logical top will be affected by
399 // this, and therefore their column heights may change as well, at least if the multicol
400 // height is constrained.
401 multicolSet->setChildNeedsLayout(MarkOnlyThis);
405 if (needsRelayout)
406 setChildNeedsLayout(MarkOnlyThis);
408 m_inBalancingPass = needsRelayout;
409 return needsRelayout;
412 void LayoutMultiColumnFlowThread::columnRuleStyleDidChange()
414 for (LayoutMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet())
415 columnSet->setShouldDoFullPaintInvalidation(PaintInvalidationStyleChange);
418 bool LayoutMultiColumnFlowThread::removeSpannerPlaceholderIfNoLongerValid(LayoutBox* spannerObjectInFlowThread)
420 ASSERT(spannerObjectInFlowThread->spannerPlaceholder());
421 if (descendantIsValidColumnSpanner(spannerObjectInFlowThread))
422 return false; // Still a valid spanner.
424 // No longer a valid spanner. Get rid of the placeholder.
425 destroySpannerPlaceholder(spannerObjectInFlowThread->spannerPlaceholder());
426 ASSERT(!spannerObjectInFlowThread->spannerPlaceholder());
428 // We may have a new containing block, since we're no longer a spanner. Mark it for relayout.
429 spannerObjectInFlowThread->containingBlock()->setNeedsLayoutAndPrefWidthsRecalc(LayoutInvalidationReason::ColumnsChanged);
431 // Now generate a column set for this ex-spanner, if needed and none is there for us already.
432 flowThreadDescendantWasInserted(spannerObjectInFlowThread);
434 return true;
437 LayoutMultiColumnFlowThread* LayoutMultiColumnFlowThread::enclosingFlowThread() const
439 if (multiColumnBlockFlow()->isInsideFlowThread())
440 return toLayoutMultiColumnFlowThread(locateFlowThreadContainingBlockOf(*multiColumnBlockFlow()));
441 return nullptr;
444 bool LayoutMultiColumnFlowThread::hasFragmentainerGroupForColumnAt(LayoutUnit offsetInFlowThread) const
446 // If there's no enclosing flow thread, there'll always be only one fragmentainer group, and it
447 // can hold as many columns as we like. We shouldn't even be here in that case.
448 ASSERT(enclosingFlowThread());
450 LayoutMultiColumnSet* lastColumnSet = lastMultiColumnSet();
451 if (!lastColumnSet) {
452 ASSERT_NOT_REACHED();
453 return true;
455 if (lastColumnSet->logicalTopInFlowThread() > offsetInFlowThread)
456 return true;
457 const MultiColumnFragmentainerGroup& lastRow = lastColumnSet->lastFragmentainerGroup();
458 if (lastRow.logicalTopInFlowThread() > offsetInFlowThread)
459 return true;
460 return offsetInFlowThread - lastRow.logicalTopInFlowThread() < lastRow.logicalHeight() * lastColumnSet->usedColumnCount();
463 void LayoutMultiColumnFlowThread::appendNewFragmentainerGroupIfNeeded(LayoutUnit offsetInFlowThread)
465 LayoutMultiColumnFlowThread* enclosingFlowThread = this->enclosingFlowThread();
466 if (!enclosingFlowThread)
467 return; // Not nested. We'll never need more rows than the one we already have then.
468 if (!hasFragmentainerGroupForColumnAt(offsetInFlowThread)) {
469 // We have run out of columns here, so we add another row to hold more columns. When we add
470 // a new row, it implicitly means that we're inserting another column in our enclosing
471 // multicol container. That in turn may mean that we've run out of columns there too.
472 const MultiColumnFragmentainerGroup& newRow = lastMultiColumnSet()->appendNewFragmentainerGroup();
473 enclosingFlowThread->appendNewFragmentainerGroupIfNeeded(newRow.blockOffsetInEnclosingFlowThread());
477 void LayoutMultiColumnFlowThread::calculateColumnCountAndWidth(LayoutUnit& width, unsigned& count) const
479 LayoutBlock* columnBlock = multiColumnBlockFlow();
480 const ComputedStyle* columnStyle = columnBlock->style();
481 LayoutUnit availableWidth = columnBlock->contentLogicalWidth();
482 LayoutUnit columnGap = columnBlock->columnGap();
483 LayoutUnit computedColumnWidth = max<LayoutUnit>(1, LayoutUnit(columnStyle->columnWidth()));
484 unsigned computedColumnCount = max<int>(1, columnStyle->columnCount());
486 ASSERT(!columnStyle->hasAutoColumnCount() || !columnStyle->hasAutoColumnWidth());
487 if (columnStyle->hasAutoColumnWidth() && !columnStyle->hasAutoColumnCount()) {
488 count = computedColumnCount;
489 width = std::max<LayoutUnit>(0, (availableWidth - ((count - 1) * columnGap)) / count);
490 } else if (!columnStyle->hasAutoColumnWidth() && columnStyle->hasAutoColumnCount()) {
491 count = std::max<LayoutUnit>(1, (availableWidth + columnGap) / (computedColumnWidth + columnGap));
492 width = ((availableWidth + columnGap) / count) - columnGap;
493 } else {
494 count = std::max<LayoutUnit>(std::min<LayoutUnit>(computedColumnCount, (availableWidth + columnGap) / (computedColumnWidth + columnGap)), 1);
495 width = ((availableWidth + columnGap) / count) - columnGap;
499 void LayoutMultiColumnFlowThread::createAndInsertMultiColumnSet(LayoutBox* insertBefore)
501 LayoutBlockFlow* multicolContainer = multiColumnBlockFlow();
502 LayoutMultiColumnSet* newSet = LayoutMultiColumnSet::createAnonymous(*this, multicolContainer->styleRef());
503 multicolContainer->LayoutBlock::addChild(newSet, insertBefore);
504 invalidateColumnSets();
506 // We cannot handle immediate column set siblings (and there's no need for it, either).
507 // There has to be at least one spanner separating them.
508 ASSERT(!newSet->previousSiblingMultiColumnBox() || !newSet->previousSiblingMultiColumnBox()->isLayoutMultiColumnSet());
509 ASSERT(!newSet->nextSiblingMultiColumnBox() || !newSet->nextSiblingMultiColumnBox()->isLayoutMultiColumnSet());
512 void LayoutMultiColumnFlowThread::createAndInsertSpannerPlaceholder(LayoutBox* spannerObjectInFlowThread, LayoutObject* insertedBeforeInFlowThread)
514 LayoutBox* insertBeforeColumnBox = nullptr;
515 LayoutMultiColumnSet* setToSplit = nullptr;
516 if (insertedBeforeInFlowThread) {
517 // The spanner is inserted before something. Figure out what this entails. If the
518 // next object is a spanner too, it means that we can simply insert a new spanner
519 // placeholder in front of its placeholder.
520 insertBeforeColumnBox = insertedBeforeInFlowThread->spannerPlaceholder();
521 if (!insertBeforeColumnBox) {
522 // The next object isn't a spanner; it's regular column content. Examine what
523 // comes right before us in the flow thread, then.
524 LayoutObject* previousLayoutObject = previousInPreOrderSkippingOutOfFlow(this, spannerObjectInFlowThread);
525 if (!previousLayoutObject || previousLayoutObject == this) {
526 // The spanner is inserted as the first child of the multicol container,
527 // which means that we simply insert a new spanner placeholder at the
528 // beginning.
529 insertBeforeColumnBox = firstMultiColumnBox();
530 } else if (LayoutMultiColumnSpannerPlaceholder* previousPlaceholder = containingColumnSpannerPlaceholder(previousLayoutObject)) {
531 // Before us is another spanner. We belong right after it then.
532 insertBeforeColumnBox = previousPlaceholder->nextSiblingMultiColumnBox();
533 } else {
534 // We're inside regular column content with both feet. Find out which column
535 // set this is. It needs to be split it into two sets, so that we can insert
536 // a new spanner placeholder between them.
537 setToSplit = mapDescendantToColumnSet(previousLayoutObject);
538 ASSERT(setToSplit == mapDescendantToColumnSet(insertedBeforeInFlowThread));
539 setToSplit->setNeedsLayoutAndFullPaintInvalidation(LayoutInvalidationReason::ColumnsChanged);
540 insertBeforeColumnBox = setToSplit->nextSiblingMultiColumnBox();
541 // We've found out which set that needs to be split. Now proceed to
542 // inserting the spanner placeholder, and then insert a second column set.
545 ASSERT(setToSplit || insertBeforeColumnBox);
548 LayoutBlockFlow* multicolContainer = multiColumnBlockFlow();
549 LayoutMultiColumnSpannerPlaceholder* newPlaceholder = LayoutMultiColumnSpannerPlaceholder::createAnonymous(multicolContainer->styleRef(), *spannerObjectInFlowThread);
550 ASSERT(!insertBeforeColumnBox || insertBeforeColumnBox->parent() == multicolContainer);
551 multicolContainer->LayoutBlock::addChild(newPlaceholder, insertBeforeColumnBox);
552 spannerObjectInFlowThread->setSpannerPlaceholder(*newPlaceholder);
554 if (setToSplit)
555 createAndInsertMultiColumnSet(insertBeforeColumnBox);
558 void LayoutMultiColumnFlowThread::destroySpannerPlaceholder(LayoutMultiColumnSpannerPlaceholder* placeholder)
560 if (LayoutBox* nextColumnBox = placeholder->nextSiblingMultiColumnBox()) {
561 LayoutBox* previousColumnBox = placeholder->previousSiblingMultiColumnBox();
562 if (nextColumnBox && nextColumnBox->isLayoutMultiColumnSet()
563 && previousColumnBox && previousColumnBox->isLayoutMultiColumnSet()) {
564 // Need to merge two column sets.
565 nextColumnBox->destroy();
566 previousColumnBox->setNeedsLayout(LayoutInvalidationReason::ColumnsChanged);
567 invalidateColumnSets();
570 placeholder->destroy();
573 bool LayoutMultiColumnFlowThread::descendantIsValidColumnSpanner(LayoutObject* descendant) const
575 // This method needs to behave correctly in the following situations:
576 // - When the descendant doesn't have a spanner placeholder but should have one (return true)
577 // - When the descendant doesn't have a spanner placeholder and still should not have one (return false)
578 // - When the descendant has a spanner placeholder but should no longer have one (return false)
579 // - When the descendant has a spanner placeholder and should still have one (return true)
581 // We assume that we're inside the flow thread. This function is not to be called otherwise.
582 ASSERT(descendant->isDescendantOf(this));
584 // The spec says that column-span only applies to in-flow block-level elements.
585 if (descendant->style()->columnSpan() != ColumnSpanAll || !descendant->isBox() || descendant->isInline() || descendant->isFloatingOrOutOfFlowPositioned())
586 return false;
588 if (!descendant->containingBlock()->isLayoutBlockFlow()) {
589 // Needs to be in a block-flow container, and not e.g. a table.
590 return false;
593 // This looks like a spanner, but if we're inside something unbreakable, it's not to be treated as one.
594 for (LayoutBox* ancestor = toLayoutBox(descendant)->parentBox(); ancestor; ancestor = ancestor->containingBlock()) {
595 if (ancestor->isLayoutFlowThread()) {
596 ASSERT(ancestor == this);
597 return true;
599 if (ancestor->spannerPlaceholder()) {
600 // FIXME: do we want to support nested spanners in a different way? The outer spanner
601 // has already broken out from the columns to become sized by the multicol container,
602 // which may be good enough for the inner spanner. But margins, borders, padding and
603 // explicit widths on the outer spanner, or on any children between the outer and inner
604 // spanner, will affect the width of the inner spanner this way, which might be
605 // undesirable. The spec has nothing to say on the matter.
606 return false; // Ignore nested spanners.
608 if (ancestor->isFloatingOrOutOfFlowPositioned()) {
609 // TODO(mstensho): It could actually be nice to support this (although the usefulness is
610 // probably very limited), but currently our column balancing algorithm gets confused
611 // when a spanner is inside a float, because a float's position isn't always known until
612 // after layout. Similarly for absolutely positioned boxes.
613 return false;
615 if (ancestor->isUnsplittableForPagination())
616 return false;
618 ASSERT_NOT_REACHED();
619 return false;
622 void LayoutMultiColumnFlowThread::addColumnSetToThread(LayoutMultiColumnSet* columnSet)
624 if (LayoutMultiColumnSet* nextSet = columnSet->nextSiblingMultiColumnSet()) {
625 LayoutMultiColumnSetList::iterator it = m_multiColumnSetList.find(nextSet);
626 ASSERT(it != m_multiColumnSetList.end());
627 m_multiColumnSetList.insertBefore(it, columnSet);
628 } else {
629 m_multiColumnSetList.add(columnSet);
633 void LayoutMultiColumnFlowThread::willBeRemovedFromTree()
635 // Detach all column sets from the flow thread. Cannot destroy them at this point, since they
636 // are siblings of this object, and there may be pointers to this object's sibling somewhere
637 // further up on the call stack.
638 for (LayoutMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet())
639 columnSet->detachFromFlowThread();
640 multiColumnBlockFlow()->resetMultiColumnFlowThread();
641 LayoutFlowThread::willBeRemovedFromTree();
644 void LayoutMultiColumnFlowThread::skipColumnSpanner(LayoutBox* layoutObject, LayoutUnit logicalTopInFlowThread)
646 ASSERT(layoutObject->isColumnSpanAll());
647 LayoutMultiColumnSpannerPlaceholder* placeholder = layoutObject->spannerPlaceholder();
648 LayoutBox* previousColumnBox = placeholder->previousSiblingMultiColumnBox();
649 if (previousColumnBox && previousColumnBox->isLayoutMultiColumnSet()) {
650 LayoutMultiColumnSet* columnSet = toLayoutMultiColumnSet(previousColumnBox);
651 if (logicalTopInFlowThread < columnSet->logicalTopInFlowThread())
652 logicalTopInFlowThread = columnSet->logicalTopInFlowThread(); // Negative margins may cause this.
653 columnSet->endFlow(logicalTopInFlowThread);
655 LayoutBox* nextColumnBox = placeholder->nextSiblingMultiColumnBox();
656 if (nextColumnBox && nextColumnBox->isLayoutMultiColumnSet()) {
657 LayoutMultiColumnSet* nextSet = toLayoutMultiColumnSet(nextColumnBox);
658 m_lastSetWorkedOn = nextSet;
659 nextSet->beginFlow(logicalTopInFlowThread);
662 // We'll lay out of spanners after flow thread layout has finished (during layout of the spanner
663 // placeholders). There may be containing blocks for out-of-flow positioned descendants of the
664 // spanner in the flow thread, so that out-of-flow objects inside the spanner will be laid out
665 // as part of flow thread layout (even if the spanner itself won't). We need to add such
666 // out-of-flow positioned objects to their containing blocks now, or they'll never get laid
667 // out. Since it's non-trivial to determine if we need this, and where such out-of-flow objects
668 // might be, just go through the whole subtree.
669 for (LayoutObject* descendant = layoutObject->slowFirstChild(); descendant; descendant = descendant->nextInPreOrder()) {
670 if (descendant->isBox() && descendant->isOutOfFlowPositioned())
671 descendant->containingBlock()->insertPositionedObject(toLayoutBox(descendant));
675 // When processing layout objects to remove or when processing layout objects that have just been
676 // inserted, certain types of objects should be skipped.
677 static bool shouldSkipInsertedOrRemovedChild(LayoutMultiColumnFlowThread* flowThread, const LayoutObject& child)
679 if (child.isSVG() && !child.isSVGRoot()) {
680 // Don't descend into SVG objects. What's in there is of no interest, and there might even
681 // be a foreignObject there with column-span:all, which doesn't apply to us.
682 return true;
684 if (child.isLayoutFlowThread()) {
685 // Found an inner flow thread. We need to skip it and its descendants.
686 return true;
688 if (child.isLayoutMultiColumnSet() || child.isLayoutMultiColumnSpannerPlaceholder()) {
689 // Column sets and spanner placeholders in a child multicol context don't affect the parent
690 // flow thread.
691 return true;
693 if (child.isOutOfFlowPositioned() && child.containingBlock()->flowThreadContainingBlock() != flowThread) {
694 // Out-of-flow with its containing block on the outside of the multicol container.
695 return true;
697 return false;
700 void LayoutMultiColumnFlowThread::flowThreadDescendantWasInserted(LayoutObject* descendant)
702 ASSERT(!m_isBeingEvacuated);
703 // This method ensures that the list of column sets and spanner placeholders reflects the
704 // multicol content after having inserted a descendant (or descendant subtree). See the header
705 // file for more information. Go through the subtree that was just inserted and create column
706 // sets (needed by regular column content) and spanner placeholders (one needed by each spanner)
707 // where needed.
708 if (shouldSkipInsertedOrRemovedChild(this, *descendant))
709 return;
710 LayoutObject* objectAfterSubtree = nextInPreOrderAfterChildrenSkippingOutOfFlow(this, descendant);
711 LayoutObject* next;
712 for (LayoutObject* layoutObject = descendant; layoutObject; layoutObject = next) {
713 if (layoutObject != descendant && shouldSkipInsertedOrRemovedChild(this, *layoutObject)) {
714 next = layoutObject->nextInPreOrderAfterChildren(descendant);
715 continue;
717 next = layoutObject->nextInPreOrder(descendant);
718 if (containingColumnSpannerPlaceholder(layoutObject))
719 continue; // Inside a column spanner. Nothing to do, then.
720 if (descendantIsValidColumnSpanner(layoutObject)) {
721 // This layoutObject is a spanner, so it needs to establish a spanner placeholder.
722 createAndInsertSpannerPlaceholder(toLayoutBox(layoutObject), objectAfterSubtree);
723 continue;
725 // This layoutObject is regular column content (i.e. not a spanner). Create a set if necessary.
726 if (objectAfterSubtree) {
727 if (LayoutMultiColumnSpannerPlaceholder* placeholder = objectAfterSubtree->spannerPlaceholder()) {
728 // If inserted right before a spanner, we need to make sure that there's a set for us there.
729 LayoutBox* previous = placeholder->previousSiblingMultiColumnBox();
730 if (!previous || !previous->isLayoutMultiColumnSet())
731 createAndInsertMultiColumnSet(placeholder);
732 } else {
733 // Otherwise, since |objectAfterSubtree| isn't a spanner, it has to mean that there's
734 // already a set for that content. We can use it for this layoutObject too.
735 ASSERT(mapDescendantToColumnSet(objectAfterSubtree));
736 ASSERT(mapDescendantToColumnSet(layoutObject) == mapDescendantToColumnSet(objectAfterSubtree));
738 } else {
739 // Inserting at the end. Then we just need to make sure that there's a column set at the end.
740 LayoutBox* lastColumnBox = lastMultiColumnBox();
741 if (!lastColumnBox || !lastColumnBox->isLayoutMultiColumnSet())
742 createAndInsertMultiColumnSet();
747 void LayoutMultiColumnFlowThread::flowThreadDescendantWillBeRemoved(LayoutObject* descendant)
749 // This method ensures that the list of column sets and spanner placeholders reflects the
750 // multicol content that we'll be left with after removal of a descendant (or descendant
751 // subtree). See the header file for more information. Removing content may mean that we need to
752 // remove column sets and/or spanner placeholders.
753 if (m_isBeingEvacuated)
754 return;
755 if (shouldSkipInsertedOrRemovedChild(this, *descendant))
756 return;
757 bool hadContainingPlaceholder = containingColumnSpannerPlaceholder(descendant);
758 bool processedSomething = false;
759 LayoutObject* next;
760 // Remove spanner placeholders that are no longer needed, and merge column sets around them.
761 for (LayoutObject* layoutObject = descendant; layoutObject; layoutObject = next) {
762 if (layoutObject != descendant && shouldSkipInsertedOrRemovedChild(this, *layoutObject)) {
763 next = layoutObject->nextInPreOrderAfterChildren(descendant);
764 continue;
766 processedSomething = true;
767 LayoutMultiColumnSpannerPlaceholder* placeholder = layoutObject->spannerPlaceholder();
768 if (!placeholder) {
769 next = layoutObject->nextInPreOrder(descendant);
770 continue;
772 next = layoutObject->nextInPreOrderAfterChildren(descendant); // It's a spanner. Its children are of no interest to us.
773 destroySpannerPlaceholder(placeholder);
775 if (hadContainingPlaceholder || !processedSomething)
776 return; // No column content will be removed, so we can stop here.
778 // Column content will be removed. Does this mean that we should destroy a column set?
779 LayoutMultiColumnSpannerPlaceholder* adjacentPreviousSpannerPlaceholder = nullptr;
780 LayoutObject* previousLayoutObject = previousInPreOrderSkippingOutOfFlow(this, descendant);
781 if (previousLayoutObject && previousLayoutObject != this) {
782 adjacentPreviousSpannerPlaceholder = containingColumnSpannerPlaceholder(previousLayoutObject);
783 if (!adjacentPreviousSpannerPlaceholder)
784 return; // Preceded by column content. Set still needed.
786 LayoutMultiColumnSpannerPlaceholder* adjacentNextSpannerPlaceholder = nullptr;
787 LayoutObject* nextLayoutObject = nextInPreOrderAfterChildrenSkippingOutOfFlow(this, descendant);
788 if (nextLayoutObject) {
789 adjacentNextSpannerPlaceholder = containingColumnSpannerPlaceholder(nextLayoutObject);
790 if (!adjacentNextSpannerPlaceholder)
791 return; // Followed by column content. Set still needed.
793 // We have now determined that, with the removal of |descendant|, we should remove a column
794 // set. Locate it and remove it. Do it without involving mapDescendantToColumnSet(), as that
795 // might be very slow. Deduce the right set from the spanner placeholders that we've already
796 // found.
797 LayoutMultiColumnSet* columnSetToRemove;
798 if (adjacentNextSpannerPlaceholder) {
799 columnSetToRemove = toLayoutMultiColumnSet(adjacentNextSpannerPlaceholder->previousSiblingMultiColumnBox());
800 ASSERT(!adjacentPreviousSpannerPlaceholder || columnSetToRemove == adjacentPreviousSpannerPlaceholder->nextSiblingMultiColumnBox());
801 } else if (adjacentPreviousSpannerPlaceholder) {
802 columnSetToRemove = toLayoutMultiColumnSet(adjacentPreviousSpannerPlaceholder->nextSiblingMultiColumnBox());
803 } else {
804 // If there were no adjacent spanners, it has to mean that there's only one column set,
805 // since it's only spanners that may cause creation of multiple sets.
806 columnSetToRemove = firstMultiColumnSet();
807 ASSERT(columnSetToRemove);
808 ASSERT(!columnSetToRemove->nextSiblingMultiColumnSet());
810 ASSERT(columnSetToRemove);
811 columnSetToRemove->destroy();
814 static inline bool needsToReinsertIntoFlowThread(const ComputedStyle& oldStyle, const ComputedStyle& newStyle)
816 // If we've become (or are about to become) a container for absolutely positioned descendants,
817 // or if we're no longer going to be one, we need to re-evaluate the need for column
818 // sets. There may be out-of-flow descendants further down that become part of the flow thread,
819 // or cease to be part of the flow thread, because of this change.
820 if (oldStyle.hasTransformRelatedProperty() != newStyle.hasTransformRelatedProperty())
821 return true;
822 return (oldStyle.hasInFlowPosition() && newStyle.position() == StaticPosition)
823 || (newStyle.hasInFlowPosition() && oldStyle.position() == StaticPosition);
826 static inline bool needsToRemoveFromFlowThread(const ComputedStyle& oldStyle, const ComputedStyle& newStyle)
828 // If an in-flow descendant goes out-of-flow, we may have to remove column sets and spanner placeholders.
829 return (newStyle.hasOutOfFlowPosition() && !oldStyle.hasOutOfFlowPosition()) || needsToReinsertIntoFlowThread(oldStyle, newStyle);
832 static inline bool needsToInsertIntoFlowThread(const ComputedStyle& oldStyle, const ComputedStyle& newStyle)
834 // If an out-of-flow descendant goes in-flow, we may have to insert column sets and spanner placeholders.
835 return (!newStyle.hasOutOfFlowPosition() && oldStyle.hasOutOfFlowPosition()) || needsToReinsertIntoFlowThread(oldStyle, newStyle);
838 void LayoutMultiColumnFlowThread::flowThreadDescendantStyleWillChange(LayoutBox* descendant, StyleDifference diff, const ComputedStyle& newStyle)
840 if (needsToRemoveFromFlowThread(descendant->styleRef(), newStyle))
841 flowThreadDescendantWillBeRemoved(descendant);
844 void LayoutMultiColumnFlowThread::flowThreadDescendantStyleDidChange(LayoutBox* descendant, StyleDifference diff, const ComputedStyle& oldStyle)
846 if (needsToInsertIntoFlowThread(oldStyle, descendant->styleRef())) {
847 flowThreadDescendantWasInserted(descendant);
848 return;
850 if (descendantIsValidColumnSpanner(descendant)) {
851 // We went from being regular column content to becoming a spanner.
852 ASSERT(!descendant->spannerPlaceholder());
854 // First remove this as regular column content. Note that this will walk the entire subtree
855 // of |descendant|. There might be spanners there (which won't be spanners anymore, since
856 // we're not allowed to nest spanners), whose placeholders must die.
857 flowThreadDescendantWillBeRemoved(descendant);
859 createAndInsertSpannerPlaceholder(descendant, nextInPreOrderAfterChildrenSkippingOutOfFlow(this, descendant));
863 void LayoutMultiColumnFlowThread::computePreferredLogicalWidths()
865 LayoutFlowThread::computePreferredLogicalWidths();
867 // The min/max intrinsic widths calculated really tell how much space elements need when
868 // laid out inside the columns. In order to eventually end up with the desired column width,
869 // we need to convert them to values pertaining to the multicol container.
870 const LayoutBlockFlow* multicolContainer = multiColumnBlockFlow();
871 const ComputedStyle* multicolStyle = multicolContainer->style();
872 int columnCount = multicolStyle->hasAutoColumnCount() ? 1 : multicolStyle->columnCount();
873 LayoutUnit columnWidth;
874 LayoutUnit gapExtra = (columnCount - 1) * multicolContainer->columnGap();
875 if (multicolStyle->hasAutoColumnWidth()) {
876 m_minPreferredLogicalWidth = m_minPreferredLogicalWidth * columnCount + gapExtra;
877 } else {
878 columnWidth = multicolStyle->columnWidth();
879 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, columnWidth);
881 // Note that if column-count is auto here, we should resolve it to calculate the maximum
882 // intrinsic width, instead of pretending that it's 1. The only way to do that is by performing
883 // a layout pass, but this is not an appropriate time or place for layout. The good news is that
884 // if height is unconstrained and there are no explicit breaks, the resolved column-count really
885 // should be 1.
886 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, columnWidth) * columnCount + gapExtra;
889 void LayoutMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
891 // We simply remain at our intrinsic height.
892 computedValues.m_extent = logicalHeight;
893 computedValues.m_position = logicalTop;
896 void LayoutMultiColumnFlowThread::updateLogicalWidth()
898 LayoutUnit columnWidth;
899 calculateColumnCountAndWidth(columnWidth, m_columnCount);
900 setLogicalWidth(columnWidth);
903 void LayoutMultiColumnFlowThread::layout()
905 ASSERT(!m_lastSetWorkedOn);
906 m_lastSetWorkedOn = firstMultiColumnSet();
907 if (m_lastSetWorkedOn)
908 m_lastSetWorkedOn->beginFlow(LayoutUnit());
909 LayoutFlowThread::layout();
910 if (LayoutMultiColumnSet* lastSet = lastMultiColumnSet()) {
911 ASSERT(lastSet == m_lastSetWorkedOn);
912 if (!lastSet->nextSiblingMultiColumnBox()) {
913 // Include trailing overflow in the last column set. The idea is that we will generate
914 // additional columns and pages to hold that overflow, since people do write bad content
915 // like <body style="height:0px"> in multi-column layouts.
916 // TODO(mstensho): Once we support nested multicol, adding in overflow here may result
917 // in the need for creating additional rows, since there may not be enough space
918 // remaining in the currently last row.
919 LayoutRect layoutRect = layoutOverflowRect();
920 LayoutUnit logicalBottomInFlowThread = isHorizontalWritingMode() ? layoutRect.maxY() : layoutRect.maxX();
921 ASSERT(logicalBottomInFlowThread >= logicalHeight());
922 lastSet->endFlow(logicalBottomInFlowThread);
925 m_lastSetWorkedOn = nullptr;
928 void LayoutMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage)
930 // Only positive values are interesting (and allowed) here. Zero space shortage may be reported
931 // when we're at the top of a column and the element has zero height. Ignore this, and also
932 // ignore any negative values, which may occur when we set an early break in order to honor
933 // widows in the next column.
934 if (spaceShortage <= 0)
935 return;
937 if (LayoutMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset))
938 multicolSet->recordSpaceShortage(offset, spaceShortage);
941 void LayoutMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight)
943 if (LayoutMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset))
944 multicolSet->updateMinimumColumnHeight(offset, minHeight);
947 bool LayoutMultiColumnFlowThread::addForcedColumnBreak(LayoutUnit offset, LayoutObject* /*breakChild*/, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment)
949 if (LayoutMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset)) {
950 multicolSet->addContentRun(offset);
951 if (offsetBreakAdjustment)
952 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRemainingLogicalHeightForOffset(offset, AssociateWithFormerPage) : LayoutUnit();
953 return true;
955 return false;