2 * Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials
14 * provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
26 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include "RenderFlowThread.h"
34 #include "FlowThreadController.h"
35 #include "HitTestRequest.h"
36 #include "HitTestResult.h"
38 #include "PaintInfo.h"
39 #include "RenderBoxRegionInfo.h"
40 #include "RenderLayer.h"
41 #include "RenderRegion.h"
42 #include "RenderView.h"
43 #include "TransformState.h"
44 #include "WebKitNamedFlow.h"
48 RenderFlowThread::RenderFlowThread(Node* node)
50 , m_hasValidRegions(false)
51 , m_regionsInvalidated(false)
52 , m_regionsHaveUniformLogicalWidth(true)
53 , m_regionsHaveUniformLogicalHeight(true)
55 , m_regionLayoutUpdateEventTimer(this, &RenderFlowThread::regionLayoutUpdateEventTimerFired)
57 ASSERT(node->document()->cssRegionsEnabled());
58 setIsAnonymous(false);
59 setInRenderFlowThread();
62 PassRefPtr<RenderStyle> RenderFlowThread::createFlowThreadStyle(RenderStyle* parentStyle)
64 RefPtr<RenderStyle> newStyle(RenderStyle::create());
65 newStyle->inheritFrom(parentStyle);
66 newStyle->setDisplay(BLOCK);
67 newStyle->setPosition(AbsolutePosition);
68 newStyle->setZIndex(0);
69 newStyle->setLeft(Length(0, Fixed));
70 newStyle->setTop(Length(0, Fixed));
71 newStyle->setWidth(Length(100, Percent));
72 newStyle->setHeight(Length(100, Percent));
73 newStyle->font().update(0);
75 return newStyle.release();
78 void RenderFlowThread::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
80 RenderBlock::styleDidChange(diff, oldStyle);
82 if (oldStyle && oldStyle->writingMode() != style()->writingMode())
83 m_regionsInvalidated = true;
86 void RenderFlowThread::removeFlowChildInfo(RenderObject* child)
89 removeRenderBoxRegionInfo(toRenderBox(child));
90 if (child->canHaveRegionStyle())
91 clearRenderBoxCustomStyle(toRenderBox(child));
95 void RenderFlowThread::addRegionToThread(RenderRegion* renderRegion)
98 m_regionList.add(renderRegion);
99 renderRegion->setIsValid(true);
103 void RenderFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
105 ASSERT(renderRegion);
106 m_regionRangeMap.clear();
107 m_regionList.remove(renderRegion);
111 class CurrentRenderFlowThreadMaintainer {
112 WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadMaintainer);
114 CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread)
115 : m_renderFlowThread(renderFlowThread)
117 RenderView* view = m_renderFlowThread->view();
118 ASSERT(!view->flowThreadController()->currentRenderFlowThread());
119 view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
121 ~CurrentRenderFlowThreadMaintainer()
123 RenderView* view = m_renderFlowThread->view();
124 ASSERT(view->flowThreadController()->currentRenderFlowThread() == m_renderFlowThread);
125 view->flowThreadController()->setCurrentRenderFlowThread(0);
128 RenderFlowThread* m_renderFlowThread;
131 class CurrentRenderFlowThreadDisabler {
132 WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadDisabler);
134 CurrentRenderFlowThreadDisabler(RenderView* view)
136 , m_renderFlowThread(0)
138 m_renderFlowThread = m_view->flowThreadController()->currentRenderFlowThread();
139 if (m_renderFlowThread)
140 view->flowThreadController()->setCurrentRenderFlowThread(0);
142 ~CurrentRenderFlowThreadDisabler()
144 if (m_renderFlowThread)
145 m_view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
149 RenderFlowThread* m_renderFlowThread;
152 void RenderFlowThread::layout()
154 bool regionsChanged = m_regionsInvalidated && everHadLayout();
155 if (m_regionsInvalidated) {
156 m_regionsInvalidated = false;
157 m_hasValidRegions = false;
158 m_regionsHaveUniformLogicalWidth = true;
159 m_regionsHaveUniformLogicalHeight = true;
160 m_regionRangeMap.clear();
161 LayoutUnit previousRegionLogicalWidth = 0;
162 LayoutUnit previousRegionLogicalHeight = 0;
164 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
165 RenderRegion* region = *iter;
166 if (!region->isValid())
168 ASSERT(!region->needsLayout());
170 region->deleteAllRenderBoxRegionInfo();
172 LayoutUnit regionLogicalWidth;
173 LayoutUnit regionLogicalHeight;
175 if (isHorizontalWritingMode()) {
176 regionLogicalWidth = region->contentWidth();
177 regionLogicalHeight = region->contentHeight();
179 regionLogicalWidth = region->contentHeight();
180 regionLogicalHeight = region->contentWidth();
183 if (!m_hasValidRegions)
184 m_hasValidRegions = true;
186 if (m_regionsHaveUniformLogicalWidth && previousRegionLogicalWidth != regionLogicalWidth)
187 m_regionsHaveUniformLogicalWidth = false;
188 if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
189 m_regionsHaveUniformLogicalHeight = false;
192 previousRegionLogicalWidth = regionLogicalWidth;
195 computeLogicalWidth(); // Called to get the maximum logical width for the region.
197 LayoutUnit logicalHeight = 0;
198 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
199 RenderRegion* region = *iter;
200 if (!region->isValid())
202 LayoutRect regionRect;
203 if (isHorizontalWritingMode()) {
204 regionRect = LayoutRect(style()->direction() == LTR ? ZERO_LAYOUT_UNIT : logicalWidth() - region->contentWidth(), logicalHeight, region->contentWidth(), region->contentHeight());
205 logicalHeight += regionRect.height();
207 regionRect = LayoutRect(logicalHeight, style()->direction() == LTR ? ZERO_LAYOUT_UNIT : logicalWidth() - region->contentHeight(), region->contentWidth(), region->contentHeight());
208 logicalHeight += regionRect.width();
210 region->setRegionRect(regionRect);
215 CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
216 LayoutStateMaintainer statePusher(view(), this, regionsChanged);
217 RenderBlock::layout();
219 if (document()->hasListenerType(Document::REGIONLAYOUTUPDATE_LISTENER) && !m_regionLayoutUpdateEventTimer.isActive())
220 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
221 RenderRegion* region = *iter;
222 if (region->shouldDispatchRegionLayoutUpdateEvent()) {
223 // at least one region needs to dispatch the event
224 m_regionLayoutUpdateEventTimer.startOneShot(0);
230 void RenderFlowThread::computeLogicalWidth()
232 LayoutUnit logicalWidth = 0;
233 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
234 RenderRegion* region = *iter;
235 if (!region->isValid())
237 ASSERT(!region->needsLayout());
238 logicalWidth = max(isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight(), logicalWidth);
240 setLogicalWidth(logicalWidth);
242 // If the regions have non-uniform logical widths, then insert inset information for the RenderFlowThread.
243 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
244 RenderRegion* region = *iter;
245 if (!region->isValid())
248 LayoutUnit regionLogicalWidth = isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight();
249 if (regionLogicalWidth != logicalWidth) {
250 LayoutUnit logicalLeft = style()->direction() == LTR ? ZERO_LAYOUT_UNIT : logicalWidth - regionLogicalWidth;
251 region->setRenderBoxRegionInfo(this, logicalLeft, regionLogicalWidth, false);
256 void RenderFlowThread::computeLogicalHeight()
258 LayoutUnit logicalHeight = 0;
260 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
261 RenderRegion* region = *iter;
262 if (!region->isValid())
264 ASSERT(!region->needsLayout());
265 logicalHeight += isHorizontalWritingMode() ? region->contentHeight() : region->contentWidth();
268 setLogicalHeight(logicalHeight);
271 void RenderFlowThread::paintIntoRegion(PaintInfo& paintInfo, RenderRegion* region, const LayoutPoint& paintOffset)
273 GraphicsContext* context = paintInfo.context;
277 // Adjust the clipping rect for the region.
278 // paintOffset contains the offset where the painting should occur
279 // adjusted with the region padding and border.
280 LayoutRect regionRect(region->regionRect());
281 LayoutRect regionOverflowRect(region->regionOverflowRect());
282 LayoutRect regionClippingRect(paintOffset + (regionOverflowRect.location() - regionRect.location()), regionOverflowRect.size());
284 PaintInfo info(paintInfo);
285 info.rect.intersect(pixelSnappedIntRect(regionClippingRect));
287 if (!info.rect.isEmpty()) {
290 context->clip(regionClippingRect);
292 // RenderFlowThread should start painting its content in a position that is offset
293 // from the region rect's current position. The amount of offset is equal to the location of
294 // region in flow coordinates.
295 LayoutPoint renderFlowThreadOffset;
296 if (style()->isFlippedBlocksWritingMode()) {
297 LayoutRect flippedRegionRect(regionRect);
298 flipForWritingMode(flippedRegionRect);
299 renderFlowThreadOffset = LayoutPoint(paintOffset - flippedRegionRect.location());
301 renderFlowThreadOffset = LayoutPoint(paintOffset - regionRect.location());
303 context->translate(renderFlowThreadOffset.x(), renderFlowThreadOffset.y());
304 info.rect.moveBy(-roundedIntPoint(renderFlowThreadOffset));
306 layer()->paint(context, info.rect, 0, 0, region, RenderLayer::PaintLayerTemporaryClipRects);
312 bool RenderFlowThread::hitTestRegion(RenderRegion* region, const HitTestRequest& request, HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
314 LayoutRect regionRect(region->regionRect());
315 LayoutRect regionOverflowRect = region->regionOverflowRect();
316 LayoutRect regionClippingRect(accumulatedOffset + (regionOverflowRect.location() - regionRect.location()), regionOverflowRect.size());
317 if (!regionClippingRect.contains(pointInContainer))
320 LayoutPoint renderFlowThreadOffset;
321 if (style()->isFlippedBlocksWritingMode()) {
322 LayoutRect flippedRegionRect(regionRect);
323 flipForWritingMode(flippedRegionRect);
324 renderFlowThreadOffset = LayoutPoint(accumulatedOffset - flippedRegionRect.location());
326 renderFlowThreadOffset = LayoutPoint(accumulatedOffset - regionRect.location());
328 LayoutPoint transformedPoint(pointInContainer.x() - renderFlowThreadOffset.x(), pointInContainer.y() - renderFlowThreadOffset.y());
330 // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView.
331 HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping);
333 RenderRegion* oldRegion = result.region();
334 result.setRegion(region);
335 LayoutPoint oldPoint = result.point();
336 result.setPoint(transformedPoint);
337 bool isPointInsideFlowThread = layer()->hitTest(newRequest, result);
338 result.setPoint(oldPoint);
339 result.setRegion(oldRegion);
341 // FIXME: Should we set result.m_localPoint back to the RenderRegion's coordinate space or leave it in the RenderFlowThread's coordinate
342 // space? Right now it's staying in the RenderFlowThread's coordinate space, which may end up being ok. We will know more when we get around to
343 // patching positionForPoint.
344 return isPointInsideFlowThread;
347 bool RenderFlowThread::shouldRepaint(const LayoutRect& r) const
349 if (view()->printing() || r.isEmpty())
355 void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect, bool immediate)
357 if (!shouldRepaint(repaintRect) || !hasValidRegionInfo())
360 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
361 RenderRegion* region = *iter;
362 if (!region->isValid())
365 // We only have to issue a repaint in this region if the region rect intersects the repaint rect.
366 LayoutRect flippedRegionRect(region->regionRect());
367 LayoutRect flippedRegionOverflowRect(region->regionOverflowRect());
368 flipForWritingMode(flippedRegionRect); // Put the region rects into physical coordinates.
369 flipForWritingMode(flippedRegionOverflowRect);
371 LayoutRect clippedRect(repaintRect);
372 clippedRect.intersect(flippedRegionOverflowRect);
373 if (clippedRect.isEmpty())
376 // Put the region rect into the region's physical coordinate space.
377 clippedRect.setLocation(region->contentBoxRect().location() + (clippedRect.location() - flippedRegionRect.location()));
379 // Now switch to the region's writing mode coordinate space and let it repaint itself.
380 region->flipForWritingMode(clippedRect);
381 LayoutStateDisabler layoutStateDisabler(view()); // We can't use layout state to repaint, since the region is somewhere else.
383 // Can't use currentFlowThread as it possible to have imbricated flow threads and the wrong one could be used,
384 // so, we let each region figure out the proper enclosing flow thread
385 CurrentRenderFlowThreadDisabler disabler(view());
386 region->repaintRectangle(clippedRect, immediate);
390 RenderRegion* RenderFlowThread::renderRegionForLine(LayoutUnit position, bool extendLastRegion) const
392 ASSERT(!m_regionsInvalidated);
394 // If no region matches the position and extendLastRegion is true, it will return
395 // the last valid region. It is similar to auto extending the size of the last region.
396 RenderRegion* lastValidRegion = 0;
398 // FIXME: The regions are always in order, optimize this search.
399 bool useHorizontalWritingMode = isHorizontalWritingMode();
400 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
401 RenderRegion* region = *iter;
402 if (!region->isValid())
408 LayoutRect regionRect = region->regionRect();
410 if ((useHorizontalWritingMode && position < regionRect.maxY()) || (!useHorizontalWritingMode && position < regionRect.maxX()))
413 if (extendLastRegion)
414 lastValidRegion = region;
417 return lastValidRegion;
420 LayoutUnit RenderFlowThread::regionLogicalTopForLine(LayoutUnit position) const
422 RenderRegion* region = renderRegionForLine(position);
425 return isHorizontalWritingMode() ? region->regionRect().y() : region->regionRect().x();
428 LayoutUnit RenderFlowThread::regionLogicalWidthForLine(LayoutUnit position) const
430 RenderRegion* region = renderRegionForLine(position, true);
432 return contentLogicalWidth();
433 return isHorizontalWritingMode() ? region->regionRect().width() : region->regionRect().height();
436 LayoutUnit RenderFlowThread::regionLogicalHeightForLine(LayoutUnit position) const
438 RenderRegion* region = renderRegionForLine(position);
441 return isHorizontalWritingMode() ? region->regionRect().height() : region->regionRect().width();
444 LayoutUnit RenderFlowThread::regionRemainingLogicalHeightForLine(LayoutUnit position, PageBoundaryRule pageBoundaryRule) const
446 RenderRegion* region = renderRegionForLine(position);
450 LayoutUnit regionLogicalBottom = isHorizontalWritingMode() ? region->regionRect().maxY() : region->regionRect().maxX();
451 LayoutUnit remainingHeight = regionLogicalBottom - position;
452 if (pageBoundaryRule == IncludePageBoundary) {
453 // If IncludePageBoundary is set, the line exactly on the top edge of a
454 // region will act as being part of the previous region.
455 LayoutUnit regionHeight = isHorizontalWritingMode() ? region->regionRect().height() : region->regionRect().width();
456 remainingHeight = layoutMod(remainingHeight, regionHeight);
458 return remainingHeight;
461 RenderRegion* RenderFlowThread::mapFromFlowToRegion(TransformState& transformState) const
463 if (!hasValidRegionInfo())
466 LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox();
467 flipForWritingMode(boxRect);
469 // FIXME: We need to refactor RenderObject::absoluteQuads to be able to split the quads across regions,
470 // for now we just take the center of the mapped enclosing box and map it to a region.
471 // Note: Using the center in order to avoid rounding errors.
473 LayoutPoint center = boxRect.center();
474 RenderRegion* renderRegion = renderRegionForLine(isHorizontalWritingMode() ? center.y() : center.x(), true);
478 LayoutRect flippedRegionRect(renderRegion->regionRect());
479 flipForWritingMode(flippedRegionRect);
481 transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location());
486 void RenderFlowThread::removeRenderBoxRegionInfo(RenderBox* box)
491 RenderRegion* startRegion;
492 RenderRegion* endRegion;
493 getRegionRangeForBox(box, startRegion, endRegion);
495 for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) {
496 RenderRegion* region = *iter;
497 if (!region->isValid())
499 region->removeRenderBoxRegionInfo(box);
500 if (region == endRegion)
505 // We have to make sure we did not leave any RenderBoxRegionInfo attached.
506 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
507 RenderRegion* region = *iter;
508 if (!region->isValid())
510 ASSERT(!region->renderBoxRegionInfo(box));
514 m_regionRangeMap.remove(box);
517 bool RenderFlowThread::logicalWidthChangedInRegions(const RenderBlock* block, LayoutUnit offsetFromLogicalTopOfFirstPage)
519 if (!hasRegions() || block == this) // Not necessary, since if any region changes, we do a full pagination relayout anyway.
522 RenderRegion* startRegion;
523 RenderRegion* endRegion;
524 getRegionRangeForBox(block, startRegion, endRegion);
526 for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) {
527 RenderRegion* region = *iter;
529 if (!region->isValid())
532 ASSERT(!region->needsLayout());
534 OwnPtr<RenderBoxRegionInfo> oldInfo = region->takeRenderBoxRegionInfo(block);
538 LayoutUnit oldLogicalWidth = oldInfo->logicalWidth();
539 RenderBoxRegionInfo* newInfo = block->renderBoxRegionInfo(region, offsetFromLogicalTopOfFirstPage);
540 if (!newInfo || newInfo->logicalWidth() != oldLogicalWidth)
543 if (region == endRegion)
550 LayoutUnit RenderFlowThread::contentLogicalWidthOfFirstRegion() const
552 if (!hasValidRegionInfo())
554 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
555 RenderRegion* region = *iter;
556 if (!region->isValid())
558 return isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight();
560 ASSERT_NOT_REACHED();
564 LayoutUnit RenderFlowThread::contentLogicalHeightOfFirstRegion() const
566 if (!hasValidRegionInfo())
568 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
569 RenderRegion* region = *iter;
570 if (!region->isValid())
572 return isHorizontalWritingMode() ? region->contentHeight() : region->contentWidth();
574 ASSERT_NOT_REACHED();
578 LayoutUnit RenderFlowThread::contentLogicalLeftOfFirstRegion() const
580 if (!hasValidRegionInfo())
582 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
583 RenderRegion* region = *iter;
584 if (!region->isValid())
586 return isHorizontalWritingMode() ? region->regionRect().x() : region->regionRect().y();
588 ASSERT_NOT_REACHED();
592 RenderRegion* RenderFlowThread::firstRegion() const
594 if (!hasValidRegionInfo())
596 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
597 RenderRegion* region = *iter;
598 if (!region->isValid())
605 RenderRegion* RenderFlowThread::lastRegion() const
607 if (!hasValidRegionInfo())
609 for (RenderRegionList::const_reverse_iterator iter = m_regionList.rbegin(); iter != m_regionList.rend(); ++iter) {
610 RenderRegion* region = *iter;
611 if (!region->isValid())
618 void RenderFlowThread::clearRenderBoxCustomStyle(const RenderBox* box,
619 const RenderRegion* oldStartRegion, const RenderRegion* oldEndRegion,
620 const RenderRegion* newStartRegion, const RenderRegion* newEndRegion)
622 // Clear the styles for the object in the regions.
623 // The styles are not cleared for the regions that are contained in both ranges.
624 bool insideOldRegionRange = false;
625 bool insideNewRegionRange = false;
626 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
627 RenderRegion* region = *iter;
629 if (oldStartRegion == region)
630 insideOldRegionRange = true;
631 if (newStartRegion == region)
632 insideNewRegionRange = true;
634 if (!(insideOldRegionRange && insideNewRegionRange))
635 region->clearBoxStyleInRegion(box);
637 if (oldEndRegion == region)
638 insideOldRegionRange = false;
639 if (newEndRegion == region)
640 insideNewRegionRange = false;
644 void RenderFlowThread::setRegionRangeForBox(const RenderBox* box, LayoutUnit offsetFromLogicalTopOfFirstPage)
649 // FIXME: Not right for differing writing-modes.
650 RenderRegion* startRegion = renderRegionForLine(offsetFromLogicalTopOfFirstPage, true);
651 RenderRegion* endRegion = renderRegionForLine(offsetFromLogicalTopOfFirstPage + box->logicalHeight(), true);
652 RenderRegionRangeMap::iterator it = m_regionRangeMap.find(box);
653 if (it == m_regionRangeMap.end()) {
654 m_regionRangeMap.set(box, RenderRegionRange(startRegion, endRegion));
658 // If nothing changed, just bail.
659 RenderRegionRange& range = it->second;
660 if (range.startRegion() == startRegion && range.endRegion() == endRegion)
663 // Delete any info that we find before our new startRegion and after our new endRegion.
664 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
665 RenderRegion* region = *iter;
666 if (region == startRegion) {
667 iter = m_regionList.find(endRegion);
671 region->removeRenderBoxRegionInfo(box);
673 if (region == range.endRegion())
677 clearRenderBoxCustomStyle(box, range.startRegion(), range.endRegion(), startRegion, endRegion);
678 range.setRange(startRegion, endRegion);
681 void RenderFlowThread::getRegionRangeForBox(const RenderBox* box, RenderRegion*& startRegion, RenderRegion*& endRegion) const
685 RenderRegionRangeMap::const_iterator it = m_regionRangeMap.find(box);
686 if (it == m_regionRangeMap.end())
689 const RenderRegionRange& range = it->second;
690 startRegion = range.startRegion();
691 endRegion = range.endRegion();
692 ASSERT(m_regionList.contains(startRegion) && m_regionList.contains(endRegion));
695 void RenderFlowThread::computeOverflowStateForRegions(LayoutUnit oldClientAfterEdge)
697 LayoutUnit height = oldClientAfterEdge;
698 // FIXME: the visual overflow of middle region (if it is the last one to contain any content in a render flow thread)
699 // might not be taken into account because the render flow thread height is greater that that regions height + its visual overflow
700 // because of how computeLogicalHeight is implemented for RenderFlowThread (as a sum of all regions height).
701 // This means that the middle region will be marked as fit (even if it has visual overflow flowing into the next region)
702 if (hasRenderOverflow())
703 height = isHorizontalWritingMode() ? visualOverflowRect().maxY() : visualOverflowRect().maxX();
705 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
706 RenderRegion* region = *iter;
707 if (!region->isValid()) {
708 region->setRegionState(RenderRegion::RegionUndefined);
711 LayoutUnit flowMin = height - (isHorizontalWritingMode() ? region->regionRect().y() : region->regionRect().x());
712 LayoutUnit flowMax = height - (isHorizontalWritingMode() ? region->regionRect().maxY() : region->regionRect().maxX());
713 RenderRegion::RegionState previousState = region->regionState();
714 RenderRegion::RegionState state = RenderRegion::RegionFit;
716 state = RenderRegion::RegionEmpty;
718 state = RenderRegion::RegionOverflow;
719 region->setRegionState(state);
720 // determine whether this region should dispatch a regionLayoutUpdate event
721 // FIXME: currently it cannot determine whether a region whose regionOverflow state remained either "fit" or "overflow" has actually
722 // changed, so it just assumes that those region should dispatch the event
723 if (previousState != state
724 || state == RenderRegion::RegionFit
725 || state == RenderRegion::RegionOverflow)
726 region->setDispatchRegionLayoutUpdateEvent(true);
729 // With the regions overflow state computed we can also set the overflow for the named flow.
730 RenderRegion* lastReg = lastRegion();
731 m_overflow = lastReg && (lastReg->regionState() == RenderRegion::RegionOverflow);
734 void RenderFlowThread::regionLayoutUpdateEventTimerFired(Timer<RenderFlowThread>*)
736 // Create a copy of region nodes, to protect them for being destroyed in the event listener
737 Vector<RefPtr<Node> > regionNodes;
738 regionNodes.reserveCapacity(m_regionList.size());
739 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
740 RenderRegion* region = *iter;
741 ASSERT(region->node() && region->node()->isElementNode());
742 // dispatch the event only for marked regions and only for those who have a listener
743 if (region->shouldDispatchRegionLayoutUpdateEvent()) {
744 regionNodes.append(region->node());
745 // clear the dispatch flag here, as it is possible to be set again due to event listeners
746 region->setDispatchRegionLayoutUpdateEvent(false);
749 for (Vector<RefPtr<Node> >::const_iterator it = regionNodes.begin(); it != regionNodes.end(); ++it) {
750 RefPtr<Node> node = *it;
751 RefPtr<Document> document = node->document();
754 RenderObject* renderer = node->renderer();
755 if (renderer && renderer->isRenderRegion()) {
756 node->dispatchRegionLayoutUpdateEvent();
757 // Layout needs to be uptodate after each event listener
758 document->updateLayoutIgnorePendingStylesheets();
763 bool RenderFlowThread::regionInRange(const RenderRegion* targetRegion, const RenderRegion* startRegion, const RenderRegion* endRegion) const
765 ASSERT(targetRegion);
767 for (RenderRegionList::const_iterator it = m_regionList.find(const_cast<RenderRegion*>(startRegion)); it != m_regionList.end(); ++it) {
768 const RenderRegion* currRegion = *it;
769 if (!currRegion->isValid())
771 if (targetRegion == currRegion)
773 if (currRegion == endRegion)
780 bool RenderFlowThread::objectInFlowRegion(const RenderObject* object, const RenderRegion* region) const
785 if (!object->inRenderFlowThread())
787 if (object->enclosingRenderFlowThread() != this)
789 if (!m_regionList.contains(const_cast<RenderRegion*>(region)))
792 RenderBox* enclosingBox = object->enclosingBox();
793 RenderRegion* enclosingBoxStartRegion = 0;
794 RenderRegion* enclosingBoxEndRegion = 0;
795 getRegionRangeForBox(enclosingBox, enclosingBoxStartRegion, enclosingBoxEndRegion);
796 if (!regionInRange(region, enclosingBoxStartRegion, enclosingBoxEndRegion))
802 LayoutRect objectABBRect = object->absoluteBoundingBoxRect(true);
803 if (!objectABBRect.width())
804 objectABBRect.setWidth(1);
805 if (!objectABBRect.height())
806 objectABBRect.setHeight(1);
807 if (objectABBRect.intersects(region->absoluteBoundingBoxRect(true)))
810 if (region == lastRegion()) {
811 // If the object does not intersect any of the enclosing box regions
812 // then the object is in last region.
813 for (RenderRegionList::const_iterator it = m_regionList.find(enclosingBoxStartRegion); it != m_regionList.end(); ++it) {
814 const RenderRegion* currRegion = *it;
815 if (!region->isValid())
817 if (currRegion == region)
819 if (objectABBRect.intersects(currRegion->absoluteBoundingBoxRect(true)))
828 } // namespace WebCore