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 clearRenderObjectCustomStyle(child);
93 void RenderFlowThread::addRegionToThread(RenderRegion* renderRegion)
96 m_regionList.add(renderRegion);
97 renderRegion->setIsValid(true);
99 checkRegionsWithStyling();
102 void RenderFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
104 ASSERT(renderRegion);
105 m_regionRangeMap.clear();
106 m_regionList.remove(renderRegion);
108 checkRegionsWithStyling();
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 = region->logicalWidthForFlowThreadContent();
173 LayoutUnit regionLogicalHeight = region->logicalHeightForFlowThreadContent();
175 if (!m_hasValidRegions)
176 m_hasValidRegions = true;
178 if (m_regionsHaveUniformLogicalWidth && previousRegionLogicalWidth != regionLogicalWidth)
179 m_regionsHaveUniformLogicalWidth = false;
180 if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
181 m_regionsHaveUniformLogicalHeight = false;
184 previousRegionLogicalWidth = regionLogicalWidth;
187 computeLogicalWidth(); // Called to get the maximum logical width for the region.
189 LayoutUnit logicalHeight = 0;
190 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
191 RenderRegion* region = *iter;
192 if (!region->isValid())
195 LayoutUnit regionLogicalWidth = region->logicalWidthForFlowThreadContent();
196 LayoutUnit regionLogicalHeight = region->logicalHeightForFlowThreadContent();
198 LayoutRect regionRect(style()->direction() == LTR ? ZERO_LAYOUT_UNIT : logicalWidth() - regionLogicalWidth, logicalHeight, regionLogicalWidth, regionLogicalHeight);
199 region->setRegionRect(isHorizontalWritingMode() ? regionRect : regionRect.transposedRect());
200 logicalHeight += regionLogicalHeight;
205 CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
206 LayoutStateMaintainer statePusher(view(), this, regionsChanged);
207 RenderBlock::layout();
209 if (document()->hasListenerType(Document::REGIONLAYOUTUPDATE_LISTENER) && !m_regionLayoutUpdateEventTimer.isActive())
210 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
211 RenderRegion* region = *iter;
212 if (region->shouldDispatchRegionLayoutUpdateEvent()) {
213 // at least one region needs to dispatch the event
214 m_regionLayoutUpdateEventTimer.startOneShot(0);
220 void RenderFlowThread::computeLogicalWidth()
222 LayoutUnit logicalWidth = 0;
223 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
224 RenderRegion* region = *iter;
225 if (!region->isValid())
227 ASSERT(!region->needsLayout());
228 logicalWidth = max(region->logicalWidthForFlowThreadContent(), logicalWidth);
230 setLogicalWidth(logicalWidth);
232 // If the regions have non-uniform logical widths, then insert inset information for the RenderFlowThread.
233 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
234 RenderRegion* region = *iter;
235 if (!region->isValid())
238 LayoutUnit regionLogicalWidth = region->logicalWidthForFlowThreadContent();
239 if (regionLogicalWidth != logicalWidth) {
240 LayoutUnit logicalLeft = style()->direction() == LTR ? ZERO_LAYOUT_UNIT : logicalWidth - regionLogicalWidth;
241 region->setRenderBoxRegionInfo(this, logicalLeft, regionLogicalWidth, false);
246 void RenderFlowThread::computeLogicalHeight()
248 LayoutUnit logicalHeight = 0;
250 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
251 RenderRegion* region = *iter;
252 if (!region->isValid())
254 ASSERT(!region->needsLayout());
255 logicalHeight += region->logicalHeightForFlowThreadContent();
258 setLogicalHeight(logicalHeight);
261 void RenderFlowThread::paintIntoRegion(PaintInfo& paintInfo, RenderRegion* region, const LayoutPoint& paintOffset)
263 GraphicsContext* context = paintInfo.context;
267 // Adjust the clipping rect for the region.
268 // paintOffset contains the offset where the painting should occur
269 // adjusted with the region padding and border.
270 LayoutRect regionRect(region->regionRect());
271 LayoutRect regionOverflowRect(region->regionOverflowRect());
272 LayoutRect regionClippingRect(paintOffset + (regionOverflowRect.location() - regionRect.location()), regionOverflowRect.size());
274 PaintInfo info(paintInfo);
275 info.rect.intersect(pixelSnappedIntRect(regionClippingRect));
277 if (!info.rect.isEmpty()) {
280 context->clip(regionClippingRect);
282 // RenderFlowThread should start painting its content in a position that is offset
283 // from the region rect's current position. The amount of offset is equal to the location of
284 // region in flow coordinates.
285 IntPoint renderFlowThreadOffset;
286 if (style()->isFlippedBlocksWritingMode()) {
287 LayoutRect flippedRegionRect(regionRect);
288 flipForWritingMode(flippedRegionRect);
289 renderFlowThreadOffset = roundedIntPoint(paintOffset - flippedRegionRect.location());
291 renderFlowThreadOffset = roundedIntPoint(paintOffset - regionRect.location());
293 context->translate(renderFlowThreadOffset.x(), renderFlowThreadOffset.y());
294 info.rect.moveBy(-renderFlowThreadOffset);
296 layer()->paint(context, info.rect, 0, 0, region, RenderLayer::PaintLayerTemporaryClipRects);
302 bool RenderFlowThread::hitTestRegion(RenderRegion* region, const HitTestRequest& request, HitTestResult& result, const HitTestPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
304 LayoutRect regionRect(region->regionRect());
305 LayoutRect regionOverflowRect = region->regionOverflowRect();
306 LayoutRect regionClippingRect(accumulatedOffset + (regionOverflowRect.location() - regionRect.location()), regionOverflowRect.size());
307 if (!regionClippingRect.contains(pointInContainer.point()))
310 LayoutSize renderFlowThreadOffset;
311 if (style()->isFlippedBlocksWritingMode()) {
312 LayoutRect flippedRegionRect(regionRect);
313 flipForWritingMode(flippedRegionRect);
314 renderFlowThreadOffset = accumulatedOffset - flippedRegionRect.location();
316 renderFlowThreadOffset = accumulatedOffset - regionRect.location();
318 // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView.
319 HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping);
321 // Make a new temporary hitTestPoint in the new region.
322 HitTestPoint newHitTestPoint(pointInContainer, -renderFlowThreadOffset, region);
324 bool isPointInsideFlowThread = layer()->hitTest(newRequest, newHitTestPoint, result);
326 // FIXME: Should we set result.m_localPoint back to the RenderRegion's coordinate space or leave it in the RenderFlowThread's coordinate
327 // 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
328 // patching positionForPoint.
329 return isPointInsideFlowThread;
332 bool RenderFlowThread::shouldRepaint(const LayoutRect& r) const
334 if (view()->printing() || r.isEmpty())
340 void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect, bool immediate)
342 if (!shouldRepaint(repaintRect) || !hasValidRegionInfo())
345 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
346 RenderRegion* region = *iter;
347 if (!region->isValid())
350 // We only have to issue a repaint in this region if the region rect intersects the repaint rect.
351 LayoutRect flippedRegionRect(region->regionRect());
352 LayoutRect flippedRegionOverflowRect(region->regionOverflowRect());
353 flipForWritingMode(flippedRegionRect); // Put the region rects into physical coordinates.
354 flipForWritingMode(flippedRegionOverflowRect);
356 LayoutRect clippedRect(repaintRect);
357 clippedRect.intersect(flippedRegionOverflowRect);
358 if (clippedRect.isEmpty())
361 // Put the region rect into the region's physical coordinate space.
362 clippedRect.setLocation(region->contentBoxRect().location() + (clippedRect.location() - flippedRegionRect.location()));
364 // Now switch to the region's writing mode coordinate space and let it repaint itself.
365 region->flipForWritingMode(clippedRect);
366 LayoutStateDisabler layoutStateDisabler(view()); // We can't use layout state to repaint, since the region is somewhere else.
368 // Can't use currentFlowThread as it possible to have imbricated flow threads and the wrong one could be used,
369 // so, we let each region figure out the proper enclosing flow thread
370 CurrentRenderFlowThreadDisabler disabler(view());
371 region->repaintRectangle(clippedRect, immediate);
375 RenderRegion* RenderFlowThread::renderRegionForLine(LayoutUnit position, bool extendLastRegion) const
377 ASSERT(!m_regionsInvalidated);
379 // If no region matches the position and extendLastRegion is true, it will return
380 // the last valid region. It is similar to auto extending the size of the last region.
381 RenderRegion* lastValidRegion = 0;
383 // FIXME: The regions are always in order, optimize this search.
384 bool useHorizontalWritingMode = isHorizontalWritingMode();
385 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
386 RenderRegion* region = *iter;
387 if (!region->isValid())
393 LayoutRect regionRect = region->regionRect();
395 if ((useHorizontalWritingMode && position < regionRect.maxY()) || (!useHorizontalWritingMode && position < regionRect.maxX()))
398 if (extendLastRegion)
399 lastValidRegion = region;
402 return lastValidRegion;
405 LayoutUnit RenderFlowThread::regionLogicalTopForLine(LayoutUnit position) const
407 RenderRegion* region = renderRegionForLine(position);
410 return isHorizontalWritingMode() ? region->regionRect().y() : region->regionRect().x();
413 LayoutUnit RenderFlowThread::regionLogicalWidthForLine(LayoutUnit position) const
415 RenderRegion* region = renderRegionForLine(position, true);
417 return contentLogicalWidth();
418 return isHorizontalWritingMode() ? region->regionRect().width() : region->regionRect().height();
421 LayoutUnit RenderFlowThread::regionLogicalHeightForLine(LayoutUnit position) const
423 RenderRegion* region = renderRegionForLine(position);
426 return isHorizontalWritingMode() ? region->regionRect().height() : region->regionRect().width();
429 LayoutUnit RenderFlowThread::regionRemainingLogicalHeightForLine(LayoutUnit position, PageBoundaryRule pageBoundaryRule) const
431 RenderRegion* region = renderRegionForLine(position);
435 LayoutUnit regionLogicalBottom = isHorizontalWritingMode() ? region->regionRect().maxY() : region->regionRect().maxX();
436 LayoutUnit remainingHeight = regionLogicalBottom - position;
437 if (pageBoundaryRule == IncludePageBoundary) {
438 // If IncludePageBoundary is set, the line exactly on the top edge of a
439 // region will act as being part of the previous region.
440 LayoutUnit regionHeight = isHorizontalWritingMode() ? region->regionRect().height() : region->regionRect().width();
441 remainingHeight = layoutMod(remainingHeight, regionHeight);
443 return remainingHeight;
446 RenderRegion* RenderFlowThread::mapFromFlowToRegion(TransformState& transformState) const
448 if (!hasValidRegionInfo())
451 LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox();
452 flipForWritingMode(boxRect);
454 // FIXME: We need to refactor RenderObject::absoluteQuads to be able to split the quads across regions,
455 // for now we just take the center of the mapped enclosing box and map it to a region.
456 // Note: Using the center in order to avoid rounding errors.
458 LayoutPoint center = boxRect.center();
459 RenderRegion* renderRegion = renderRegionForLine(isHorizontalWritingMode() ? center.y() : center.x(), true);
463 LayoutRect flippedRegionRect(renderRegion->regionRect());
464 flipForWritingMode(flippedRegionRect);
466 transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location());
471 void RenderFlowThread::removeRenderBoxRegionInfo(RenderBox* box)
476 RenderRegion* startRegion;
477 RenderRegion* endRegion;
478 getRegionRangeForBox(box, startRegion, endRegion);
480 for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) {
481 RenderRegion* region = *iter;
482 if (!region->isValid())
484 region->removeRenderBoxRegionInfo(box);
485 if (region == endRegion)
490 // We have to make sure we did not leave any RenderBoxRegionInfo attached.
491 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
492 RenderRegion* region = *iter;
493 if (!region->isValid())
495 ASSERT(!region->renderBoxRegionInfo(box));
499 m_regionRangeMap.remove(box);
502 bool RenderFlowThread::logicalWidthChangedInRegions(const RenderBlock* block, LayoutUnit offsetFromLogicalTopOfFirstPage)
504 if (!hasRegions() || block == this) // Not necessary, since if any region changes, we do a full pagination relayout anyway.
507 RenderRegion* startRegion;
508 RenderRegion* endRegion;
509 getRegionRangeForBox(block, startRegion, endRegion);
511 for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) {
512 RenderRegion* region = *iter;
514 if (!region->isValid())
517 ASSERT(!region->needsLayout());
519 OwnPtr<RenderBoxRegionInfo> oldInfo = region->takeRenderBoxRegionInfo(block);
523 LayoutUnit oldLogicalWidth = oldInfo->logicalWidth();
524 RenderBoxRegionInfo* newInfo = block->renderBoxRegionInfo(region, offsetFromLogicalTopOfFirstPage);
525 if (!newInfo || newInfo->logicalWidth() != oldLogicalWidth)
528 if (region == endRegion)
535 LayoutUnit RenderFlowThread::contentLogicalWidthOfFirstRegion() const
537 if (!hasValidRegionInfo())
539 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
540 RenderRegion* region = *iter;
541 if (!region->isValid())
543 return isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight();
545 ASSERT_NOT_REACHED();
549 LayoutUnit RenderFlowThread::contentLogicalHeightOfFirstRegion() const
551 if (!hasValidRegionInfo())
553 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
554 RenderRegion* region = *iter;
555 if (!region->isValid())
557 return isHorizontalWritingMode() ? region->contentHeight() : region->contentWidth();
559 ASSERT_NOT_REACHED();
563 LayoutUnit RenderFlowThread::contentLogicalLeftOfFirstRegion() const
565 if (!hasValidRegionInfo())
567 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
568 RenderRegion* region = *iter;
569 if (!region->isValid())
571 return isHorizontalWritingMode() ? region->regionRect().x() : region->regionRect().y();
573 ASSERT_NOT_REACHED();
577 RenderRegion* RenderFlowThread::firstRegion() const
579 if (!hasValidRegionInfo())
581 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
582 RenderRegion* region = *iter;
583 if (!region->isValid())
590 RenderRegion* RenderFlowThread::lastRegion() const
592 if (!hasValidRegionInfo())
594 for (RenderRegionList::const_reverse_iterator iter = m_regionList.rbegin(); iter != m_regionList.rend(); ++iter) {
595 RenderRegion* region = *iter;
596 if (!region->isValid())
603 void RenderFlowThread::clearRenderObjectCustomStyle(const RenderObject* object,
604 const RenderRegion* oldStartRegion, const RenderRegion* oldEndRegion,
605 const RenderRegion* newStartRegion, const RenderRegion* newEndRegion)
607 // Clear the styles for the object in the regions.
608 // The styles are not cleared for the regions that are contained in both ranges.
609 bool insideOldRegionRange = false;
610 bool insideNewRegionRange = false;
611 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
612 RenderRegion* region = *iter;
614 if (oldStartRegion == region)
615 insideOldRegionRange = true;
616 if (newStartRegion == region)
617 insideNewRegionRange = true;
619 if (!(insideOldRegionRange && insideNewRegionRange))
620 region->clearObjectStyleInRegion(object);
622 if (oldEndRegion == region)
623 insideOldRegionRange = false;
624 if (newEndRegion == region)
625 insideNewRegionRange = false;
629 void RenderFlowThread::setRegionRangeForBox(const RenderBox* box, LayoutUnit offsetFromLogicalTopOfFirstPage)
634 // FIXME: Not right for differing writing-modes.
635 RenderRegion* startRegion = renderRegionForLine(offsetFromLogicalTopOfFirstPage, true);
636 RenderRegion* endRegion = renderRegionForLine(offsetFromLogicalTopOfFirstPage + box->logicalHeight(), true);
637 RenderRegionRangeMap::iterator it = m_regionRangeMap.find(box);
638 if (it == m_regionRangeMap.end()) {
639 m_regionRangeMap.set(box, RenderRegionRange(startRegion, endRegion));
640 clearRenderObjectCustomStyle(box);
644 // If nothing changed, just bail.
645 RenderRegionRange& range = it->second;
646 if (range.startRegion() == startRegion && range.endRegion() == endRegion)
649 // Delete any info that we find before our new startRegion and after our new endRegion.
650 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
651 RenderRegion* region = *iter;
652 if (region == startRegion) {
653 iter = m_regionList.find(endRegion);
657 region->removeRenderBoxRegionInfo(box);
659 if (region == range.endRegion())
663 clearRenderObjectCustomStyle(box, range.startRegion(), range.endRegion(), startRegion, endRegion);
664 range.setRange(startRegion, endRegion);
667 void RenderFlowThread::getRegionRangeForBox(const RenderBox* box, RenderRegion*& startRegion, RenderRegion*& endRegion) const
671 RenderRegionRangeMap::const_iterator it = m_regionRangeMap.find(box);
672 if (it == m_regionRangeMap.end())
675 const RenderRegionRange& range = it->second;
676 startRegion = range.startRegion();
677 endRegion = range.endRegion();
678 ASSERT(m_regionList.contains(startRegion) && m_regionList.contains(endRegion));
681 void RenderFlowThread::computeOverflowStateForRegions(LayoutUnit oldClientAfterEdge)
683 LayoutUnit height = oldClientAfterEdge;
684 // FIXME: the visual overflow of middle region (if it is the last one to contain any content in a render flow thread)
685 // might not be taken into account because the render flow thread height is greater that that regions height + its visual overflow
686 // because of how computeLogicalHeight is implemented for RenderFlowThread (as a sum of all regions height).
687 // This means that the middle region will be marked as fit (even if it has visual overflow flowing into the next region)
688 if (hasRenderOverflow())
689 height = isHorizontalWritingMode() ? visualOverflowRect().maxY() : visualOverflowRect().maxX();
691 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
692 RenderRegion* region = *iter;
693 if (!region->isValid()) {
694 region->setRegionState(RenderRegion::RegionUndefined);
697 LayoutUnit flowMin = height - (isHorizontalWritingMode() ? region->regionRect().y() : region->regionRect().x());
698 LayoutUnit flowMax = height - (isHorizontalWritingMode() ? region->regionRect().maxY() : region->regionRect().maxX());
699 RenderRegion::RegionState previousState = region->regionState();
700 RenderRegion::RegionState state = RenderRegion::RegionFit;
702 state = RenderRegion::RegionEmpty;
704 state = RenderRegion::RegionOverflow;
705 region->setRegionState(state);
706 // determine whether this region should dispatch a regionLayoutUpdate event
707 // FIXME: currently it cannot determine whether a region whose regionOverflow state remained either "fit" or "overflow" has actually
708 // changed, so it just assumes that those region should dispatch the event
709 if (previousState != state
710 || state == RenderRegion::RegionFit
711 || state == RenderRegion::RegionOverflow)
712 region->setDispatchRegionLayoutUpdateEvent(true);
715 // With the regions overflow state computed we can also set the overset flag for the named flow.
716 // If there are no valid regions in the chain, overset is true
717 RenderRegion* lastReg = lastRegion();
718 m_overset = lastReg ? lastReg->regionState() == RenderRegion::RegionOverflow : true;
721 void RenderFlowThread::regionLayoutUpdateEventTimerFired(Timer<RenderFlowThread>*)
723 // Create a copy of region nodes, to protect them for being destroyed in the event listener
724 Vector<RefPtr<Node> > regionNodes;
725 regionNodes.reserveCapacity(m_regionList.size());
726 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
727 RenderRegion* region = *iter;
728 ASSERT(region->node() && region->node()->isElementNode());
729 // dispatch the event only for marked regions and only for those who have a listener
730 if (region->shouldDispatchRegionLayoutUpdateEvent()) {
731 regionNodes.append(region->node());
732 // clear the dispatch flag here, as it is possible to be set again due to event listeners
733 region->setDispatchRegionLayoutUpdateEvent(false);
736 for (Vector<RefPtr<Node> >::const_iterator it = regionNodes.begin(); it != regionNodes.end(); ++it) {
737 RefPtr<Node> node = *it;
738 RefPtr<Document> document = node->document();
741 RenderObject* renderer = node->renderer();
742 if (renderer && renderer->isRenderRegion()) {
743 node->dispatchRegionLayoutUpdateEvent();
744 // Layout needs to be uptodate after each event listener
745 document->updateLayoutIgnorePendingStylesheets();
750 bool RenderFlowThread::regionInRange(const RenderRegion* targetRegion, const RenderRegion* startRegion, const RenderRegion* endRegion) const
752 ASSERT(targetRegion);
754 for (RenderRegionList::const_iterator it = m_regionList.find(const_cast<RenderRegion*>(startRegion)); it != m_regionList.end(); ++it) {
755 const RenderRegion* currRegion = *it;
756 if (!currRegion->isValid())
758 if (targetRegion == currRegion)
760 if (currRegion == endRegion)
767 // Check if the content is flown into at least a region with region styling rules.
768 void RenderFlowThread::checkRegionsWithStyling()
770 bool hasRegionsWithStyling = false;
771 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
772 RenderRegion* region = *iter;
773 if (!region->isValid())
775 if (region->hasCustomRegionStyle()) {
776 hasRegionsWithStyling = true;
780 m_hasRegionsWithStyling = hasRegionsWithStyling;
783 bool RenderFlowThread::objectInFlowRegion(const RenderObject* object, const RenderRegion* region) const
788 if (!object->inRenderFlowThread())
790 if (object->enclosingRenderFlowThread() != this)
792 if (!m_regionList.contains(const_cast<RenderRegion*>(region)))
795 RenderBox* enclosingBox = object->enclosingBox();
796 RenderRegion* enclosingBoxStartRegion = 0;
797 RenderRegion* enclosingBoxEndRegion = 0;
798 getRegionRangeForBox(enclosingBox, enclosingBoxStartRegion, enclosingBoxEndRegion);
799 if (!regionInRange(region, enclosingBoxStartRegion, enclosingBoxEndRegion))
805 LayoutRect objectABBRect = object->absoluteBoundingBoxRect(true);
806 if (!objectABBRect.width())
807 objectABBRect.setWidth(1);
808 if (!objectABBRect.height())
809 objectABBRect.setHeight(1);
810 if (objectABBRect.intersects(region->absoluteBoundingBoxRect(true)))
813 if (region == lastRegion()) {
814 // If the object does not intersect any of the enclosing box regions
815 // then the object is in last region.
816 for (RenderRegionList::const_iterator it = m_regionList.find(enclosingBoxStartRegion); it != m_regionList.end(); ++it) {
817 const RenderRegion* currRegion = *it;
818 if (!region->isValid())
820 if (currRegion == region)
822 if (objectABBRect.intersects(currRegion->absoluteBoundingBoxRect(true)))
831 } // namespace WebCore