void RenderFlowThread::validateRegions()
{
    if (m_regionsInvalidated) {
        m_regionsInvalidated = false;
        m_regionsHaveUniformLogicalHeight = true;

        if (hasRegions()) {
            LayoutUnit previousRegionLogicalHeight = 0;
            bool firstRegionVisited = false;

            for (RenderMultiColumnSetList::iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
                RenderMultiColumnSet* columnSet = *iter;
                LayoutUnit regionLogicalHeight = columnSet->pageLogicalHeight();

                if (!firstRegionVisited) {
                    firstRegionVisited = true;
                } else {
                    if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
                        m_regionsHaveUniformLogicalHeight = false;
                }

                previousRegionLogicalHeight = regionLogicalHeight;
            }
        }
    }

    updateLogicalWidth(); // Called to get the maximum logical width for the columnSet.
    updateRegionsFlowThreadPortionRect();
}
RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* flowThread)
{
    Document& document = flowThread->document();
    RenderMultiColumnSet* renderer = new RenderMultiColumnSet(flowThread);
    renderer->setDocumentForAnonymous(&document);
    return renderer;
}
Exemple #3
0
void RenderMultiColumnBlock::ensureColumnSets()
{
    // This function ensures we have the correct column set information before we get into layout.
    // 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 region.
    //
    // 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: For now just make one column set. 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.
    if (flowThread() && !firstChild()->isRenderMultiColumnSet()) {
        RenderMultiColumnSet* columnSet = new (renderArena()) RenderMultiColumnSet(document(), flowThread());
        columnSet->setStyle(RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK));
        RenderBlock::addChild(columnSet, firstChild());
        flowThread()->addRegionToThread(columnSet);
    }
}
LayoutUnit RenderFlowThread::pageLogicalHeightForOffset(LayoutUnit offset)
{
    RenderMultiColumnSet* columnSet = columnSetAtBlockOffset(offset);
    if (!columnSet)
        return 0;

    return columnSet->pageLogicalHeight();
}
RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* flowThread, RenderStyle* parentStyle)
{
    Document& document = flowThread->document();
    RenderMultiColumnSet* renderer = new RenderMultiColumnSet(flowThread);
    renderer->setDocumentForAnonymous(&document);
    renderer->setStyle(RenderStyle::createAnonymousStyleWithDisplay(parentStyle, BLOCK));
    return renderer;
}
RenderMultiColumnSet* RenderMultiColumnFlowThread::findSetRendering(RenderObject* renderer) const
{
    for (RenderMultiColumnSet* multicolSet = firstMultiColumnSet(); multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) {
        if (multicolSet->containsRendererInFlowThread(renderer))
            return multicolSet;
    }
    return nullptr;
}
void RenderFlowThread::collectLayerFragments(LayerFragments& layerFragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
{
    ASSERT(!m_regionsInvalidated);

    for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
        RenderMultiColumnSet* columnSet = *iter;
        columnSet->collectLayerFragments(layerFragments, layerBoundingBox, dirtyRect);
    }
}
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();
}
void RenderFlowThread::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
{
    computedValues.m_position = logicalTop;
    computedValues.m_extent = 0;

    for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
        RenderMultiColumnSet* columnSet = *iter;
        computedValues.m_extent += columnSet->logicalHeightOfAllFlowThreadContent();
    }
}
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);
}
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;
}
LayoutUnit RenderFlowThread::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule)
{
    RenderMultiColumnSet* columnSet = columnSetAtBlockOffset(offset);
    if (!columnSet)
        return 0;

    LayoutUnit pageLogicalTop = columnSet->pageLogicalTopForOffset(offset);
    LayoutUnit pageLogicalHeight = columnSet->pageLogicalHeight();
    LayoutUnit pageLogicalBottom = pageLogicalTop + pageLogicalHeight;
    LayoutUnit remainingHeight = pageLogicalBottom - offset;
    if (pageBoundaryRule == IncludePageBoundary) {
        // If IncludePageBoundary is set, the line exactly on the top edge of a
        // columnSet will act as being part of the previous columnSet.
        remainingHeight = intMod(remainingHeight, pageLogicalHeight);
    }
    return remainingHeight;
}
void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect) const
{
    if (!shouldRepaint(repaintRect) || !hasValidRegionInfo())
        return;

    ForceHorriblySlowRectMapping slowRectMapping(*this); // We can't use layout state to repaint, since the regions are somewhere else.

    // We can't use currentFlowThread as it is possible to have interleaved flow threads and the wrong one could be used.
    // Let each columnSet figure out the proper enclosing flow thread.
    CurrentRenderFlowThreadDisabler disabler(view());

    for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
        RenderMultiColumnSet* columnSet = *iter;

        columnSet->repaintFlowThreadContent(repaintRect);
    }
}
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);
}
LayoutRect RenderFlowThread::fragmentsBoundingBox(const LayoutRect& layerBoundingBox)
{
    ASSERT(!m_regionsInvalidated);

    LayoutRect result;
    for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
        RenderMultiColumnSet* columnSet = *iter;
        LayerFragments fragments;
        columnSet->collectLayerFragments(fragments, layerBoundingBox, PaintInfo::infiniteRect());
        for (size_t i = 0; i < fragments.size(); ++i) {
            const LayerFragment& fragment = fragments.at(i);
            LayoutRect fragmentRect(layerBoundingBox);
            fragmentRect.intersect(fragment.paginationClip);
            fragmentRect.moveBy(fragment.paginationOffset);
            result.unite(fragmentRect);
        }
    }

    return result;
}
void RenderFlowThread::updateRegionsFlowThreadPortionRect()
{
    LayoutUnit logicalHeight = 0;
    // FIXME: Optimize not to clear the interval all the time. This implies manually managing the tree nodes lifecycle.
    m_multiColumnSetIntervalTree.clear();
    m_multiColumnSetIntervalTree.initIfNeeded();
    for (RenderMultiColumnSetList::iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
        RenderMultiColumnSet* columnSet = *iter;

        LayoutUnit columnSetLogicalWidth = columnSet->pageLogicalWidth();
        LayoutUnit columnSetLogicalHeight = std::min<LayoutUnit>(RenderFlowThread::maxLogicalHeight() - logicalHeight, columnSet->logicalHeightOfAllFlowThreadContent());

        LayoutRect columnSetRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - columnSetLogicalWidth, logicalHeight, columnSetLogicalWidth, columnSetLogicalHeight);

        columnSet->setFlowThreadPortionRect(isHorizontalWritingMode() ? columnSetRect : columnSetRect.transposedRect());

        m_multiColumnSetIntervalTree.add(MultiColumnSetIntervalTree::createInterval(logicalHeight, logicalHeight + columnSetLogicalHeight, columnSet));

        logicalHeight += columnSetLogicalHeight;
    }
}
Exemple #17
0
bool RenderMultiColumnBlock::relayoutForPagination(bool, LayoutUnit, LayoutStateMaintainer& statePusher)
{
    if (m_inBalancingPass || !requiresBalancing())
        return false;
    m_inBalancingPass = true; // Prevent re-entering this method (and recursion into layout).

    bool needsRelayout;
    bool neededRelayout = false;
    bool firstPass = true;
    do {
        // Column heights may change here because of balancing. We may have to do multiple layout
        // passes, depending on how the contents is fitted to the changed column heights. In most
        // cases, laying out again twice or even just once will suffice. Sometimes we need more
        // passes than that, though, but the number of retries should not exceed the number of
        // columns, unless we have a bug.
        needsRelayout = false;
        for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox())
            if (childBox != m_flowThread && childBox->isRenderMultiColumnSet()) {
                RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(childBox);
                if (multicolSet->calculateBalancedHeight(firstPass)) {
                    multicolSet->setChildNeedsLayout(MarkOnlyThis);
                    needsRelayout = true;
                }
            }

        if (needsRelayout) {
            // Layout again. Column balancing resulted in a new height.
            neededRelayout = true;
            m_flowThread->setChildNeedsLayout(MarkOnlyThis);
            setChildNeedsLayout(MarkOnlyThis);
            if (firstPass)
                statePusher.pop();
            layoutBlock(false);
        }
        firstPass = false;
    } while (needsRelayout);
    m_inBalancingPass = false;
    return neededRelayout;
}
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: For now just make one column set. 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.
    RenderMultiColumnSet* firstSet = toRenderMultiColumnSet(firstRegion());
    if (firstSet)
        return;
    
    invalidateRegions();

    RenderBlockFlow* parentBlock = toRenderBlockFlow(parent());
    firstSet = new RenderMultiColumnSet(*this, RenderStyle::createAnonymousStyleWithDisplay(&parentBlock->style(), BLOCK));
    firstSet->initializeStyle();
    parentBlock->RenderBlock::addChild(firstSet);

    // Even though we aren't placed yet, we can go ahead and set up our size. At this point we're
    // typically in the middle of laying out the thread, attempting to paginate, and we need to do
    // some rudimentary "layout" of the set now, so that pagination will work.
    firstSet->prepareForLayout();

    validateRegions();
}
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 center = boxRect.center();
    LayoutUnit centerOffset = isHorizontalWritingMode() ? center.y() : center.x();
    RenderRegion* renderRegion = const_cast<RenderMultiColumnFlowThread*>(this)->regionAtBlockOffset(this, centerOffset, true, DisallowRegionAutoGeneration);
    if (!renderRegion)
        return nullptr;

    // Now that we know which multicolumn set we hit, we need to get the appropriate translation offset for the column.
    RenderMultiColumnSet* columnSet = toRenderMultiColumnSet(renderRegion);
    LayoutPoint translationOffset = columnSet->columnTranslationForOffset(centerOffset);
    
    // Now we know how we want the rect to be translated into the region.
    LayoutRect flippedRegionRect(renderRegion->flowThreadPortionRect());
    if (isHorizontalWritingMode())
        flippedRegionRect.setHeight(columnSet->computedColumnHeight());
    else
        flippedRegionRect.setWidth(columnSet->computedColumnHeight());
    flipForWritingMode(flippedRegionRect);
    
    flippedRegionRect.moveBy(-translationOffset);
    
    // There is an additional offset to apply, which is the offset of the region within the multi-column space.
    transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location());

    return renderRegion;
}
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());
    }
}
LayoutPoint RenderFlowThread::adjustedPositionRelativeToOffsetParent(const RenderBoxModelObject& boxModelObject, const LayoutPoint& startPoint)
{
    LayoutPoint referencePoint = startPoint;

    // FIXME: This needs to be adapted for different writing modes inside the flow thread.
    RenderMultiColumnSet* startColumnSet = columnSetAtBlockOffset(referencePoint.y());
    if (startColumnSet) {
        // Take into account the offset coordinates of the columnSet.
        RenderObject* currObject = startColumnSet;
        RenderObject* currOffsetParentRenderer;
        Element* currOffsetParentElement;
        while ((currOffsetParentElement = currObject->offsetParent()) && (currOffsetParentRenderer = currOffsetParentElement->renderer())) {
            if (currObject->isBoxModelObject())
                referencePoint.move(toRenderBoxModelObject(currObject)->offsetLeft(), toRenderBoxModelObject(currObject)->offsetTop());

            // Since we're looking for the offset relative to the body, we must also
            // take into consideration the borders of the columnSet's offsetParent.
            if (currOffsetParentRenderer->isBox() && !currOffsetParentRenderer->isBody())
                referencePoint.move(toRenderBox(currOffsetParentRenderer)->borderLeft(), toRenderBox(currOffsetParentRenderer)->borderTop());

            currObject = currOffsetParentRenderer;
        }

        // We need to check if any of this box's containing blocks start in a different columnSet
        // and if so, drop the object's top position (which was computed relative to its containing block
        // and is no longer valid) and recompute it using the columnSet in which it flows as reference.
        bool wasComputedRelativeToOtherRegion = false;
        const RenderBlock* objContainingBlock = boxModelObject.containingBlock();
        while (objContainingBlock) {
            // Check if this object is in a different columnSet.
            RenderMultiColumnSet* parentStartRegion = 0;
            RenderMultiColumnSet* parentEndRegion = 0;
            getRegionRangeForBox(objContainingBlock, parentStartRegion, parentEndRegion);
            if (parentStartRegion && parentStartRegion != startColumnSet) {
                wasComputedRelativeToOtherRegion = true;
                break;
            }
            objContainingBlock = objContainingBlock->containingBlock();
        }

        if (wasComputedRelativeToOtherRegion) {
            // Get the logical top coordinate of the current object.
            LayoutUnit top = 0;
            if (boxModelObject.isRenderBlock()) {
                top = toRenderBlock(&boxModelObject)->offsetFromLogicalTopOfFirstPage();
            } else {
                if (boxModelObject.containingBlock())
                    top = boxModelObject.containingBlock()->offsetFromLogicalTopOfFirstPage();

                if (boxModelObject.isBox())
                    top += toRenderBox(&boxModelObject)->topLeftLocation().y();
                else if (boxModelObject.isRenderInline())
                    top -= toRenderInline(&boxModelObject)->borderTop();
            }

            // Get the logical top of the columnSet this object starts in
            // and compute the object's top, relative to the columnSet's top.
            LayoutUnit regionLogicalTop = startColumnSet->pageLogicalTopForOffset(top);
            LayoutUnit topRelativeToRegion = top - regionLogicalTop;
            referencePoint.setY(startColumnSet->offsetTop() + topRelativeToRegion);

            // Since the top has been overriden, check if the
            // relative positioning must be reconsidered.
            if (boxModelObject.isRelPositioned())
                referencePoint.move(0, boxModelObject.relativePositionOffset().height());
        }

        // Since we're looking for the offset relative to the body, we must also
        // take into consideration the borders of the columnSet.
        referencePoint.move(startColumnSet->borderLeft(), startColumnSet->borderTop());
    }

    return referencePoint;
}
LayoutUnit RenderFlowThread::pageLogicalTopForOffset(LayoutUnit offset)
{
    RenderMultiColumnSet* columnSet = columnSetAtBlockOffset(offset);
    return columnSet ? columnSet->pageLogicalTopForOffset(offset) : LayoutUnit();
}