RenderNamedFlowThread.cpp [plain text]
#include "config.h"
#include "RenderNamedFlowThread.h"
#include "ExceptionCodePlaceholder.h"
#include "FlowThreadController.h"
#include "InlineTextBox.h"
#include "InspectorInstrumentation.h"
#include "NodeRenderingTraversal.h"
#include "NodeTraversal.h"
#include "Position.h"
#include "Range.h"
#include "RenderInline.h"
#include "RenderLayer.h"
#include "RenderLineBreak.h"
#include "RenderNamedFlowFragment.h"
#include "RenderText.h"
#include "RenderView.h"
#include "ShadowRoot.h"
#include "Text.h"
#include "WebKitNamedFlow.h"
namespace WebCore {
RenderNamedFlowThread::RenderNamedFlowThread(Document& document, PassRef<RenderStyle> style, PassRef<WebKitNamedFlow> namedFlow)
: RenderFlowThread(document, WTF::move(style))
, m_hasRegionsWithStyling(false)
, m_dispatchRegionOversetChangeEvent(false)
, m_namedFlow(WTF::move(namedFlow))
, m_regionOversetChangeEventTimer(this, &RenderNamedFlowThread::regionOversetChangeEventTimerFired)
{
}
RenderNamedFlowThread::~RenderNamedFlowThread()
{
clearContentElements();
setMarkForDestruction();
}
const char* RenderNamedFlowThread::renderName() const
{
return "RenderNamedFlowThread";
}
void RenderNamedFlowThread::clearContentElements()
{
for (auto& contentElement : m_contentElements) {
ASSERT(contentElement);
ASSERT(contentElement->isNamedFlowContentNode());
ASSERT(&contentElement->document() == &document());
contentElement->clearIsNamedFlowContentNode();
}
m_contentElements.clear();
}
void RenderNamedFlowThread::updateWritingMode()
{
RenderNamedFlowFragment* firstFragment = toRenderNamedFlowFragment(m_regionList.first());
if (!firstFragment)
return;
if (style().writingMode() == firstFragment->style().writingMode())
return;
auto newStyle = RenderStyle::clone(&style());
newStyle.get().setWritingMode(firstFragment->style().writingMode());
setStyle(WTF::move(newStyle));
}
RenderElement* RenderNamedFlowThread::nextRendererForElement(Element& element) const
{
for (auto& child : m_flowThreadChildList) {
ASSERT(!child->isAnonymous());
unsigned short position = element.compareDocumentPosition(child->element());
if (position & Node::DOCUMENT_POSITION_FOLLOWING)
return child;
}
return 0;
}
void RenderNamedFlowThread::addFlowChild(RenderElement& newChild)
{
if (newChild.isAnonymous())
return;
auto* beforeChild = nextRendererForElement(*newChild.element());
if (beforeChild)
m_flowThreadChildList.insertBefore(beforeChild, &newChild);
else
m_flowThreadChildList.add(&newChild);
}
void RenderNamedFlowThread::removeFlowChild(RenderElement& child)
{
m_flowThreadChildList.remove(&child);
}
bool RenderNamedFlowThread::dependsOn(RenderNamedFlowThread* otherRenderFlowThread) const
{
if (m_layoutBeforeThreadsSet.contains(otherRenderFlowThread))
return true;
for (const auto& beforeFlowThreadPair : m_layoutBeforeThreadsSet) {
const auto& beforeFlowThread = beforeFlowThreadPair.key;
if (beforeFlowThread->dependsOn(otherRenderFlowThread))
return true;
}
return false;
}
static bool compareRenderNamedFlowFragments(const RenderNamedFlowFragment* firstFragment, const RenderNamedFlowFragment* secondFragment)
{
ASSERT(firstFragment);
ASSERT(secondFragment);
ASSERT(firstFragment->generatingElement());
ASSERT(secondFragment->generatingElement());
if (firstFragment->generatingElement() != secondFragment->generatingElement()) {
unsigned short position = firstFragment->generatingElement()->compareDocumentPosition(secondFragment->generatingElement());
if (position & Node::DOCUMENT_POSITION_CONTAINED_BY) {
ASSERT(secondFragment->style().styleType() == NOPSEUDO);
return firstFragment->style().styleType() == BEFORE;
}
if (position & Node::DOCUMENT_POSITION_CONTAINS) {
ASSERT(firstFragment->style().styleType() == NOPSEUDO);
return secondFragment->style().styleType() == AFTER;
}
return (position & Node::DOCUMENT_POSITION_FOLLOWING);
}
switch (firstFragment->style().styleType()) {
case BEFORE:
return true;
case AFTER:
return false;
case NOPSEUDO:
return firstFragment->style().styleType() == AFTER;
default:
break;
}
ASSERT_NOT_REACHED();
return true;
}
static void addFragmentToList(RenderRegionList& regionList, RenderNamedFlowFragment* renderNamedFlowFragment)
{
if (regionList.isEmpty())
regionList.add(renderNamedFlowFragment);
else {
auto it = regionList.begin();
while (it != regionList.end() && !compareRenderNamedFlowFragments(renderNamedFlowFragment, toRenderNamedFlowFragment(*it)))
++it;
regionList.insertBefore(it, renderNamedFlowFragment);
}
}
void RenderNamedFlowThread::addFragmentToNamedFlowThread(RenderNamedFlowFragment* renderNamedFlowFragment)
{
ASSERT(renderNamedFlowFragment);
ASSERT(!renderNamedFlowFragment->isValid());
if (renderNamedFlowFragment->parentNamedFlowThread())
addDependencyOnFlowThread(renderNamedFlowFragment->parentNamedFlowThread());
renderNamedFlowFragment->setIsValid(true);
renderNamedFlowFragment->updateRegionFlags();
addFragmentToList(m_regionList, renderNamedFlowFragment);
if (m_regionList.first() == renderNamedFlowFragment)
updateWritingMode();
}
void RenderNamedFlowThread::addRegionToThread(RenderRegion* renderRegion)
{
ASSERT(renderRegion);
ASSERT(!renderRegion->isValid());
RenderNamedFlowFragment* renderNamedFlowFragment = toRenderNamedFlowFragment(renderRegion);
resetMarkForDestruction();
if (renderNamedFlowFragment->parentNamedFlowThread() && renderNamedFlowFragment->parentNamedFlowThread()->dependsOn(this)) {
m_invalidRegionList.add(renderNamedFlowFragment);
renderNamedFlowFragment->parentNamedFlowThread()->m_observerThreadsSet.add(this);
return;
}
addFragmentToNamedFlowThread(renderNamedFlowFragment);
invalidateRegions();
}
void RenderNamedFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
{
ASSERT(renderRegion);
RenderNamedFlowFragment* renderNamedFlowFragment = toRenderNamedFlowFragment(renderRegion);
if (renderNamedFlowFragment->parentNamedFlowThread()) {
if (!renderNamedFlowFragment->isValid()) {
ASSERT(m_invalidRegionList.contains(renderNamedFlowFragment));
m_invalidRegionList.remove(renderNamedFlowFragment);
renderNamedFlowFragment->parentNamedFlowThread()->m_observerThreadsSet.remove(this);
return;
}
removeDependencyOnFlowThread(renderNamedFlowFragment->parentNamedFlowThread());
}
ASSERT(m_regionList.contains(renderNamedFlowFragment));
bool wasFirst = m_regionList.first() == renderNamedFlowFragment;
m_regionList.remove(renderNamedFlowFragment);
if (canBeDestroyed())
setMarkForDestruction();
if (!m_regionList.isEmpty() && wasFirst)
updateWritingMode();
invalidateRegions();
}
void RenderNamedFlowThread::regionChangedWritingMode(RenderRegion* region)
{
if (m_regionList.first() == region)
updateWritingMode();
}
LayoutRect RenderNamedFlowThread::decorationsClipRectForBoxInNamedFlowFragment(const RenderBox& box, RenderNamedFlowFragment& fragment) const
{
LayoutRect visualOverflowRect = fragment.visualOverflowRectForBox(&box);
LayoutUnit initialLogicalX = style().isHorizontalWritingMode() ? visualOverflowRect.x() : visualOverflowRect.y();
flipForWritingModeLocalCoordinates(visualOverflowRect);
visualOverflowRect = fragment.rectFlowPortionForBox(&box, visualOverflowRect);
flipForWritingModeLocalCoordinates(visualOverflowRect);
IntSize scrolledContentOffset;
RenderBlock* containingBlock = box.containingBlock();
while (containingBlock) {
if (containingBlock->isRenderNamedFlowThread()) {
ASSERT(containingBlock == this);
scrolledContentOffset += fragment.fragmentContainer().scrolledContentOffset();
break;
}
scrolledContentOffset += containingBlock->scrolledContentOffset();
containingBlock = containingBlock->containingBlock();
}
if (!scrolledContentOffset.isZero()) {
if (style().isFlippedBlocksWritingMode())
scrolledContentOffset = -scrolledContentOffset;
visualOverflowRect.inflateX(scrolledContentOffset.width());
visualOverflowRect.inflateY(scrolledContentOffset.height());
}
if (style().isFlippedBlocksWritingMode()) {
if (style().isHorizontalWritingMode())
visualOverflowRect.moveBy(LayoutPoint(0, height()));
else
visualOverflowRect.moveBy(LayoutPoint(width(), 0));
}
const RenderBox* iterBox = &box;
while (iterBox && iterBox != this) {
RenderBlock* containerBlock = iterBox->containingBlock();
if (iterBox->isPositioned()) {
visualOverflowRect.moveBy(iterBox->layer()->absoluteBoundingBox().location());
break;
}
LayoutRect currentBoxRect = iterBox->frameRect();
if (iterBox->style().isFlippedBlocksWritingMode()) {
if (iterBox->style().isHorizontalWritingMode())
currentBoxRect.setY(currentBoxRect.height() - currentBoxRect.maxY());
else
currentBoxRect.setX(currentBoxRect.width() - currentBoxRect.maxX());
}
if (containerBlock->style().writingMode() != iterBox->style().writingMode())
iterBox->flipForWritingMode(currentBoxRect);
visualOverflowRect.moveBy(currentBoxRect.location());
iterBox = containerBlock;
}
if (style().isHorizontalWritingMode()) {
if (initialLogicalX < visualOverflowRect.x())
visualOverflowRect.shiftXEdgeTo(initialLogicalX);
if (visualOverflowRect.width() < frameRect().width())
visualOverflowRect.setWidth(frameRect().width());
} else {
if (initialLogicalX < visualOverflowRect.y())
visualOverflowRect.shiftYEdgeTo(initialLogicalX);
if (visualOverflowRect.height() < frameRect().height())
visualOverflowRect.setHeight(frameRect().height());
}
return visualOverflowRect;
}
RenderNamedFlowFragment* RenderNamedFlowThread::fragmentFromAbsolutePointAndBox(const IntPoint& absolutePoint, const RenderBox& flowedBox)
{
RenderRegion* startRegion = nullptr;
RenderRegion* endRegion = nullptr;
if (!getRegionRangeForBox(&flowedBox, startRegion, endRegion))
return nullptr;
for (auto iter = m_regionList.find(startRegion), end = m_regionList.end(); iter != end; ++iter) {
RenderNamedFlowFragment* fragment = toRenderNamedFlowFragment(*iter);
RenderBlockFlow& fragmentContainer = fragment->fragmentContainer();
IntRect fragmentAbsoluteRect(roundedIntPoint(fragmentContainer.localToAbsolute()), roundedIntSize(fragmentContainer.paddingBoxRect().size()));
if (fragmentAbsoluteRect.contains(absolutePoint))
return fragment;
if (fragment == endRegion)
break;
}
return nullptr;
}
void RenderNamedFlowThread::computeOverflow(LayoutUnit oldClientAfterEdge, bool recomputeFloats)
{
RenderFlowThread::computeOverflow(oldClientAfterEdge, recomputeFloats);
m_flowContentBottom = oldClientAfterEdge;
}
void RenderNamedFlowThread::layout()
{
RenderFlowThread::layout();
if (previousRegionCountChanged()) {
setDispatchRegionOversetChangeEvent(true);
updatePreviousRegionCount();
}
}
void RenderNamedFlowThread::dispatchNamedFlowEvents()
{
ASSERT(inFinalLayoutPhase());
dispatchRegionOversetChangeEventIfNeeded();
}
void RenderNamedFlowThread::checkInvalidRegions()
{
Vector<RenderNamedFlowFragment*> newValidFragments;
for (auto& region : m_invalidRegionList) {
RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(region);
ASSERT(!namedFlowFragment->isValid() && namedFlowFragment->parentNamedFlowThread());
if (namedFlowFragment->parentNamedFlowThread()->dependsOn(this))
continue;
newValidFragments.append(namedFlowFragment);
}
for (auto& namedFlowFragment : newValidFragments) {
m_invalidRegionList.remove(namedFlowFragment);
namedFlowFragment->parentNamedFlowThread()->m_observerThreadsSet.remove(this);
addFragmentToNamedFlowThread(namedFlowFragment);
}
if (!newValidFragments.isEmpty())
invalidateRegions();
if (m_observerThreadsSet.isEmpty())
return;
Vector<RenderNamedFlowThread*> observers;
copyToVector(m_observerThreadsSet, observers);
for (auto& flowThread : observers)
flowThread->checkInvalidRegions();
}
void RenderNamedFlowThread::addDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread)
{
RenderNamedFlowThreadCountedSet::AddResult result = m_layoutBeforeThreadsSet.add(otherFlowThread);
if (result.isNewEntry) {
view().flowThreadController().setIsRenderNamedFlowThreadOrderDirty(true);
}
}
void RenderNamedFlowThread::removeDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread)
{
bool removed = m_layoutBeforeThreadsSet.remove(otherFlowThread);
if (removed) {
checkInvalidRegions();
view().flowThreadController().setIsRenderNamedFlowThreadOrderDirty(true);
}
}
void RenderNamedFlowThread::pushDependencies(RenderNamedFlowThreadList& list)
{
for (auto& flowThreadPair : m_layoutBeforeThreadsSet) {
auto& flowThread = flowThreadPair.key;
if (list.contains(flowThread))
continue;
flowThread->pushDependencies(list);
list.add(flowThread);
}
}
void RenderNamedFlowThread::registerNamedFlowContentElement(Element& contentElement)
{
ASSERT(&contentElement.document() == &document());
contentElement.setIsNamedFlowContentNode();
resetMarkForDestruction();
for (auto& element : m_contentElements) {
unsigned short position = contentElement.compareDocumentPosition(element);
if (position & Node::DOCUMENT_POSITION_FOLLOWING) {
m_contentElements.insertBefore(element, &contentElement);
InspectorInstrumentation::didRegisterNamedFlowContentElement(&document(), &namedFlow(), &contentElement, element);
return;
}
}
m_contentElements.add(&contentElement);
InspectorInstrumentation::didRegisterNamedFlowContentElement(&document(), &namedFlow(), &contentElement);
}
void RenderNamedFlowThread::unregisterNamedFlowContentElement(Element& contentElement)
{
ASSERT(m_contentElements.contains(&contentElement));
ASSERT(contentElement.isNamedFlowContentNode());
ASSERT(&contentElement.document() == &document());
contentElement.clearIsNamedFlowContentNode();
m_contentElements.remove(&contentElement);
if (canBeDestroyed())
setMarkForDestruction();
InspectorInstrumentation::didUnregisterNamedFlowContentElement(&document(), &namedFlow(), &contentElement);
}
bool RenderNamedFlowThread::hasContentElement(Element& contentElement) const
{
return m_contentElements.contains(&contentElement);
}
const AtomicString& RenderNamedFlowThread::flowThreadName() const
{
return namedFlow().name();
}
bool RenderNamedFlowThread::isChildAllowed(const RenderObject& child, const RenderStyle& style) const
{
if (!child.node())
return true;
ASSERT(child.node()->isElementNode());
Node* originalParent = NodeRenderingTraversal::parent(child.node());
if (!originalParent || !originalParent->isElementNode() || !originalParent->renderer())
return true;
return toElement(originalParent)->renderer()->isChildAllowed(child, style);
}
void RenderNamedFlowThread::dispatchRegionOversetChangeEventIfNeeded()
{
if (!m_dispatchRegionOversetChangeEvent)
return;
m_dispatchRegionOversetChangeEvent = false;
InspectorInstrumentation::didChangeRegionOverset(&document(), &namedFlow());
if (!m_regionOversetChangeEventTimer.isActive() && namedFlow().hasEventListeners())
m_regionOversetChangeEventTimer.startOneShot(0);
}
void RenderNamedFlowThread::regionOversetChangeEventTimerFired(Timer<RenderNamedFlowThread>&)
{
namedFlow().dispatchRegionOversetChangeEvent();
}
void RenderNamedFlowThread::setMarkForDestruction()
{
if (namedFlow().flowState() == WebKitNamedFlow::FlowStateNull)
return;
namedFlow().setRenderer(nullptr);
}
void RenderNamedFlowThread::resetMarkForDestruction()
{
if (namedFlow().flowState() == WebKitNamedFlow::FlowStateCreated)
return;
namedFlow().setRenderer(this);
}
bool RenderNamedFlowThread::isMarkedForDestruction() const
{
return namedFlow().flowState() == WebKitNamedFlow::FlowStateNull;
}
static bool isContainedInElements(const Vector<Element*>& others, Element* element)
{
for (auto& other : others) {
if (other->contains(element))
return true;
}
return false;
}
static bool boxIntersectsRegion(LayoutUnit logicalTopForBox, LayoutUnit logicalBottomForBox, LayoutUnit logicalTopForRegion, LayoutUnit logicalBottomForRegion)
{
bool regionIsEmpty = logicalBottomForRegion != LayoutUnit::max() && logicalTopForRegion != LayoutUnit::min()
&& (logicalBottomForRegion - logicalTopForRegion) <= 0;
return (logicalBottomForBox - logicalTopForBox) > 0
&& !regionIsEmpty
&& logicalTopForBox < logicalBottomForRegion && logicalTopForRegion < logicalBottomForBox;
}
static Node* nextNodeInsideContentElement(const Node* currNode, const Element* contentElement)
{
ASSERT(currNode);
ASSERT(contentElement && contentElement->isNamedFlowContentNode());
if (currNode->renderer() && currNode->renderer()->isSVGRoot())
return NodeTraversal::nextSkippingChildren(currNode, contentElement);
return NodeTraversal::next(currNode, contentElement);
}
void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range>>& rangeObjects, const RenderNamedFlowFragment* namedFlowFragment) const
{
LayoutUnit logicalTopForRegion;
LayoutUnit logicalBottomForRegion;
if (namedFlowFragment->isFirstRegion())
logicalTopForRegion = LayoutUnit::min();
else
logicalTopForRegion = namedFlowFragment->logicalTopForFlowThreadContent();
if (namedFlowFragment->isLastRegion())
logicalBottomForRegion = LayoutUnit::max();
else
logicalBottomForRegion = namedFlowFragment->logicalBottomForFlowThreadContent();
Vector<Element*> elements;
for (auto& element : contentElements()) {
if (!isContainedInElements(elements, element))
elements.append(element);
}
for (auto& contentElement : elements) {
if (!contentElement->renderer())
continue;
RefPtr<Range> range = Range::create(contentElement->document());
bool foundStartPosition = false;
bool startsAboveRegion = true;
bool endsBelowRegion = true;
bool skipOverOutsideNodes = false;
Node* lastEndNode = 0;
for (Node* node = contentElement; node; node = nextNodeInsideContentElement(node, contentElement)) {
RenderObject* renderer = node->renderer();
if (!renderer)
continue;
LayoutRect boundingBox;
if (renderer->isRenderInline())
boundingBox = toRenderInline(renderer)->linesBoundingBox();
else if (renderer->isText())
boundingBox = toRenderText(renderer)->linesBoundingBox();
else if (renderer->isLineBreak())
boundingBox = toRenderLineBreak(renderer)->linesBoundingBox();
else if (renderer->isBox()) {
boundingBox = toRenderBox(renderer)->frameRect();
if (toRenderBox(renderer)->isRelPositioned())
boundingBox.move(toRenderBox(renderer)->relativePositionLogicalOffset());
} else
continue;
LayoutUnit offsetTop = renderer->containingBlock()->offsetFromLogicalTopOfFirstPage();
const LayoutPoint logicalOffsetFromTop(isHorizontalWritingMode() ? LayoutUnit() : offsetTop,
isHorizontalWritingMode() ? offsetTop : LayoutUnit());
boundingBox.moveBy(logicalOffsetFromTop);
LayoutUnit logicalTopForRenderer = namedFlowFragment->logicalTopOfFlowThreadContentRect(boundingBox);
LayoutUnit logicalBottomForRenderer = namedFlowFragment->logicalBottomOfFlowThreadContentRect(boundingBox);
if (!boxIntersectsRegion(logicalTopForRenderer, logicalBottomForRenderer, logicalTopForRegion, logicalBottomForRegion)) {
if (foundStartPosition) {
if (!startsAboveRegion) {
if (range->intersectsNode(node, IGNORE_EXCEPTION))
range->setEndBefore(node, IGNORE_EXCEPTION);
rangeObjects.append(range->cloneRange(IGNORE_EXCEPTION));
range = Range::create(contentElement->document());
startsAboveRegion = true;
} else
skipOverOutsideNodes = true;
}
if (skipOverOutsideNodes)
range->setStartAfter(node, IGNORE_EXCEPTION);
foundStartPosition = false;
continue;
}
if (logicalTopForRenderer < logicalTopForRegion && startsAboveRegion) {
if (renderer->isText()) {
RenderText* textRenderer = toRenderText(renderer);
for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
if (offsetTop + box->logicalBottom() < logicalTopForRegion)
continue;
range->setStart(Position(toText(node), box->start()));
startsAboveRegion = false;
break;
}
} else {
startsAboveRegion = true;
range->setStart(Position(node, Position::PositionIsBeforeChildren));
}
} else {
if (startsAboveRegion) {
startsAboveRegion = false;
range->setStartBefore(node, IGNORE_EXCEPTION);
}
}
skipOverOutsideNodes = false;
foundStartPosition = true;
if (logicalBottomForRegion < logicalBottomForRenderer && (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode)))) {
if (renderer->isText()) {
RenderText* textRenderer = toRenderText(renderer);
InlineTextBox* lastBox = 0;
for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
if ((offsetTop + box->logicalTop()) < logicalBottomForRegion) {
lastBox = box;
continue;
}
ASSERT(lastBox);
if (lastBox)
range->setEnd(Position(toText(node), lastBox->start() + lastBox->len()));
break;
}
endsBelowRegion = false;
lastEndNode = node;
} else {
range->setEnd(Position(node, Position::PositionIsAfterChildren));
endsBelowRegion = true;
lastEndNode = node;
}
} else {
if (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode))) {
range->setEndAfter(node, IGNORE_EXCEPTION);
endsBelowRegion = false;
lastEndNode = node;
}
}
}
if (foundStartPosition || skipOverOutsideNodes)
rangeObjects.append(range);
}
}
void RenderNamedFlowThread::applyBreakAfterContent(LayoutUnit clientHeight)
{
addForcedRegionBreak(this, clientHeight, this, false);
}
bool RenderNamedFlowThread::collectsGraphicsLayersUnderRegions() const
{
return true;
}
void RenderNamedFlowThread::checkRegionsWithStyling()
{
bool hasRegionsWithStyling = false;
for (const auto& region : m_regionList) {
if (toRenderNamedFlowFragment(region)->hasCustomRegionStyle()) {
hasRegionsWithStyling = true;
break;
}
}
m_hasRegionsWithStyling = hasRegionsWithStyling;
}
void RenderNamedFlowThread::clearRenderObjectCustomStyle(const RenderObject* object)
{
for (auto& region : m_regionList)
toRenderNamedFlowFragment(region)->clearObjectStyleInRegion(object);
}
void RenderNamedFlowThread::removeFlowChildInfo(RenderObject* child)
{
RenderFlowThread::removeFlowChildInfo(child);
clearRenderObjectCustomStyle(child);
}
bool RenderNamedFlowThread::absoluteQuadsForBox(Vector<FloatQuad>& quads, bool* wasFixed, const RenderBox* renderer, float localTop, float localBottom) const
{
RenderRegion* startRegion = nullptr;
RenderRegion* endRegion = nullptr;
if (!computedRegionRangeForBox(renderer, startRegion, endRegion))
return false;
for (auto iter = m_regionList.find(startRegion), end = m_regionList.end(); iter != end; ++iter) {
RenderRegion* region = *iter;
region->absoluteQuadsForBoxInRegion(quads, wasFixed, renderer, localTop, localBottom);
if (region == endRegion)
break;
}
return true;
}
}