void RenderFlowThread::updateRegionsFlowThreadPortionRect()
{
    LayoutUnit logicalHeight = 0;
    for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
        RenderRegion* region = *iter;

        LayoutUnit regionLogicalWidth = region->pageLogicalWidth();
        LayoutUnit regionLogicalHeight = region->logicalHeightOfAllFlowThreadContent();

        LayoutRect regionRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - regionLogicalWidth, logicalHeight, regionLogicalWidth, regionLogicalHeight);

        // When a flow thread has more than one auto logical height region,
        // we have to take into account the override logical content height value,
        // if computed for an auto logical height region, and use it to set the height
        // for the region rect. This way, the regions in the chain following the auto
        // logical height region, will be able to fragment the right part of their
        // associated flow thread content (and compute their overrideComputedLogicalHeight properly).
        if (view()->normalLayoutPhase()) {
            ASSERT(region->hasOverrideHeight() || !region->hasAutoLogicalHeight());
            if (region->hasOverrideHeight())
                regionLogicalHeight = region->overrideLogicalContentHeight();

            regionRect.setHeight(regionLogicalHeight);
        }

        region->setFlowThreadPortionRect(isHorizontalWritingMode() ? regionRect : regionRect.transposedRect());
        logicalHeight += regionLogicalHeight;
    }
}
Example #2
0
void RenderFlowThread::resetRegionsOverrideLogicalContentHeight()
{
    ASSERT(view()->layoutState());
    ASSERT(view()->normalLayoutPhase());

    // We need to reset the override logical content height for regions with auto logical height
    // only if the flow thread content needs layout.
    if (!needsLayout())
        return;

    // FIXME: optimize this to iterate the region chain only if the flow thread has auto logical height
    // region.

    for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
        RenderRegion* region = *iter;
        if (!region->hasAutoLogicalHeight())
            continue;

        region->clearOverrideLogicalContentHeight();
        // FIXME: We need to find a way to avoid marking all the regions ancestors for layout
        // as we are already inside layout.
        region->setNeedsLayout(true);
    }
    // Make sure we don't skip any region breaks when we do the layout again.
    // Using m_regionsInvalidated to force all the RenderFlowThread children do the layout again.
    m_regionsInvalidated = true;
}
void RenderFlowThread::computeOverflowStateForRegions(LayoutUnit oldClientAfterEdge)
{
    LayoutUnit height = oldClientAfterEdge;

    LayoutUnit offsetBreakAdjustment = 0;
    // Simulate a region break at height. If it points inside an auto logical height region,
    // then it may determine the region override logical content height.
    addForcedRegionBreak(height, this, false, &offsetBreakAdjustment);

    // During the normal layout phase of the flow thread all the auto-height regions have the overrideLogicalContentHeight set to max height.
    // We need to clear the overrideLogicalContentHeight for all the regions that didn't receive any content, starting with firstEmptyRegion.
    RenderRegion* firstEmptyRegion = 0;
    if (view()->normalLayoutPhase())
        firstEmptyRegion = regionAtBlockOffset(height + offsetBreakAdjustment);

    // FIXME: the visual overflow of middle region (if it is the last one to contain any content in a render flow thread)
    // might not be taken into account because the render flow thread height is greater that that regions height + its visual overflow
    // because of how computeLogicalHeight is implemented for RenderFlowThread (as a sum of all regions height).
    // This means that the middle region will be marked as fit (even if it has visual overflow flowing into the next region)
    if (hasRenderOverflow()
        && ( (isHorizontalWritingMode() && visualOverflowRect().maxY() > clientBoxRect().maxY())
            || (!isHorizontalWritingMode() && visualOverflowRect().maxX() > clientBoxRect().maxX())))
        height = isHorizontalWritingMode() ? visualOverflowRect().maxY() : visualOverflowRect().maxX();

    bool inEmptyRegionsSection = false;
    RenderRegion* lastReg = lastRegion();
    for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
        RenderRegion* region = *iter;
        LayoutUnit flowMin = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().y() : region->flowThreadPortionRect().x());
        LayoutUnit flowMax = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().maxY() : region->flowThreadPortionRect().maxX());
        RenderRegion::RegionState previousState = region->regionState();
        RenderRegion::RegionState state = RenderRegion::RegionFit;
        if (flowMin <= 0)
            state = RenderRegion::RegionEmpty;
        if (flowMax > 0 && region == lastReg)
            state = RenderRegion::RegionOverset;
        region->setRegionState(state);
        // determine whether the NamedFlow object should dispatch a regionLayoutUpdate event
        // FIXME: currently it cannot determine whether a region whose regionOverset state remained either "fit" or "overset" has actually
        // changed, so it just assumes that the NamedFlow should dispatch the event
        if (previousState != state
            || state == RenderRegion::RegionFit
            || state == RenderRegion::RegionOverset)
            setDispatchRegionLayoutUpdateEvent(true);

        if (region == firstEmptyRegion)
            inEmptyRegionsSection = true;

        // Clear the overrideLogicalContentHeight value for autoheight regions that didn't receive any content.
        if (inEmptyRegionsSection && region->hasAutoLogicalHeight())
            region->clearOverrideLogicalContentHeight();
    }

    // With the regions overflow state computed we can also set the overset flag for the named flow.
    // If there are no valid regions in the chain, overset is true.
    m_overset = lastReg ? lastReg->regionState() == RenderRegion::RegionOverset : true;
}
Example #4
0
// During the normal layout phase of the named flow the regions are initialized with a height equal to their max-height.
// This way unforced breaks are automatically placed when a region is full and the content height/position correctly estimated.
// Also, the region where a forced break falls is exactly the region found at the forced break offset inside the flow content.
void RenderFlowThread::initializeRegionsOverrideLogicalContentHeight(RenderRegion* startRegion)
{
    ASSERT(view()->normalLayoutPhase());
    RenderRegionList::iterator regionIter = startRegion ? m_regionList.find(startRegion) : m_regionList.begin();
    for (; regionIter != m_regionList.end(); ++regionIter) {
        RenderRegion* region = *regionIter;
        if (region->hasAutoLogicalHeight())
            region->setOverrideLogicalContentHeight(region->maxPageLogicalHeight());
    }
}
Example #5
0
void RenderFlowThread::validateRegions()
{
    if (m_regionsInvalidated) {
        m_regionsInvalidated = false;
        m_regionsHaveUniformLogicalWidth = true;
        m_regionsHaveUniformLogicalHeight = true;

        if (hasRegions()) {
            LayoutUnit previousRegionLogicalWidth = 0;
            LayoutUnit previousRegionLogicalHeight = 0;
            bool firstRegionVisited = false;
            
            for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
                RenderRegion* region = *iter;
                ASSERT(!region->needsLayout() || region->isRenderRegionSet());

                region->deleteAllRenderBoxRegionInfo();

                // In the normal layout phase we need to initialize the overrideLogicalContentHeight for auto-height regions.
                // See initializeRegionsOverrideLogicalContentHeight for the explanation.
                // Also, if we have auto-height regions we can't assume m_regionsHaveUniformLogicalHeight to be true in the first phase
                // because the auto-height regions don't have their height computed yet.
                if (view()->normalLayoutPhase() && region->hasAutoLogicalHeight()) {
                    region->setOverrideLogicalContentHeight(region->maxPageLogicalHeight());
                    m_regionsHaveUniformLogicalHeight = false;
                }

                LayoutUnit regionLogicalWidth = region->pageLogicalWidth();
                LayoutUnit regionLogicalHeight = region->pageLogicalHeight();

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

                previousRegionLogicalWidth = regionLogicalWidth;
            }
        }
    }

    updateLogicalWidth(); // Called to get the maximum logical width for the region.
    updateRegionsFlowThreadPortionRect();
}
void RenderFlowThread::markAutoLogicalHeightRegionsForLayout()
{
    ASSERT(view()->layoutState());
    ASSERT(view()->constrainedFlowThreadsLayoutPhase());

    if (!hasAutoLogicalHeightRegions())
        return;

    for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
        RenderRegion* region = *iter;
        if (!region->hasAutoLogicalHeight())
            continue;

        // FIXME: We need to find a way to avoid marking all the regions ancestors for layout
        // as we are already inside layout.
        region->setNeedsLayout(true);
    }

    invalidateRegions();
}
Example #7
0
void RenderFlowThread::markAutoLogicalHeightRegionsForLayout()
{
    ASSERT(view()->layoutState());
    ASSERT(view()->constrainedFlowThreadsLayoutPhase());

    // FIXME: optimize this to iterate the region chain only if the flow thread has auto logical height
    // region.

    for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
        RenderRegion* region = *iter;
        if (!region->hasAutoLogicalHeight())
            continue;

        // FIXME: We need to find a way to avoid marking all the regions ancestors for layout
        // as we are already inside layout.
        region->setNeedsLayout(true);
    }

    m_regionsInvalidated = true;
    setNeedsLayout(true);
}
void RenderFlowThread::resetRegionsOverrideLogicalContentHeight()
{
    ASSERT(view()->layoutState());
    ASSERT(view()->normalLayoutPhase());

    if (!hasAutoLogicalHeightRegions())
        return;

    for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
        RenderRegion* region = *iter;
        if (!region->hasAutoLogicalHeight())
            continue;

        region->clearOverrideLogicalContentHeight();
        // FIXME: We need to find a way to avoid marking all the regions ancestors for layout
        // as we are already inside layout.
        region->setNeedsLayout(true);
    }
    // Make sure we don't skip any region breaks when we do the layout again.
    // Using m_regionsInvalidated to force all the RenderFlowThread children do the layout again.
    invalidateRegions();
}
Example #9
0
static void writeRenderRegionList(const RenderRegionList& flowThreadRegionList, TextStream& ts, int indent)
{
    for (RenderRegionList::const_iterator itRR = flowThreadRegionList.begin(); itRR != flowThreadRegionList.end(); ++itRR) {
        RenderRegion* renderRegion = *itRR;
        writeIndent(ts, indent + 2);
        ts << "RenderRegion";
        if (renderRegion->generatingNode()) {
            String tagName = getTagName(renderRegion->generatingNode());
            if (!tagName.isEmpty())
                ts << " {" << tagName << "}";
            if (renderRegion->generatingNode()->isElementNode() && renderRegion->generatingNode()->hasID()) {
                Element* element = static_cast<Element*>(renderRegion->generatingNode());
                ts << " #" << element->idForStyleResolution();
            }
            if (renderRegion->hasCustomRegionStyle())
                ts << " region style: 1";
            if (renderRegion->hasAutoLogicalHeight())
                ts << " hasAutoLogicalHeight";
        }
        if (!renderRegion->isValid())
            ts << " invalid";
        ts << "\n";
    }
}
Example #10
0
// Even if we require the break to occur at offsetBreakInFlowThread, because regions may have min/max-height values,
// it is possible that the break will occur at a different offset than the original one required.
// offsetBreakAdjustment measures the different between the requested break offset and the current break offset.
bool RenderFlowThread::addForcedRegionBreak(LayoutUnit offsetBreakInFlowThread, RenderObject* breakChild, bool isBefore, LayoutUnit* offsetBreakAdjustment)
{
    // We take breaks into account for height computation for auto logical height regions
    // only in the layout phase in which we lay out the flows threads unconstrained
    // and we use the content breaks to determine the overrideContentLogicalHeight for
    // auto logical height regions.
    if (view()->constrainedFlowThreadsLayoutPhase())
        return false;

    // Breaks can come before or after some objects. We need to track these objects, so that if we get
    // multiple breaks for the same object (for example because of multiple layouts on the same object),
    // we need to invalidate every other region after the old one and start computing from fresh.
    RenderObjectToRegionMap& mapToUse = isBefore ? m_breakBeforeToRegionMap : m_breakAfterToRegionMap;
    RenderObjectToRegionMap::iterator iter = mapToUse.find(breakChild);
    if (iter != mapToUse.end()) {
        RenderRegionList::iterator regionIter = m_regionList.find(iter->value);
        ASSERT(regionIter != m_regionList.end());
        ASSERT((*regionIter)->hasAutoLogicalHeight());
        initializeRegionsOverrideLogicalContentHeight(*regionIter);

        // We need to update the regions flow thread portion rect because we are going to process
        // a break on these regions.
        updateRegionsFlowThreadPortionRect();
    }

    // Simulate a region break at offsetBreakInFlowThread. If it points inside an auto logical height region,
    // then it determines the region override logical content height.
    RenderRegion* region = regionAtBlockOffset(offsetBreakInFlowThread);
    if (!region)
        return false;

    bool overrideLogicalContentHeightComputed = false;

    LayoutUnit currentRegionOffsetInFlowThread = isHorizontalWritingMode() ? region->flowThreadPortionRect().y() : region->flowThreadPortionRect().x();
    LayoutUnit offsetBreakInCurrentRegion = offsetBreakInFlowThread - currentRegionOffsetInFlowThread;

    if (region->hasAutoLogicalHeight()) {
        // A forced break can appear only in an auto-height region that didn't have a forced break before.
        // This ASSERT is a good-enough heuristic to verify the above condition.
        ASSERT(region->maxPageLogicalHeight() == region->overrideLogicalContentHeight());

        mapToUse.set(breakChild, region);

        overrideLogicalContentHeightComputed = true;

        // Compute the region height pretending that the offsetBreakInCurrentRegion is the logicalHeight for the auto-height region.
        LayoutUnit regionOverrideLogicalContentHeight = region->computeReplacedLogicalHeightRespectingMinMaxHeight(offsetBreakInCurrentRegion);

        // The new height of this region needs to be smaller than the initial value, the max height. A forced break is the only way to change the initial
        // height of an auto-height region besides content ending.
        ASSERT(regionOverrideLogicalContentHeight <= region->maxPageLogicalHeight());

        region->setOverrideLogicalContentHeight(regionOverrideLogicalContentHeight);

        currentRegionOffsetInFlowThread += regionOverrideLogicalContentHeight;
    } else
        currentRegionOffsetInFlowThread += isHorizontalWritingMode() ? region->flowThreadPortionRect().height() : region->flowThreadPortionRect().width();

    // If the break was found inside an auto-height region its size changed so we need to recompute the flow thread portion rectangles.
    if (overrideLogicalContentHeightComputed)
        updateRegionsFlowThreadPortionRect();

    if (offsetBreakAdjustment)
        *offsetBreakAdjustment = max<LayoutUnit>(0, currentRegionOffsetInFlowThread - offsetBreakInFlowThread);

    return overrideLogicalContentHeightComputed;
}
Example #11
0
void RenderFlowThread::layout()
{
    StackStats::LayoutCheckPoint layoutCheckPoint;

    m_pageLogicalHeightChanged = m_regionsInvalidated && everHadLayout();
    if (m_regionsInvalidated) {
        m_regionsInvalidated = false;
        m_regionsHaveUniformLogicalWidth = true;
        m_regionsHaveUniformLogicalHeight = true;
        m_regionRangeMap.clear();
        m_breakBeforeToRegionMap.clear();
        m_breakAfterToRegionMap.clear();

        LayoutUnit previousRegionLogicalWidth = 0;
        LayoutUnit previousRegionLogicalHeight = 0;
        bool firstRegionVisited = false;
        if (hasRegions()) {
            for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
                RenderRegion* region = *iter;
                ASSERT(!region->needsLayout());
                
                region->deleteAllRenderBoxRegionInfo();

                // In the normal layout phase we need to initialize the overrideLogicalContentHeight for auto-height regions.
                // See initializeRegionsOverrideLogicalContentHeight for the explanation.
                // Also, if we have auto-height regions we can't assume m_regionsHaveUniformLogicalHeight to be true in the first phase
                // because the auto-height regions don't have their height computed yet.
                if (view()->normalLayoutPhase() && region->hasAutoLogicalHeight()) {
                    region->setOverrideLogicalContentHeight(region->maxPageLogicalHeight());
                    m_regionsHaveUniformLogicalHeight = false;
                }

                LayoutUnit regionLogicalWidth = region->pageLogicalWidth();
                LayoutUnit regionLogicalHeight = region->pageLogicalHeight();

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

                previousRegionLogicalWidth = regionLogicalWidth;
            }
            
            updateLogicalWidth(); // Called to get the maximum logical width for the region.
            updateRegionsFlowThreadPortionRect();
        }
    }

    CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
    RenderBlock::layout();

    m_pageLogicalHeightChanged = false;

    if (lastRegion())
        lastRegion()->expandToEncompassFlowThreadContentsIfNeeded();

    if (shouldDispatchRegionLayoutUpdateEvent())
        dispatchRegionLayoutUpdateEvent();
}