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