void RenderMultiColumnFlowThread::layout() { ASSERT(!m_inLayout); m_inLayout = true; m_lastSetWorkedOn = nullptr; if (RenderBox* first = firstColumnSetOrSpanner()) { if (first->isRenderMultiColumnSet()) { m_lastSetWorkedOn = toRenderMultiColumnSet(first); m_lastSetWorkedOn->beginFlow(this); } } RenderFlowThread::layout(); if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) { if (!nextColumnSetOrSpannerSiblingOf(lastSet)) lastSet->endFlow(this, logicalHeight()); lastSet->expandToEncompassFlowThreadContentsIfNeeded(); } m_inLayout = false; m_lastSetWorkedOn = nullptr; }
void LayoutMultiColumnFlowThread::layout() { ASSERT(!m_lastSetWorkedOn); m_lastSetWorkedOn = firstMultiColumnSet(); if (m_lastSetWorkedOn) m_lastSetWorkedOn->beginFlow(LayoutUnit()); LayoutFlowThread::layout(); if (LayoutMultiColumnSet* lastSet = lastMultiColumnSet()) { ASSERT(lastSet == m_lastSetWorkedOn); if (!lastSet->nextSiblingMultiColumnBox()) { // Include trailing overflow in the last column set. The idea is that we will generate // additional columns and pages to hold that overflow, since people do write bad content // like <body style="height:0px"> in multi-column layouts. // TODO(mstensho): Once we support nested multicol, adding in overflow here may result // in the need for creating additional rows, since there may not be enough space // remaining in the currently last row. LayoutRect layoutRect = layoutOverflowRect(); LayoutUnit logicalBottomInFlowThread = isHorizontalWritingMode() ? layoutRect.maxY() : layoutRect.maxX(); ASSERT(logicalBottomInFlowThread >= logicalHeight()); lastSet->endFlow(logicalBottomInFlowThread); } } m_lastSetWorkedOn = nullptr; }
bool LayoutMultiColumnFlowThread::isPageLogicalHeightKnown() const { if (LayoutMultiColumnSet* columnSet = lastMultiColumnSet()) return columnSet->isPageLogicalHeightKnown(); return false; }
bool RenderMultiColumnFlowThread::isPageLogicalHeightKnown() const { if (RenderMultiColumnSet* columnSet = lastMultiColumnSet()) return columnSet->computedColumnHeight(); return false; }
void RenderMultiColumnFlowThread::flowThreadDescendantInserted(RenderObject* descendant) { if (gShiftingSpanner || m_beingEvacuated || descendant->isInFlowRenderFlowThread()) return; RenderObject* subtreeRoot = descendant; for (; descendant; descendant = (descendant ? descendant->nextInPreOrder(subtreeRoot) : nullptr)) { if (descendant->isRenderMultiColumnSpannerPlaceholder()) { // A spanner's placeholder has been inserted. The actual spanner renderer is moved from // where it would otherwise occur (if it weren't a spanner) to becoming a sibling of the // column sets. RenderMultiColumnSpannerPlaceholder* placeholder = toRenderMultiColumnSpannerPlaceholder(descendant); if (placeholder->flowThread() != this) { // This isn't our spanner! It shifted here from an ancestor multicolumn block. It's going to end up // becoming our spanner instead, but for it to do that we first have to nuke the original spanner, // and get the spanner content back into this flow thread. RenderBox* spanner = placeholder->spanner(); // Get info for the move of the original content back into our flow thread. RenderBoxModelObject* placeholderParent = toRenderBoxModelObject(placeholder->parent()); // We have to nuke the placeholder, since the ancestor already lost the mapping to it when // we shifted the placeholder down into this flow thread. RenderObject* placeholderNextSibling = placeholderParent->removeChild(*placeholder); // Get the ancestor multicolumn flow thread to clean up its mess. RenderBlockFlow* ancestorBlock = toRenderBlockFlow(spanner->parent()); ancestorBlock->multiColumnFlowThread()->flowThreadRelativeWillBeRemoved(spanner); // Now move the original content into our flow thread. It will end up calling flowThreadDescendantInserted // on the new content only, and everything will get set up properly. ancestorBlock->moveChildTo(placeholderParent, spanner, placeholderNextSibling, true); // Advance descendant. descendant = placeholderNextSibling; // If the spanner was the subtree root, then we're done, since there is nothing else left to insert. if (!descendant) return; // Now that we have done this, we can continue past the spanning content, since we advanced // descendant already. if (descendant) descendant = descendant->previousInPreOrder(subtreeRoot); continue; } ASSERT(!m_spannerMap.get(placeholder->spanner())); m_spannerMap.add(placeholder->spanner(), placeholder); ASSERT(!placeholder->firstChild()); // There should be no children here, but if there are, we ought to skip them. continue; } RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); RenderObject* nextRendererInFlowThread = descendant->nextInPreOrderAfterChildren(this); RenderObject* insertBeforeMulticolChild = nullptr; if (isValidColumnSpanner(this, descendant)) { // This is a spanner (column-span:all). Such renderers are moved from where they would // otherwise occur in the render tree to becoming a direct child of the multicol container, // so that they live among the column sets. This simplifies the layout implementation, and // basically just relies on regular block layout done by the RenderBlockFlow that // establishes the multicol container. RenderBlockFlow* container = toRenderBlockFlow(descendant->parent()); RenderMultiColumnSet* setToSplit = nullptr; if (nextRendererInFlowThread) { setToSplit = findSetRendering(descendant); if (setToSplit) { setToSplit->setNeedsLayout(); insertBeforeMulticolChild = setToSplit->nextSibling(); } } // Moving a spanner's renderer so that it becomes a sibling of the column sets requires us // to insert an anonymous placeholder in the tree where the spanner's renderer otherwise // would have been. This is needed for a two reasons: We need a way of separating inline // content before and after the spanner, so that it becomes separate line boxes. Secondly, // this placeholder serves as a break point for column sets, so that, when encountered, we // end flowing one column set and move to the next one. RenderMultiColumnSpannerPlaceholder* placeholder = RenderMultiColumnSpannerPlaceholder::createAnonymous(this, toRenderBox(descendant), &container->style()); container->addChild(placeholder, descendant->nextSibling()); container->removeChild(*descendant); // This is a guard to stop an ancestor flow thread from processing the spanner. gShiftingSpanner = true; multicolContainer->RenderBlock::addChild(descendant, insertBeforeMulticolChild); gShiftingSpanner = false; // The spanner has now been moved out from the flow thread, but we don't want to // examine its children anyway. They are all part of the spanner and shouldn't trigger // creation of column sets or anything like that. Continue at its original position in // the tree, i.e. where the placeholder was just put. if (subtreeRoot == descendant) subtreeRoot = placeholder; descendant = placeholder; } else { // This is regular multicol content, i.e. not part of a spanner. if (nextRendererInFlowThread && nextRendererInFlowThread->isRenderMultiColumnSpannerPlaceholder()) { // Inserted right before a spanner. Is there a set for us there? RenderMultiColumnSpannerPlaceholder* placeholder = toRenderMultiColumnSpannerPlaceholder(nextRendererInFlowThread); if (RenderObject* previous = placeholder->spanner()->previousSibling()) { if (previous->isRenderMultiColumnSet()) continue; // There's already a set there. Nothing to do. } insertBeforeMulticolChild = placeholder->spanner(); } else if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) { // This child is not an immediate predecessor of a spanner, which means that if this // child precedes a spanner at all, there has to be a column set created for us there // already. If it doesn't precede any spanner at all, on the other hand, we need a // column set at the end of the multicol container. We don't really check here if the // child inserted precedes any spanner or not (as that's an expensive operation). Just // make sure we have a column set at the end. It's no big deal if it remains unused. if (!lastSet->nextSibling()) continue; } } // Need to create a new column set when there's no set already created. We also always insert // another column set after a spanner. Even if it turns out that there are no renderers // following the spanner, there may be bottom margins there, which take up space. RenderMultiColumnSet* newSet = new RenderMultiColumnSet(*this, RenderStyle::createAnonymousStyleWithDisplay(&multicolContainer->style(), BLOCK)); newSet->initializeStyle(); multicolContainer->RenderBlock::addChild(newSet, insertBeforeMulticolChild); invalidateRegions(); // We cannot handle immediate column set siblings at the moment (and there's no need for // it, either). There has to be at least one spanner separating them. ASSERT(!previousColumnSetOrSpannerSiblingOf(newSet) || !previousColumnSetOrSpannerSiblingOf(newSet)->isRenderMultiColumnSet()); ASSERT(!nextColumnSetOrSpannerSiblingOf(newSet) || !nextColumnSetOrSpannerSiblingOf(newSet)->isRenderMultiColumnSet()); } }