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 #2
0
LayoutUnit RenderFlowThread::contentLogicalLeftOfFirstRegion() const
{
    if (!hasValidRegionInfo())
        return 0;
    for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
        RenderRegion* region = *iter;
        if (!region->isValid())
            continue;
        return isHorizontalWritingMode() ? region->flowThreadPortionRect().x() : region->flowThreadPortionRect().y();
    }
    ASSERT_NOT_REACHED();
    return 0;
}
LayoutUnit RenderFlowThread::contentLogicalLeftOfFirstRegion() const
{
    RenderRegion* firstValidRegionInFlow = firstRegion();
    if (!firstValidRegionInFlow)
        return 0;
    return isHorizontalWritingMode() ? firstValidRegionInFlow->flowThreadPortionRect().x() : firstValidRegionInFlow->flowThreadPortionRect().y();
}
RenderRegion* RenderFlowThread::regionAtBlockOffset(LayoutUnit offset, bool extendLastRegion) const
{
    ASSERT(!m_regionsInvalidated);

    // If no region matches the position and extendLastRegion is true, it will return
    // the last valid region. It is similar to auto extending the size of the last region. 
    RenderRegion* lastValidRegion = 0;

    LayoutUnit accumulatedLogicalHeight = 0;
    
    // FIXME: The regions are always in order, optimize this search.
    for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
        RenderRegion* region = *iter;

        if (offset <= 0)
            return region;

        if (extendLastRegion || region->isRenderRegionSet())
            lastValidRegion = region;

        if (region->hasOverrideHeight() && view()->normalLayoutPhase()) {
            accumulatedLogicalHeight += region->overrideLogicalContentHeight();
            if (offset < accumulatedLogicalHeight)
                return region;
            continue;
        }

        LayoutRect regionRect = region->flowThreadPortionRect();
        accumulatedLogicalHeight += isHorizontalWritingMode() ? regionRect.height() : regionRect.width();
        if (offset < accumulatedLogicalHeight)
            return region;
    }

    return lastValidRegion;
}
Example #5
0
RenderRegion* RenderFlowThread::regionAtBlockOffset(LayoutUnit offset, bool extendLastRegion) const
{
    ASSERT(!m_regionsInvalidated);

    // If no region matches the position and extendLastRegion is true, it will return
    // the last valid region. It is similar to auto extending the size of the last region. 
    RenderRegion* lastValidRegion = 0;
    
    // FIXME: The regions are always in order, optimize this search.
    bool useHorizontalWritingMode = isHorizontalWritingMode();
    for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
        RenderRegion* region = *iter;
        if (!region->isValid())
            continue;

        if (offset <= 0)
            return region;

        LayoutRect regionRect = region->flowThreadPortionRect();
        if ((useHorizontalWritingMode && offset < regionRect.maxY()) || (!useHorizontalWritingMode && offset < regionRect.maxX()))
            return region;

        if (extendLastRegion || region->isRenderRegionSet())
            lastValidRegion = region;
    }

    return lastValidRegion;
}
Example #6
0
void RenderNamedFlowThread::computeOversetStateForRegions(LayoutUnit oldClientAfterEdge)
{
    LayoutUnit height = oldClientAfterEdge;

    // 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 RenderNamedFlowThread (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();

    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());
        RegionOversetState previousState = region->regionOversetState();
        RegionOversetState state = RegionFit;
        if (flowMin <= 0)
            state = RegionEmpty;
        if (flowMax > 0 && region == lastReg)
            state = RegionOverset;
        region->setRegionOversetState(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 == RegionFit
            || state == RegionOverset)
            setDispatchRegionLayoutUpdateEvent(true);
        
        if (previousState != state)
            setDispatchRegionOversetChangeEvent(true);
    }
    
    // If the number of regions has changed since we last computed the overset property, schedule the regionOversetChange event.
    if (previousRegionCountChanged()) {
        setDispatchRegionOversetChangeEvent(true);
        updatePreviousRegionCount();
    }

    // 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->regionOversetState() == RegionOverset : true;
}
RenderRegion* RenderFlowThread::mapFromFlowToRegion(TransformState& transformState) const
{
    if (!hasValidRegionInfo())
        return 0;

    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 region.
    // Note: Using the center in order to avoid rounding errors.

    LayoutPoint center = boxRect.center();
    RenderRegion* renderRegion = regionAtBlockOffset(isHorizontalWritingMode() ? center.y() : center.x(), true);
    if (!renderRegion)
        return 0;

    LayoutRect flippedRegionRect(renderRegion->flowThreadPortionRect());
    flipForWritingMode(flippedRegionRect);

    transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location());

    return renderRegion;
}
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;
}
// 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;
}