| /* | 
 |  * Copyright (C) 2012 Apple Inc.  All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * 1. Redistributions of source code must retain the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer. | 
 |  * 2. Redistributions in binary form must reproduce the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer in the | 
 |  *    documentation and/or other materials provided with the distribution. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 
 |  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
 |  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR | 
 |  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 
 |  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 
 |  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 
 |  * PROFITS; OR BUSINESS IN..0TERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 
 |  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
 |  * OF THIS SOFTWARE, EVEN IF  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include "config.h" | 
 | #include "RenderMultiColumnFlowThread.h" | 
 |  | 
 | #include "HitTestResult.h" | 
 | #include "LayoutState.h" | 
 | #include "RenderIterator.h" | 
 | #include "RenderMultiColumnSet.h" | 
 | #include "RenderMultiColumnSpannerPlaceholder.h" | 
 | #include "RenderView.h" | 
 | #include "TransformState.h" | 
 |  | 
 | namespace WebCore { | 
 |  | 
 | bool RenderMultiColumnFlowThread::gShiftingSpanner = false; | 
 |  | 
 | RenderMultiColumnFlowThread::RenderMultiColumnFlowThread(Document& document, PassRef<RenderStyle> style) | 
 |     : RenderFlowThread(document, WTF::move(style)) | 
 |     , m_lastSetWorkedOn(nullptr) | 
 |     , m_columnCount(1) | 
 |     , m_columnWidth(0) | 
 |     , m_columnHeightAvailable(0) | 
 |     , m_inLayout(false) | 
 |     , m_inBalancingPass(false) | 
 |     , m_needsHeightsRecalculation(false) | 
 |     , m_progressionIsInline(false) | 
 |     , m_progressionIsReversed(false) | 
 |     , m_beingEvacuated(false) | 
 | { | 
 |     setFlowThreadState(InsideInFlowThread); | 
 | } | 
 |  | 
 | RenderMultiColumnFlowThread::~RenderMultiColumnFlowThread() | 
 | { | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::removeFlowChildInfo(RenderObject* child) | 
 | { | 
 |     RenderFlowThread::removeFlowChildInfo(child); | 
 |     flowThreadRelativeWillBeRemoved(child); | 
 | } | 
 |  | 
 | const char* RenderMultiColumnFlowThread::renderName() const | 
 | {     | 
 |     return "RenderMultiColumnFlowThread"; | 
 | } | 
 |  | 
 | RenderMultiColumnSet* RenderMultiColumnFlowThread::firstMultiColumnSet() const | 
 | { | 
 |     for (RenderObject* sibling = nextSibling(); sibling; sibling = sibling->nextSibling()) { | 
 |         if (sibling->isRenderMultiColumnSet()) | 
 |             return toRenderMultiColumnSet(sibling); | 
 |     } | 
 |     return nullptr; | 
 | } | 
 |  | 
 | RenderMultiColumnSet* RenderMultiColumnFlowThread::lastMultiColumnSet() const | 
 | { | 
 |     for (RenderObject* sibling = multiColumnBlockFlow()->lastChild(); sibling; sibling = sibling->previousSibling()) { | 
 |         if (sibling->isRenderMultiColumnSet()) | 
 |             return toRenderMultiColumnSet(sibling); | 
 |     } | 
 |     return nullptr; | 
 | } | 
 |  | 
 | RenderBox* RenderMultiColumnFlowThread::firstColumnSetOrSpanner() const | 
 | { | 
 |     if (RenderObject* sibling = nextSibling()) { | 
 |         ASSERT(sibling->isBox()); | 
 |         ASSERT(sibling->isRenderMultiColumnSet() || findColumnSpannerPlaceholder(toRenderBox(sibling))); | 
 |         return toRenderBox(sibling); | 
 |     } | 
 |     return nullptr; | 
 | } | 
 |  | 
 | RenderBox* RenderMultiColumnFlowThread::nextColumnSetOrSpannerSiblingOf(const RenderBox* child) | 
 | { | 
 |     if (!child) | 
 |         return nullptr; | 
 |     if (RenderObject* sibling = child->nextSibling()) { | 
 |         ASSERT(sibling->isBox()); | 
 |         return toRenderBox(sibling); | 
 |     } | 
 |     return nullptr; | 
 | } | 
 |  | 
 | RenderBox* RenderMultiColumnFlowThread::previousColumnSetOrSpannerSiblingOf(const RenderBox* child) | 
 | { | 
 |     if (!child) | 
 |         return nullptr; | 
 |     if (RenderObject* sibling = child->previousSibling()) { | 
 |         ASSERT(sibling->isBox()); | 
 |         if (sibling->isRenderFlowThread()) | 
 |             return nullptr; | 
 |         return toRenderBox(sibling); | 
 |     } | 
 |     return nullptr; | 
 | } | 
 |  | 
 | 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; | 
 | } | 
 |  | 
 | RenderMultiColumnSet* RenderMultiColumnFlowThread::findSetRendering(RenderObject* renderer) const | 
 | { | 
 |     for (RenderMultiColumnSet* multicolSet = firstMultiColumnSet(); multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) { | 
 |         if (multicolSet->containsRendererInFlowThread(renderer)) | 
 |             return multicolSet; | 
 |     } | 
 |     return nullptr; | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::populate() | 
 | { | 
 |     RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); | 
 |     ASSERT(!nextSibling()); | 
 |     // Reparent children preceding the flow thread into the flow thread. It's multicol content | 
 |     // now. At this point there's obviously nothing after the flow thread, but renderers (column | 
 |     // sets and spanners) will be inserted there as we insert elements into the flow thread. | 
 |     LayoutStateDisabler layoutStateDisabler(&view()); | 
 |     multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), this, true); | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::evacuateAndDestroy() | 
 | { | 
 |     RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); | 
 |     m_beingEvacuated = true; | 
 |      | 
 |     // Delete the line box tree. | 
 |     deleteLines(); | 
 |      | 
 |     LayoutStateDisabler layoutStateDisabler(&view()); | 
 |  | 
 |     // First promote all children of the flow thread. 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->setMultiColumnFlowThread(nullptr); | 
 |     moveAllChildrenTo(multicolContainer, true); | 
 |  | 
 |     // Move spanners back to their original DOM position in the tree, and destroy the placeholders. | 
 |     SpannerMap::iterator it; | 
 |     while ((it = m_spannerMap.begin()) != m_spannerMap.end()) { | 
 |         RenderBox* spanner = it->key; | 
 |         RenderMultiColumnSpannerPlaceholder* placeholder = it->value; | 
 |         RenderBlockFlow* originalContainer = toRenderBlockFlow(placeholder->parent()); | 
 |         multicolContainer->removeChild(*spanner); | 
 |         originalContainer->addChild(spanner, placeholder); | 
 |         placeholder->destroy(); | 
 |         m_spannerMap.remove(it); | 
 |     } | 
 |  | 
 |     // Remove all sets. | 
 |     while (RenderMultiColumnSet* columnSet = firstMultiColumnSet()) | 
 |         columnSet->destroy(); | 
 |      | 
 |     destroy(); | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::addRegionToThread(RenderRegion* renderRegion) | 
 | { | 
 |     RenderMultiColumnSet* columnSet = toRenderMultiColumnSet(renderRegion); | 
 |     if (RenderMultiColumnSet* nextSet = columnSet->nextSiblingMultiColumnSet()) { | 
 |         RenderRegionList::iterator it = m_regionList.find(nextSet); | 
 |         ASSERT(it != m_regionList.end()); | 
 |         m_regionList.insertBefore(it, columnSet); | 
 |     } else | 
 |         m_regionList.add(columnSet); | 
 |     renderRegion->setIsValid(true); | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::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 (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) | 
 |         columnSet->detachRegion(); | 
 |     multiColumnBlockFlow()->setMultiColumnFlowThread(nullptr); | 
 |     RenderFlowThread::willBeRemovedFromTree(); | 
 | } | 
 |  | 
 | RenderObject* RenderMultiColumnFlowThread::resolveMovedChild(RenderObject* child) const | 
 | { | 
 |     if (child->style().columnSpan() != ColumnSpanAll || !child->isBox()) { | 
 |         // We only need to resolve for column spanners. | 
 |         return child; | 
 |     } | 
 |     // The renderer for the actual DOM node that establishes a spanner is moved from its original | 
 |     // location in the render tree to becoming a sibling of the column sets. In other words, it's | 
 |     // moved out from the flow thread (and becomes a sibling of it). When we for instance want to | 
 |     // create and insert a renderer for the sibling node immediately preceding the spanner, we need | 
 |     // to map that spanner renderer to the spanner's placeholder, which is where the new inserted | 
 |     // renderer belongs. | 
 |     if (RenderMultiColumnSpannerPlaceholder* placeholder = findColumnSpannerPlaceholder(toRenderBox(child))) | 
 |         return placeholder; | 
 |  | 
 |     // This is an invalid spanner, or its placeholder hasn't been created yet. This happens when | 
 |     // moving an entire subtree into the flow thread, when we are processing the insertion of this | 
 |     // spanner's preceding sibling, and we obviously haven't got as far as processing this spanner | 
 |     // yet. | 
 |     return child; | 
 | } | 
 |  | 
 | static bool isValidColumnSpanner(RenderMultiColumnFlowThread* flowThread, RenderObject* descendant) | 
 | { | 
 |     // We assume that we're inside the flow thread. This function is not to be called otherwise. | 
 |     ASSERT(descendant->isDescendantOf(flowThread)); | 
 |  | 
 |     // First make sure that the renderer itself has the right properties for becoming a spanner. | 
 |     RenderStyle& style = descendant->style(); | 
 |     if (style.columnSpan() != ColumnSpanAll || !descendant->isBox() || descendant->isFloatingOrOutOfFlowPositioned()) | 
 |         return false; | 
 |  | 
 |     RenderElement* container = descendant->parent(); | 
 |     if (!container->isRenderBlockFlow() || container->childrenInline()) { | 
 |         // Needs to be block-level. | 
 |         return false; | 
 |     } | 
 |      | 
 |     // We need to have the flow thread as the containing block. A spanner cannot break out of the flow thread. | 
 |     RenderFlowThread* enclosingFlowThread = descendant->flowThreadContainingBlock(); | 
 |     if (enclosingFlowThread != flowThread) | 
 |         return false; | 
 |  | 
 |     // This looks like a spanner, but if we're inside something unbreakable, it's not to be treated as one. | 
 |     for (RenderBox* ancestor = toRenderBox(descendant)->containingBlock(); ancestor; ancestor = ancestor->containingBlock()) { | 
 |         if (ancestor->isRenderFlowThread()) { | 
 |             // Don't allow any intervening non-multicol fragmentation contexts. The spec doesn't say | 
 |             // anything about disallowing this, but it's just going to be too complicated to | 
 |             // implement (not to mention specify behavior). | 
 |             return ancestor == flowThread; | 
 |         } | 
 |         ASSERT(ancestor->style().columnSpan() != ColumnSpanAll || !isValidColumnSpanner(flowThread, ancestor)); | 
 |         if (ancestor->isUnsplittableForPagination()) | 
 |             return false; | 
 |     } | 
 |     ASSERT_NOT_REACHED(); | 
 |     return false; | 
 | } | 
 |  | 
 | RenderObject* RenderMultiColumnFlowThread::processPossibleSpannerDescendant(RenderObject*& subtreeRoot, RenderObject* descendant) | 
 | { | 
 |     RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); | 
 |     RenderObject* nextRendererInFlowThread = descendant->nextInPreOrderAfterChildren(this); | 
 |     RenderObject* insertBeforeMulticolChild = nullptr; | 
 |     RenderObject* nextDescendant = descendant; | 
 |  | 
 |     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; | 
 |         nextDescendant = 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()) | 
 |                     return nextDescendant; // 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()) | 
 |                 return nextDescendant; | 
 |         } | 
 |     } | 
 |     // 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()); | 
 |      | 
 |     return nextDescendant; | 
 | } | 
 |  | 
 | 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(); | 
 |                  | 
 |                 // Insert after the placeholder, but don't let a notification happen. | 
 |                 gShiftingSpanner = true; | 
 |                 RenderBlockFlow* ancestorBlock = toRenderBlockFlow(spanner->parent()); | 
 |                 ancestorBlock->moveChildTo(placeholder->parentBox(), spanner, placeholder->nextSibling(), true); | 
 |                 gShiftingSpanner = false; | 
 |                  | 
 |                 // 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. | 
 |                 if (subtreeRoot == descendant) | 
 |                     subtreeRoot = spanner; | 
 |                 placeholder->parent()->removeChild(*placeholder); | 
 |  | 
 |                 // Now we process the spanner. | 
 |                 descendant = processPossibleSpannerDescendant(subtreeRoot, spanner); | 
 |                 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; | 
 |         } | 
 |          | 
 |         descendant = processPossibleSpannerDescendant(subtreeRoot, descendant); | 
 |     } | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::handleSpannerRemoval(RenderObject* spanner) | 
 | { | 
 |      // The placeholder may already have been removed, but if it hasn't, do so now. | 
 |     if (RenderMultiColumnSpannerPlaceholder* placeholder = m_spannerMap.get(toRenderBox(spanner))) { | 
 |         placeholder->parent()->removeChild(*placeholder); | 
 |         m_spannerMap.remove(toRenderBox(spanner)); | 
 |     } | 
 |  | 
 |     if (RenderObject* next = spanner->nextSibling()) { | 
 |         if (RenderObject* previous = spanner->previousSibling()) { | 
 |             if (previous->isRenderMultiColumnSet() && next->isRenderMultiColumnSet()) { | 
 |                 // Merge two sets that no longer will be separated by a spanner. | 
 |                 next->destroy(); | 
 |                 previous->setNeedsLayout(); | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::flowThreadRelativeWillBeRemoved(RenderObject* relative) | 
 | { | 
 |     if (m_beingEvacuated) | 
 |         return; | 
 |     invalidateRegions(); | 
 |     if (relative->isRenderMultiColumnSpannerPlaceholder()) { | 
 |         // Remove the map entry for this spanner, but leave the actual spanner renderer alone. Also | 
 |         // keep the reference to the spanner, since the placeholder may be about to be re-inserted | 
 |         // in the tree. | 
 |         ASSERT(relative->isDescendantOf(this)); | 
 |         m_spannerMap.remove(toRenderMultiColumnSpannerPlaceholder(relative)->spanner()); | 
 |         return; | 
 |     } | 
 |     if (relative->style().columnSpan() == ColumnSpanAll) { | 
 |         if (relative->parent() != parent()) | 
 |             return; // not a valid spanner. | 
 |          | 
 |         handleSpannerRemoval(relative); | 
 |     } | 
 |     // Note that we might end up with empty column sets if all column content is removed. That's no | 
 |     // big deal though (and locating them would be expensive), and they will be found and re-used if | 
 |     // content is added again later. | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::flowThreadDescendantBoxLaidOut(RenderBox* descendant) | 
 | { | 
 |     if (!descendant->isRenderMultiColumnSpannerPlaceholder()) | 
 |         return; | 
 |     RenderMultiColumnSpannerPlaceholder* placeholder = toRenderMultiColumnSpannerPlaceholder(descendant); | 
 |     RenderBlock* container = placeholder->containingBlock(); | 
 |  | 
 |     for (RenderBox* prev = previousColumnSetOrSpannerSiblingOf(placeholder->spanner()); prev; prev = previousColumnSetOrSpannerSiblingOf(prev)) { | 
 |         if (prev->isRenderMultiColumnSet()) { | 
 |             toRenderMultiColumnSet(prev)->endFlow(container, placeholder->logicalTop()); | 
 |             break; | 
 |         } | 
 |     } | 
 |  | 
 |     for (RenderBox* next = nextColumnSetOrSpannerSiblingOf(placeholder->spanner()); next; next = nextColumnSetOrSpannerSiblingOf(next)) { | 
 |         if (next->isRenderMultiColumnSet()) { | 
 |             m_lastSetWorkedOn = toRenderMultiColumnSet(next); | 
 |             m_lastSetWorkedOn->beginFlow(container); | 
 |             break; | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const | 
 | { | 
 |     // We simply remain at our intrinsic height. | 
 |     computedValues.m_extent = logicalHeight; | 
 |     computedValues.m_position = logicalTop; | 
 | } | 
 |  | 
 | LayoutUnit RenderMultiColumnFlowThread::initialLogicalWidth() const | 
 | { | 
 |     return columnWidth(); | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::autoGenerateRegionsToBlockOffset(LayoutUnit offset) | 
 | { | 
 |     // This function ensures we have the correct column set information at all times. | 
 |     // For a simple multi-column layout in continuous media, only one column set child is required. | 
 |     // Once a column is nested inside an enclosing pagination context, the number of column sets | 
 |     // required becomes 2n-1, where n is the total number of nested pagination contexts. For example: | 
 |     // | 
 |     // Column layout with no enclosing pagination model = 2 * 1 - 1 = 1 column set. | 
 |     // Columns inside pages = 2 * 2 - 1 = 3 column sets (bottom of first page, all the subsequent pages, then the last page). | 
 |     // Columns inside columns inside pages = 2 * 3 - 1 = 5 column sets. | 
 |     // | 
 |     // In addition, column spans will force a column set to "split" into before/after sets around the spanning element. | 
 |     // | 
 |     // Finally, we will need to deal with columns inside regions. If regions have variable widths, then there will need | 
 |     // to be unique column sets created inside any region whose width is different from its surrounding regions. This is | 
 |     // actually pretty similar to the spanning case, in that we break up the column sets whenever the width varies. | 
 |     // | 
 |     // FIXME: Parent fragmentation contexts might require us to insert additional column sets here, | 
 |     // but we don't worry about it for now. This matches the old multi-column code. Right now our | 
 |     // goal is just feature parity with the old multi-column code so that we can switch over to the | 
 |     // new code as soon as possible. The one column set that we need has already been made during | 
 |     // render tree creation, so there's nothing to do here, for the time being. | 
 |  | 
 |     (void)offset; // hide warning | 
 |     ASSERT(toRenderMultiColumnSet(regionAtBlockOffset(0, offset, true, DisallowRegionAutoGeneration))); | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::setPageBreak(const RenderBlock* block, LayoutUnit offset, LayoutUnit spaceShortage) | 
 | { | 
 |     if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlockOffset(block, offset))) | 
 |         multicolSet->recordSpaceShortage(spaceShortage); | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::updateMinimumPageHeight(const RenderBlock* block, LayoutUnit offset, LayoutUnit minHeight) | 
 | { | 
 |     if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlockOffset(block, offset))) | 
 |         multicolSet->updateMinimumColumnHeight(minHeight); | 
 | } | 
 |  | 
 | RenderRegion* RenderMultiColumnFlowThread::regionAtBlockOffset(const RenderBox* box, LayoutUnit offset, bool extendLastRegion, RegionAutoGenerationPolicy autoGenerationPolicy) | 
 | { | 
 |     if (!m_inLayout) | 
 |         return RenderFlowThread::regionAtBlockOffset(box, offset, extendLastRegion, autoGenerationPolicy); | 
 |  | 
 |     // Layout in progress. We are calculating the set heights as we speak, so the region range | 
 |     // information is not up-to-date. | 
 |  | 
 |     RenderMultiColumnSet* columnSet = m_lastSetWorkedOn ? m_lastSetWorkedOn : firstMultiColumnSet(); | 
 |     if (!columnSet) { | 
 |         // If there's no set, bail. This multicol is empty or only consists of spanners. There | 
 |         // are no regions. | 
 |         return nullptr; | 
 |     } | 
 |     // The last set worked on is a good guess. But if we're not within the bounds, search for the | 
 |     // right one. | 
 |     if (offset < columnSet->logicalTopInFlowThread()) { | 
 |         do { | 
 |             if (RenderMultiColumnSet* prev = columnSet->previousSiblingMultiColumnSet()) | 
 |                 columnSet = prev; | 
 |             else | 
 |                 break; | 
 |         } while (offset < columnSet->logicalTopInFlowThread()); | 
 |     } else { | 
 |         while (offset >= columnSet->logicalBottomInFlowThread()) { | 
 |             RenderMultiColumnSet* next = columnSet->nextSiblingMultiColumnSet(); | 
 |             if (!next || !next->hasBeenFlowed()) | 
 |                 break; | 
 |             columnSet = next; | 
 |         } | 
 |     } | 
 |     return columnSet; | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::setRegionRangeForBox(const RenderBox* box, RenderRegion* startRegion, RenderRegion* endRegion) | 
 | { | 
 |     // Some column sets may have zero height, which means that two or more sets may start at the | 
 |     // exact same flow thread position, which means that some parts of the code may believe that a | 
 |     // given box lives in sets that it doesn't really live in. Make some adjustments here and | 
 |     // include such sets if they are adjacent to the start and/or end regions. | 
 |     for (RenderMultiColumnSet* columnSet = toRenderMultiColumnSet(startRegion)->previousSiblingMultiColumnSet(); columnSet; columnSet = columnSet->previousSiblingMultiColumnSet()) { | 
 |         if (columnSet->logicalHeightInFlowThread()) | 
 |             break; | 
 |         startRegion = columnSet; | 
 |     } | 
 |     for (RenderMultiColumnSet* columnSet = toRenderMultiColumnSet(startRegion)->nextSiblingMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) { | 
 |         if (columnSet->logicalHeightInFlowThread()) | 
 |             break; | 
 |         endRegion = columnSet; | 
 |     } | 
 |  | 
 |     RenderFlowThread::setRegionRangeForBox(box, startRegion, endRegion); | 
 | } | 
 |  | 
 | bool RenderMultiColumnFlowThread::addForcedRegionBreak(const RenderBlock* block, LayoutUnit offset, RenderBox* /*breakChild*/, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment) | 
 | { | 
 |     if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlockOffset(block, offset))) { | 
 |         multicolSet->addForcedBreak(offset); | 
 |         if (offsetBreakAdjustment) | 
 |             *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRemainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit::fromPixel(0); | 
 |         return true; | 
 |     } | 
 |     return false; | 
 | } | 
 |  | 
 | void RenderMultiColumnFlowThread::computeLineGridPaginationOrigin(LayoutState& layoutState) const | 
 | { | 
 |     if (!progressionIsInline()) | 
 |         return; | 
 |      | 
 |     // We need to cache a line grid pagination origin so that we understand how to reset the line grid | 
 |     // at the top of each column. | 
 |     // Get the current line grid and offset. | 
 |     const auto lineGrid = layoutState.lineGrid(); | 
 |     if (!lineGrid) | 
 |         return; | 
 |  | 
 |     // Get the hypothetical line box used to establish the grid. | 
 |     auto lineGridBox = lineGrid->lineGridBox(); | 
 |     if (!lineGridBox) | 
 |         return; | 
 |      | 
 |     bool isHorizontalWritingMode = lineGrid->isHorizontalWritingMode(); | 
 |  | 
 |     LayoutUnit lineGridBlockOffset = isHorizontalWritingMode ? layoutState.lineGridOffset().height() : layoutState.lineGridOffset().width(); | 
 |  | 
 |     // Now determine our position on the grid. Our baseline needs to be adjusted to the nearest baseline multiple | 
 |     // as established by the line box. | 
 |     // FIXME: Need to handle crazy line-box-contain values that cause the root line box to not be considered. I assume | 
 |     // the grid should honor line-box-contain. | 
 |     LayoutUnit gridLineHeight = lineGridBox->lineBottomWithLeading() - lineGridBox->lineTopWithLeading(); | 
 |     if (!gridLineHeight) | 
 |         return; | 
 |  | 
 |     LayoutUnit firstLineTopWithLeading = lineGridBlockOffset + lineGridBox->lineTopWithLeading(); | 
 |      | 
 |     if (layoutState.isPaginated() && layoutState.pageLogicalHeight()) { | 
 |         LayoutUnit pageLogicalTop = isHorizontalWritingMode ? layoutState.pageOffset().height() : layoutState.pageOffset().width(); | 
 |         if (pageLogicalTop > firstLineTopWithLeading) { | 
 |             // Shift to the next highest line grid multiple past the page logical top. Cache the delta | 
 |             // between this new value and the page logical top as the pagination origin. | 
 |             LayoutUnit remainder = roundToInt(pageLogicalTop - firstLineTopWithLeading) % roundToInt(gridLineHeight); | 
 |             LayoutUnit paginationDelta = gridLineHeight - remainder; | 
 |             if (isHorizontalWritingMode) | 
 |                 layoutState.setLineGridPaginationOrigin(LayoutSize(layoutState.lineGridPaginationOrigin().width(), paginationDelta)); | 
 |             else | 
 |                 layoutState.setLineGridPaginationOrigin(LayoutSize(paginationDelta, layoutState.lineGridPaginationOrigin().height())); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | LayoutSize RenderMultiColumnFlowThread::offsetFromContainer(RenderObject* enclosingContainer, const LayoutPoint& physicalPoint, bool* offsetDependsOnPoint) const | 
 | { | 
 |     ASSERT(enclosingContainer == container()); | 
 |  | 
 |     if (offsetDependsOnPoint) | 
 |         *offsetDependsOnPoint = true; | 
 |      | 
 |     LayoutPoint translatedPhysicalPoint(physicalPoint); | 
 |     RenderRegion* region = physicalTranslationFromFlowToRegion(translatedPhysicalPoint); | 
 |     if (region) | 
 |         translatedPhysicalPoint.moveBy(region->topLeftLocation()); | 
 |      | 
 |     LayoutSize offset(translatedPhysicalPoint.x(), translatedPhysicalPoint.y()); | 
 |     if (enclosingContainer->isBox()) | 
 |         offset -= toRenderBox(enclosingContainer)->scrolledContentOffset(); | 
 |     return offset; | 
 | } | 
 |      | 
 | void RenderMultiColumnFlowThread::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const | 
 | { | 
 |     // First get the transform state's point into the block flow thread's physical coordinate space. | 
 |     parent()->mapAbsoluteToLocalPoint(mode, transformState); | 
 |     LayoutPoint transformPoint = roundedLayoutPoint(transformState.mappedPoint()); | 
 |      | 
 |     // Now walk through each region. | 
 |     const RenderMultiColumnSet* candidateColumnSet = nullptr; | 
 |     LayoutPoint candidatePoint; | 
 |     LayoutSize candidateContainerOffset; | 
 |      | 
 |     for (const auto& columnSet : childrenOfType<RenderMultiColumnSet>(*parent())) { | 
 |         candidateContainerOffset = columnSet.offsetFromContainer(parent(), LayoutPoint()); | 
 |          | 
 |         candidatePoint = transformPoint - candidateContainerOffset; | 
 |         candidateColumnSet = &columnSet; | 
 |          | 
 |         // We really have no clue what to do with overflow. We'll just use the closest region to the point in that case. | 
 |         LayoutUnit pointOffset = isHorizontalWritingMode() ? candidatePoint.y() : candidatePoint.x(); | 
 |         LayoutUnit regionOffset = isHorizontalWritingMode() ? columnSet.topLeftLocation().y() : columnSet.topLeftLocation().x(); | 
 |         if (pointOffset < regionOffset + columnSet.logicalHeight()) | 
 |             break; | 
 |     } | 
 |      | 
 |     // Once we have a good guess as to which region we hit tested through (and yes, this was just a heuristic, but it's | 
 |     // the best we could do), then we can map from the region into the flow thread. | 
 |     LayoutSize translationOffset = physicalTranslationFromRegionToFlow(candidateColumnSet, candidatePoint) + candidateContainerOffset; | 
 |     bool preserve3D = mode & UseTransforms && (parent()->style().preserves3D() || style().preserves3D()); | 
 |     if (mode & UseTransforms && shouldUseTransformFromContainer(parent())) { | 
 |         TransformationMatrix t; | 
 |         getTransformFromContainer(parent(), translationOffset, t); | 
 |         transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); | 
 |     } else | 
 |         transformState.move(translationOffset.width(), translationOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); | 
 | } | 
 |  | 
 | LayoutSize RenderMultiColumnFlowThread::physicalTranslationFromRegionToFlow(const RenderMultiColumnSet* columnSet, const LayoutPoint& physicalPoint) const | 
 | { | 
 |     LayoutPoint logicalPoint = columnSet->flipForWritingMode(physicalPoint); | 
 |     LayoutPoint translatedPoint = columnSet->translateRegionPointToFlowThread(logicalPoint); | 
 |     LayoutPoint physicalTranslatedPoint = columnSet->flipForWritingMode(translatedPoint); | 
 |     return physicalPoint - physicalTranslatedPoint; | 
 | } | 
 |  | 
 | RenderRegion* RenderMultiColumnFlowThread::mapFromFlowToRegion(TransformState& transformState) const | 
 | { | 
 |     if (!hasValidRegionInfo()) | 
 |         return nullptr; | 
 |  | 
 |     // Get back into our local flow thread space. | 
 |     LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox(); | 
 |     flipForWritingMode(boxRect); | 
 |  | 
 |     // FIXME: We need to refactor RenderObject::absoluteQuads to be able to split the quads across regions, | 
 |     // for now we just take the center of the mapped enclosing box and map it to a column. | 
 |     LayoutPoint centerPoint = boxRect.center(); | 
 |     LayoutUnit centerLogicalOffset = isHorizontalWritingMode() ? centerPoint.y() : centerPoint.x(); | 
 |     RenderRegion* renderRegion = const_cast<RenderMultiColumnFlowThread*>(this)->regionAtBlockOffset(this, centerLogicalOffset, true, DisallowRegionAutoGeneration); | 
 |     if (!renderRegion) | 
 |         return nullptr; | 
 |     transformState.move(physicalTranslationOffsetFromFlowToRegion(renderRegion, centerLogicalOffset)); | 
 |     return renderRegion; | 
 | } | 
 |  | 
 | LayoutSize RenderMultiColumnFlowThread::physicalTranslationOffsetFromFlowToRegion(const RenderRegion* renderRegion, const LayoutUnit logicalOffset) const | 
 | { | 
 |     // Now that we know which multicolumn set we hit, we need to get the appropriate translation offset for the column. | 
 |     const RenderMultiColumnSet* columnSet = toRenderMultiColumnSet(renderRegion); | 
 |     LayoutPoint translationOffset = columnSet->columnTranslationForOffset(logicalOffset); | 
 |      | 
 |     // Now we know how we want the rect to be translated into the region. At this point we're converting | 
 |     // back to physical coordinates. | 
 |     if (style().isFlippedBlocksWritingMode()) { | 
 |         LayoutRect portionRect(columnSet->flowThreadPortionRect()); | 
 |         LayoutRect columnRect = columnSet->columnRectAt(0); | 
 |         LayoutUnit physicalDeltaFromPortionBottom = logicalHeight() - columnSet->logicalBottomInFlowThread(); | 
 |         if (isHorizontalWritingMode()) | 
 |             columnRect.setHeight(portionRect.height()); | 
 |         else | 
 |             columnRect.setWidth(portionRect.width()); | 
 |         columnSet->flipForWritingMode(columnRect); | 
 |         if (isHorizontalWritingMode()) | 
 |             translationOffset.move(0, columnRect.y() - portionRect.y() - physicalDeltaFromPortionBottom); | 
 |         else | 
 |             translationOffset.move(columnRect.x() - portionRect.x() - physicalDeltaFromPortionBottom, 0); | 
 |     } | 
 |      | 
 |     return LayoutSize(translationOffset.x(), translationOffset.y()); | 
 | } | 
 |  | 
 | RenderRegion* RenderMultiColumnFlowThread::physicalTranslationFromFlowToRegion(LayoutPoint& physicalPoint) const | 
 | { | 
 |     if (!hasValidRegionInfo()) | 
 |         return nullptr; | 
 |      | 
 |     // Put the physical point into the flow thread's coordinate space. | 
 |     LayoutPoint logicalPoint = flipForWritingMode(physicalPoint); | 
 |      | 
 |     // Now get the region that we are in. | 
 |     LayoutUnit logicalOffset = isHorizontalWritingMode() ? logicalPoint.y() : logicalPoint.x(); | 
 |     RenderRegion* renderRegion = const_cast<RenderMultiColumnFlowThread*>(this)->regionAtBlockOffset(this, logicalOffset, true, DisallowRegionAutoGeneration); | 
 |     if (!renderRegion) | 
 |         return nullptr; | 
 |      | 
 |     // Translate to the coordinate space of the region. | 
 |     LayoutSize translationOffset = physicalTranslationOffsetFromFlowToRegion(renderRegion, logicalOffset); | 
 |      | 
 |     // Now shift the physical point into the region's coordinate space. | 
 |     physicalPoint += translationOffset; | 
 |      | 
 |     return renderRegion; | 
 | } | 
 |  | 
 | bool RenderMultiColumnFlowThread::isPageLogicalHeightKnown() const | 
 | { | 
 |     if (RenderMultiColumnSet* columnSet = lastMultiColumnSet()) | 
 |         return columnSet->columnHeightComputed(); | 
 |     return false; | 
 | } | 
 |  | 
 | bool RenderMultiColumnFlowThread::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) | 
 | { | 
 |     // You cannot be inside an in-flow RenderFlowThread without a corresponding DOM node. It's better to | 
 |     // just let the ancestor figure out where we are instead. | 
 |     if (hitTestAction == HitTestBlockBackground) | 
 |         return false; | 
 |     bool inside = RenderFlowThread::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction); | 
 |     if (inside && !result.innerNode()) | 
 |         return false; | 
 |     return inside; | 
 | } | 
 |  | 
 | bool RenderMultiColumnFlowThread::shouldCheckColumnBreaks() const | 
 | { | 
 |     if (!parent()->isRenderView()) | 
 |         return true; | 
 |     return view().frameView().pagination().behavesLikeColumns; | 
 | } | 
 |  | 
 | } |