void RenderFlowThread::removeRenderBoxRegionInfo(RenderBox* box) { if (!hasRegions()) return; RenderRegion* startRegion; RenderRegion* endRegion; getRegionRangeForBox(box, startRegion, endRegion); for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; region->removeRenderBoxRegionInfo(box); if (region == endRegion) break; } #ifndef NDEBUG // We have to make sure we did not leave any RenderBoxRegionInfo attached. for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; ASSERT(!region->renderBoxRegionInfo(box)); } #endif m_regionRangeMap.remove(box); }
void RenderFlowThread::updateLogicalWidth() { LayoutUnit logicalWidth = 0; for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; ASSERT(!region->needsLayout()); logicalWidth = max(region->pageLogicalWidth(), logicalWidth); } setLogicalWidth(logicalWidth); // If the regions have non-uniform logical widths, then insert inset information for the RenderFlowThread. for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; LayoutUnit regionLogicalWidth = region->pageLogicalWidth(); if (regionLogicalWidth != logicalWidth) { LayoutUnit logicalLeft = style()->direction() == LTR ? ZERO_LAYOUT_UNIT : logicalWidth - regionLogicalWidth; region->setRenderBoxRegionInfo(this, logicalLeft, regionLogicalWidth, false); } } }
void RenderNamedFlowThread::checkInvalidRegions() { for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; // The only reason a region would be invalid is because it has a parent flow thread. ASSERT(region->isValid() || region->parentNamedFlowThread()); if (region->isValid() || region->parentNamedFlowThread()->dependsOn(this)) continue; region->parentNamedFlowThread()->m_observerThreadsSet.remove(this); addDependencyOnFlowThread(region->parentNamedFlowThread()); region->setIsValid(true); invalidateRegions(); } if (m_observerThreadsSet.isEmpty()) return; // Notify all the flow threads that were dependent on this flow. // Create a copy of the list first. That's because observers might change the list when calling checkInvalidRegions. Vector<RenderNamedFlowThread*> observers; copyToVector(m_observerThreadsSet, observers); for (size_t i = 0; i < observers.size(); ++i) { RenderNamedFlowThread* flowThread = observers.at(i); flowThread->checkInvalidRegions(); } }
void RenderFlowThread::computeLogicalWidth() { LayoutUnit logicalWidth = 0; for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; ASSERT(!region->needsLayout()); logicalWidth = max(isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight(), logicalWidth); } setLogicalWidth(logicalWidth); // If the regions have non-uniform logical widths, then insert inset information for the RenderFlowThread. for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; LayoutUnit regionLogicalWidth = isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight(); if (regionLogicalWidth != logicalWidth) { LayoutUnit logicalLeft = style()->direction() == LTR ? zeroLayoutUnit : logicalWidth - regionLogicalWidth; region->setRenderBoxRegionInfo(this, logicalLeft, regionLogicalWidth, false); } } }
bool RenderFlowThread::logicalWidthChangedInRegions(const RenderBlock* block, LayoutUnit offsetFromLogicalTopOfFirstPage) { if (!hasRegions() || block == this) // Not necessary, since if any region changes, we do a full pagination relayout anyway. return false; RenderRegion* startRegion; RenderRegion* endRegion; getRegionRangeForBox(block, startRegion, endRegion); for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; ASSERT(!region->needsLayout()); OwnPtr<RenderBoxRegionInfo> oldInfo = region->takeRenderBoxRegionInfo(block); if (!oldInfo) continue; LayoutUnit oldLogicalWidth = oldInfo->logicalWidth(); RenderBoxRegionInfo* newInfo = block->renderBoxRegionInfo(region, offsetFromLogicalTopOfFirstPage); if (!newInfo || newInfo->logicalWidth() != oldLogicalWidth) return true; if (region == endRegion) break; } return false; }
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; }
void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect, bool immediate) { if (!shouldRepaint(repaintRect) || !hasValidRegionInfo()) return; for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; // We only have to issue a repaint in this region if the region rect intersects the repaint rect. LayoutRect flippedRegionRect(region->regionRect()); LayoutRect flippedRegionOverflowRect(region->regionOverflowRect()); flipForWritingMode(flippedRegionRect); // Put the region rects into physical coordinates. flipForWritingMode(flippedRegionOverflowRect); LayoutRect clippedRect(repaintRect); clippedRect.intersect(flippedRegionOverflowRect); if (clippedRect.isEmpty()) continue; // Put the region rect into the region's physical coordinate space. clippedRect.setLocation(region->contentBoxRect().location() + (clippedRect.location() - flippedRegionRect.location())); // Now switch to the region's writing mode coordinate space and let it repaint itself. region->flipForWritingMode(clippedRect); LayoutStateDisabler layoutStateDisabler(view()); // We can't use layout state to repaint, since the region is somewhere else. // Can't use currentFlowThread as it possible to have imbricated flow threads and the wrong one could be used, // so, we let each region figure out the proper enclosing flow thread CurrentRenderFlowThreadDisabler disabler(view()); region->repaintRectangle(clippedRect, immediate); } }
RenderRegion* RenderFlowThread::lastRegion() const { if (!hasValidRegionInfo()) return 0; for (RenderRegionList::const_reverse_iterator iter = m_regionList.rbegin(); iter != m_regionList.rend(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; return region; } return 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; }
void RenderFlowThread::updateLogicalHeight() { LayoutUnit logicalHeight = 0; for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; ASSERT(!region->needsLayout()); logicalHeight += region->logicalHeightOfAllFlowThreadContent(); } setLogicalHeight(logicalHeight); }
// Check if the content is flown into at least a region with region styling rules. void RenderFlowThread::checkRegionsWithStyling() { bool hasRegionsWithStyling = false; for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; if (region->hasCustomRegionStyle()) { hasRegionsWithStyling = true; break; } } m_hasRegionsWithStyling = hasRegionsWithStyling; }
void RenderFlowThread::computeLogicalHeight() { LayoutUnit logicalHeight = 0; for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; ASSERT(!region->needsLayout()); logicalHeight += isHorizontalWritingMode() ? region->contentHeight() : region->contentWidth(); } setLogicalHeight(logicalHeight); }
static void writeRenderNamedFlowThreads(TextStream& ts, RenderView* renderView, const RenderLayer* rootLayer, const LayoutRect& paintRect, int indent, RenderAsTextBehavior behavior) { if (!renderView->hasRenderNamedFlowThreads()) return; const RenderNamedFlowThreadList* list = renderView->flowThreadController()->renderNamedFlowThreadList(); writeIndent(ts, indent); ts << "Flow Threads\n"; for (RenderNamedFlowThreadList::const_iterator iter = list->begin(); iter != list->end(); ++iter) { const RenderNamedFlowThread* renderFlowThread = *iter; writeIndent(ts, indent + 1); ts << "Thread with flow-name '" << renderFlowThread->flowThreadName() << "'\n"; RenderLayer* layer = renderFlowThread->layer(); writeLayers(ts, rootLayer, layer, paintRect, indent + 2, behavior); // Display the render regions attached to this flow thread const RenderRegionList& flowThreadRegionList = renderFlowThread->renderRegionList(); if (!flowThreadRegionList.isEmpty()) { writeIndent(ts, indent + 1); ts << "Regions for flow '"<< renderFlowThread->flowThreadName() << "'\n"; for (RenderRegionList::const_iterator itRR = flowThreadRegionList.begin(); itRR != flowThreadRegionList.end(); ++itRR) { RenderRegion* renderRegion = *itRR; writeIndent(ts, indent + 2); ts << "RenderRegion"; if (renderRegion->node()) { String tagName = getTagName(renderRegion->node()); if (!tagName.isEmpty()) ts << " {" << tagName << "}"; if (renderRegion->node()->isElementNode() && renderRegion->node()->hasID()) { Element* element = static_cast<Element*>(renderRegion->node()); ts << " #" << element->idForStyleResolution(); } if (renderRegion->hasCustomRegionStyle()) ts << " region style: 1"; } if (!renderRegion->isValid()) ts << " invalid"; ts << "\n"; } } } }
void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect, bool immediate) { if (!shouldRepaint(repaintRect) || !hasValidRegionInfo()) return; LayoutStateDisabler layoutStateDisabler(view()); // 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 region figure out the proper enclosing flow thread. CurrentRenderFlowThreadDisabler disabler(view()); for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; region->repaintFlowThreadContent(repaintRect, immediate); } }
void RenderFlowThread::computeOverflowStateForRegions(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 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(); RenderRegion* lastReg = lastRegion(); for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) { region->setRegionState(RenderRegion::RegionUndefined); continue; } 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); } // 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; }
static PassRefPtr<InspectorArray> buildObjectForRendererFragments(RenderObject* renderer, const HighlightConfig& config) { RefPtr<InspectorArray> fragmentsArray = InspectorArray::create(); RenderFlowThread* containingFlowThread = renderer->flowThreadContainingBlock(); if (!containingFlowThread) { Highlight highlight; buildRendererHighlight(renderer, nullptr, config, &highlight, InspectorOverlay::CoordinateSystem::View); fragmentsArray->pushObject(buildObjectForHighlight(highlight)); } else { RenderRegion* startRegion = nullptr; RenderRegion* endRegion = nullptr; if (!containingFlowThread->getRegionRangeForBox(&renderer->enclosingBox(), startRegion, endRegion)) { // The flow has no visible regions. The renderer is not visible on screen. return nullptr; } const RenderRegionList& regionList = containingFlowThread->renderRegionList(); for (RenderRegionList::const_iterator iter = regionList.find(startRegion); iter != regionList.end(); ++iter) { RenderRegion* region = *iter; if (region->isValid()) { // Compute the highlight of the fragment inside the current region. Highlight highlight; buildRendererHighlight(renderer, region, config, &highlight, InspectorOverlay::CoordinateSystem::View); RefPtr<InspectorObject> fragmentObject = buildObjectForHighlight(highlight); // Compute the clipping area of the region. fragmentObject->setObject("region", buildObjectForCSSRegionContentClip(region)); fragmentsArray->pushObject(fragmentObject.release()); } if (region == endRegion) break; } } return fragmentsArray.release(); }
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"; } }
void RenderFlowThread::layout() { m_pageLogicalHeightChanged = m_regionsInvalidated && everHadLayout(); if (m_regionsInvalidated) { m_regionsInvalidated = false; m_hasValidRegions = false; m_regionsHaveUniformLogicalWidth = true; m_regionsHaveUniformLogicalHeight = true; m_regionRangeMap.clear(); LayoutUnit previousRegionLogicalWidth = 0; LayoutUnit previousRegionLogicalHeight = 0; if (hasRegions()) { for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; ASSERT(!region->needsLayout()); region->deleteAllRenderBoxRegionInfo(); LayoutUnit regionLogicalWidth = region->pageLogicalWidth(); LayoutUnit regionLogicalHeight = region->pageLogicalHeight(); if (!m_hasValidRegions) m_hasValidRegions = 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. LayoutUnit logicalHeight = 0; for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; LayoutUnit regionLogicalWidth = region->pageLogicalWidth(); LayoutUnit regionLogicalHeight = region->logicalHeightOfAllFlowThreadContent(); LayoutRect regionRect(style()->direction() == LTR ? ZERO_LAYOUT_UNIT : logicalWidth() - regionLogicalWidth, logicalHeight, regionLogicalWidth, regionLogicalHeight); region->setFlowThreadPortionRect(isHorizontalWritingMode() ? regionRect : regionRect.transposedRect()); logicalHeight += regionLogicalHeight; } } } CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this); RenderBlock::layout(); m_pageLogicalHeightChanged = false; if (lastRegion()) lastRegion()->expandToEncompassFlowThreadContentsIfNeeded(); if (shouldDispatchRegionLayoutUpdateEvent()) dispatchRegionLayoutUpdateEvent(); }
void RenderFlowThread::layout() { bool regionsChanged = m_regionsInvalidated && everHadLayout(); if (m_regionsInvalidated) { m_regionsInvalidated = false; m_hasValidRegions = false; m_regionsHaveUniformLogicalWidth = true; m_regionsHaveUniformLogicalHeight = true; m_regionRangeMap.clear(); LayoutUnit previousRegionLogicalWidth = 0; LayoutUnit previousRegionLogicalHeight = 0; if (hasRegions()) { for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; ASSERT(!region->needsLayout()); region->deleteAllRenderBoxRegionInfo(); LayoutUnit regionLogicalWidth; LayoutUnit regionLogicalHeight; if (isHorizontalWritingMode()) { regionLogicalWidth = region->contentWidth(); regionLogicalHeight = region->contentHeight(); } else { regionLogicalWidth = region->contentHeight(); regionLogicalHeight = region->contentWidth(); } if (!m_hasValidRegions) m_hasValidRegions = true; else { if (m_regionsHaveUniformLogicalWidth && previousRegionLogicalWidth != regionLogicalWidth) m_regionsHaveUniformLogicalWidth = false; if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight) m_regionsHaveUniformLogicalHeight = false; } previousRegionLogicalWidth = regionLogicalWidth; } computeLogicalWidth(); // Called to get the maximum logical width for the region. LayoutUnit logicalHeight = 0; for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; LayoutRect regionRect; if (isHorizontalWritingMode()) { regionRect = LayoutRect(style()->direction() == LTR ? zeroLayoutUnit : logicalWidth() - region->contentWidth(), logicalHeight, region->contentWidth(), region->contentHeight()); logicalHeight += regionRect.height(); } else { regionRect = LayoutRect(logicalHeight, style()->direction() == LTR ? zeroLayoutUnit : logicalWidth() - region->contentHeight(), region->contentWidth(), region->contentHeight()); logicalHeight += regionRect.width(); } region->setRegionRect(regionRect); } } } CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this); LayoutStateMaintainer statePusher(view(), this, regionsChanged); RenderBlock::layout(); statePusher.pop(); if (document()->hasListenerType(Document::REGIONLAYOUTUPDATE_LISTENER) && !m_regionLayoutUpdateEventTimer.isActive()) for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (region->shouldDispatchRegionLayoutUpdateEvent()) { // at least one region needs to dispatch the event m_regionLayoutUpdateEventTimer.startOneShot(0); break; } } }