[CSS Regions]Add helper class for flow threads info in RenderView
[WebKit-https.git] / Source / WebCore / rendering / RenderFlowThread.cpp
1 /*
2  * Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above
9  *    copyright notice, this list of conditions and the following
10  *    disclaimer.
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.
15  * 
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
27  * SUCH DAMAGE.
28  */
29
30 #include "config.h"
31
32 #include "RenderFlowThread.h"
33
34 #include "FlowThreadController.h"
35 #include "HitTestRequest.h"
36 #include "HitTestResult.h"
37 #include "Node.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"
45
46 namespace WebCore {
47
48 RenderFlowThread::RenderFlowThread(Node* node)
49     : RenderBlock(node)
50     , m_hasValidRegions(false)
51     , m_regionsInvalidated(false)
52     , m_regionsHaveUniformLogicalWidth(true)
53     , m_regionsHaveUniformLogicalHeight(true)
54     , m_overflow(false)
55     , m_regionLayoutUpdateEventTimer(this, &RenderFlowThread::regionLayoutUpdateEventTimerFired)
56 {
57     ASSERT(node->document()->cssRegionsEnabled());
58     setIsAnonymous(false);
59     setInRenderFlowThread();
60 }
61
62 PassRefPtr<RenderStyle> RenderFlowThread::createFlowThreadStyle(RenderStyle* parentStyle)
63 {
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);
74     
75     return newStyle.release();
76 }
77
78 void RenderFlowThread::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
79 {
80     RenderBlock::styleDidChange(diff, oldStyle);
81
82     if (oldStyle && oldStyle->writingMode() != style()->writingMode())
83         m_regionsInvalidated = true;
84 }
85
86 void RenderFlowThread::removeFlowChildInfo(RenderObject* child)
87 {
88     if (child->isBox()) {
89         removeRenderBoxRegionInfo(toRenderBox(child));
90         if (child->canHaveRegionStyle())
91             clearRenderBoxCustomStyle(toRenderBox(child));
92     }
93 }
94
95 void RenderFlowThread::addRegionToThread(RenderRegion* renderRegion)
96 {
97     ASSERT(renderRegion);
98     m_regionList.add(renderRegion);
99     renderRegion->setIsValid(true);
100     invalidateRegions();
101 }
102
103 void RenderFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
104 {
105     ASSERT(renderRegion);
106     m_regionRangeMap.clear();
107     m_regionList.remove(renderRegion);
108     invalidateRegions();
109 }
110
111 class CurrentRenderFlowThreadMaintainer {
112     WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadMaintainer);
113 public:
114     CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread)
115         : m_renderFlowThread(renderFlowThread)
116     {
117         RenderView* view = m_renderFlowThread->view();
118         ASSERT(!view->flowThreadController()->currentRenderFlowThread());
119         view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
120     }
121     ~CurrentRenderFlowThreadMaintainer()
122     {
123         RenderView* view = m_renderFlowThread->view();
124         ASSERT(view->flowThreadController()->currentRenderFlowThread() == m_renderFlowThread);
125         view->flowThreadController()->setCurrentRenderFlowThread(0);
126     }
127 private:
128     RenderFlowThread* m_renderFlowThread;
129 };
130
131 class CurrentRenderFlowThreadDisabler {
132     WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadDisabler);
133 public:
134     CurrentRenderFlowThreadDisabler(RenderView* view)
135         : m_view(view)
136         , m_renderFlowThread(0)
137     {
138         m_renderFlowThread = m_view->flowThreadController()->currentRenderFlowThread();
139         if (m_renderFlowThread)
140             view->flowThreadController()->setCurrentRenderFlowThread(0);
141     }
142     ~CurrentRenderFlowThreadDisabler()
143     {
144         if (m_renderFlowThread)
145             m_view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
146     }
147 private:
148     RenderView* m_view;
149     RenderFlowThread* m_renderFlowThread;
150 };
151
152 void RenderFlowThread::layout()
153 {
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;
163         if (hasRegions()) {
164             for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
165                 RenderRegion* region = *iter;
166                 if (!region->isValid())
167                     continue;
168                 ASSERT(!region->needsLayout());
169                 
170                 region->deleteAllRenderBoxRegionInfo();
171
172                 LayoutUnit regionLogicalWidth;
173                 LayoutUnit regionLogicalHeight;
174
175                 if (isHorizontalWritingMode()) {
176                     regionLogicalWidth = region->contentWidth();
177                     regionLogicalHeight = region->contentHeight();
178                 } else {
179                     regionLogicalWidth = region->contentHeight();
180                     regionLogicalHeight = region->contentWidth();
181                 }
182
183                 if (!m_hasValidRegions)
184                     m_hasValidRegions = true;
185                 else {
186                     if (m_regionsHaveUniformLogicalWidth && previousRegionLogicalWidth != regionLogicalWidth)
187                         m_regionsHaveUniformLogicalWidth = false;
188                     if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
189                         m_regionsHaveUniformLogicalHeight = false;
190                 }
191
192                 previousRegionLogicalWidth = regionLogicalWidth;
193             }
194             
195             computeLogicalWidth(); // Called to get the maximum logical width for the region.
196             
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())
201                     continue;
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();
206                 } else {
207                     regionRect = LayoutRect(logicalHeight, style()->direction() == LTR ? ZERO_LAYOUT_UNIT : logicalWidth() - region->contentHeight(), region->contentWidth(), region->contentHeight());
208                     logicalHeight += regionRect.width();
209                 }
210                 region->setRegionRect(regionRect);
211             }
212         }
213     }
214
215     CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
216     LayoutStateMaintainer statePusher(view(), this, regionsChanged);
217     RenderBlock::layout();
218     statePusher.pop();
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);
225                 break;
226             }
227         }
228 }
229
230 void RenderFlowThread::computeLogicalWidth()
231 {
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())
236             continue;
237         ASSERT(!region->needsLayout());
238         logicalWidth = max(isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight(), logicalWidth);
239     }
240     setLogicalWidth(logicalWidth);
241
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())
246             continue;
247         
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);
252         }
253     }
254 }
255
256 void RenderFlowThread::computeLogicalHeight()
257 {
258     LayoutUnit logicalHeight = 0;
259
260     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
261         RenderRegion* region = *iter;
262         if (!region->isValid())
263             continue;
264         ASSERT(!region->needsLayout());
265         logicalHeight += isHorizontalWritingMode() ? region->contentHeight() : region->contentWidth();
266     }
267
268     setLogicalHeight(logicalHeight);
269 }
270
271 void RenderFlowThread::paintIntoRegion(PaintInfo& paintInfo, RenderRegion* region, const LayoutPoint& paintOffset)
272 {
273     GraphicsContext* context = paintInfo.context;
274     if (!context)
275         return;
276
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());
283
284     PaintInfo info(paintInfo);
285     info.rect.intersect(pixelSnappedIntRect(regionClippingRect));
286
287     if (!info.rect.isEmpty()) {
288         context->save();
289
290         context->clip(regionClippingRect);
291
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());
300         } else
301             renderFlowThreadOffset = LayoutPoint(paintOffset - regionRect.location());
302
303         context->translate(renderFlowThreadOffset.x(), renderFlowThreadOffset.y());
304         info.rect.moveBy(-roundedIntPoint(renderFlowThreadOffset));
305         
306         layer()->paint(context, info.rect, 0, 0, region, RenderLayer::PaintLayerTemporaryClipRects);
307
308         context->restore();
309     }
310 }
311
312 bool RenderFlowThread::hitTestRegion(RenderRegion* region, const HitTestRequest& request, HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
313 {
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))
318         return false;
319     
320     LayoutPoint renderFlowThreadOffset;
321     if (style()->isFlippedBlocksWritingMode()) {
322         LayoutRect flippedRegionRect(regionRect);
323         flipForWritingMode(flippedRegionRect);
324         renderFlowThreadOffset = LayoutPoint(accumulatedOffset - flippedRegionRect.location());
325     } else
326         renderFlowThreadOffset = LayoutPoint(accumulatedOffset - regionRect.location());
327
328     LayoutPoint transformedPoint(pointInContainer.x() - renderFlowThreadOffset.x(), pointInContainer.y() - renderFlowThreadOffset.y());
329     
330     // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView.
331     HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping);
332
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);
340
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;
345 }
346
347 bool RenderFlowThread::shouldRepaint(const LayoutRect& r) const
348 {
349     if (view()->printing() || r.isEmpty())
350         return false;
351
352     return true;
353 }
354
355 void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect, bool immediate)
356 {
357     if (!shouldRepaint(repaintRect) || !hasValidRegionInfo())
358         return;
359
360     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
361         RenderRegion* region = *iter;
362         if (!region->isValid())
363             continue;
364
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);
370
371         LayoutRect clippedRect(repaintRect);
372         clippedRect.intersect(flippedRegionOverflowRect);
373         if (clippedRect.isEmpty())
374             continue;
375
376         // Put the region rect into the region's physical coordinate space.
377         clippedRect.setLocation(region->contentBoxRect().location() + (clippedRect.location() - flippedRegionRect.location()));
378
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.
382
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);
387     }
388 }
389
390 RenderRegion* RenderFlowThread::renderRegionForLine(LayoutUnit position, bool extendLastRegion) const
391 {
392     ASSERT(!m_regionsInvalidated);
393
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;
397     
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())
403             continue;
404
405         if (position <= 0)
406             return region;
407
408         LayoutRect regionRect = region->regionRect();
409
410         if ((useHorizontalWritingMode && position < regionRect.maxY()) || (!useHorizontalWritingMode && position < regionRect.maxX()))
411             return region;
412
413         if (extendLastRegion)
414             lastValidRegion = region;
415     }
416
417     return lastValidRegion;
418 }
419
420 LayoutUnit RenderFlowThread::regionLogicalTopForLine(LayoutUnit position) const
421 {
422     RenderRegion* region = renderRegionForLine(position);
423     if (!region)
424         return 0;
425     return isHorizontalWritingMode() ? region->regionRect().y() : region->regionRect().x();
426 }
427
428 LayoutUnit RenderFlowThread::regionLogicalWidthForLine(LayoutUnit position) const
429 {
430     RenderRegion* region = renderRegionForLine(position, true);
431     if (!region)
432         return contentLogicalWidth();
433     return isHorizontalWritingMode() ? region->regionRect().width() : region->regionRect().height();
434 }
435
436 LayoutUnit RenderFlowThread::regionLogicalHeightForLine(LayoutUnit position) const
437 {
438     RenderRegion* region = renderRegionForLine(position);
439     if (!region)
440         return 0;
441     return isHorizontalWritingMode() ? region->regionRect().height() : region->regionRect().width();
442 }
443
444 LayoutUnit RenderFlowThread::regionRemainingLogicalHeightForLine(LayoutUnit position, PageBoundaryRule pageBoundaryRule) const
445 {
446     RenderRegion* region = renderRegionForLine(position);
447     if (!region)
448         return 0;
449
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);
457     }
458     return remainingHeight;
459 }
460
461 RenderRegion* RenderFlowThread::mapFromFlowToRegion(TransformState& transformState) const
462 {
463     if (!hasValidRegionInfo())
464         return 0;
465
466     LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox();
467     flipForWritingMode(boxRect);
468
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.
472
473     LayoutPoint center = boxRect.center();
474     RenderRegion* renderRegion = renderRegionForLine(isHorizontalWritingMode() ? center.y() : center.x(), true);
475     if (!renderRegion)
476         return 0;
477
478     LayoutRect flippedRegionRect(renderRegion->regionRect());
479     flipForWritingMode(flippedRegionRect);
480
481     transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location());
482
483     return renderRegion;
484 }
485
486 void RenderFlowThread::removeRenderBoxRegionInfo(RenderBox* box)
487 {
488     if (!hasRegions())
489         return;
490
491     RenderRegion* startRegion;
492     RenderRegion* endRegion;
493     getRegionRangeForBox(box, startRegion, endRegion);
494
495     for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) {
496         RenderRegion* region = *iter;
497         if (!region->isValid())
498             continue;
499         region->removeRenderBoxRegionInfo(box);
500         if (region == endRegion)
501             break;
502     }
503
504 #ifndef NDEBUG
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())
509             continue;
510         ASSERT(!region->renderBoxRegionInfo(box));
511     }
512 #endif
513
514     m_regionRangeMap.remove(box);
515 }
516
517 bool RenderFlowThread::logicalWidthChangedInRegions(const RenderBlock* block, LayoutUnit offsetFromLogicalTopOfFirstPage)
518 {
519     if (!hasRegions() || block == this) // Not necessary, since if any region changes, we do a full pagination relayout anyway.
520         return false;
521
522     RenderRegion* startRegion;
523     RenderRegion* endRegion;
524     getRegionRangeForBox(block, startRegion, endRegion);
525
526     for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) {
527         RenderRegion* region = *iter;
528         
529         if (!region->isValid())
530             continue;
531
532         ASSERT(!region->needsLayout());
533
534         OwnPtr<RenderBoxRegionInfo> oldInfo = region->takeRenderBoxRegionInfo(block);
535         if (!oldInfo)
536             continue;
537
538         LayoutUnit oldLogicalWidth = oldInfo->logicalWidth();
539         RenderBoxRegionInfo* newInfo = block->renderBoxRegionInfo(region, offsetFromLogicalTopOfFirstPage);
540         if (!newInfo || newInfo->logicalWidth() != oldLogicalWidth)
541             return true;
542
543         if (region == endRegion)
544             break;
545     }
546
547     return false;
548 }
549
550 LayoutUnit RenderFlowThread::contentLogicalWidthOfFirstRegion() const
551 {
552     if (!hasValidRegionInfo())
553         return 0;
554     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
555         RenderRegion* region = *iter;
556         if (!region->isValid())
557             continue;
558         return isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight();
559     }
560     ASSERT_NOT_REACHED();
561     return 0;
562 }
563
564 LayoutUnit RenderFlowThread::contentLogicalHeightOfFirstRegion() const
565 {
566     if (!hasValidRegionInfo())
567         return 0;
568     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
569         RenderRegion* region = *iter;
570         if (!region->isValid())
571             continue;
572         return isHorizontalWritingMode() ? region->contentHeight() : region->contentWidth();
573     }
574     ASSERT_NOT_REACHED();
575     return 0;
576 }
577  
578 LayoutUnit RenderFlowThread::contentLogicalLeftOfFirstRegion() const
579 {
580     if (!hasValidRegionInfo())
581         return 0;
582     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
583         RenderRegion* region = *iter;
584         if (!region->isValid())
585             continue;
586         return isHorizontalWritingMode() ? region->regionRect().x() : region->regionRect().y();
587     }
588     ASSERT_NOT_REACHED();
589     return 0;
590 }
591
592 RenderRegion* RenderFlowThread::firstRegion() const
593 {
594     if (!hasValidRegionInfo())
595         return 0;
596     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
597         RenderRegion* region = *iter;
598         if (!region->isValid())
599             continue;
600         return region;
601     }
602     return 0;
603 }
604
605 RenderRegion* RenderFlowThread::lastRegion() const
606 {
607     if (!hasValidRegionInfo())
608         return 0;
609     for (RenderRegionList::const_reverse_iterator iter = m_regionList.rbegin(); iter != m_regionList.rend(); ++iter) {
610         RenderRegion* region = *iter;
611         if (!region->isValid())
612             continue;
613         return region;
614     }
615     return 0;
616 }
617
618 void RenderFlowThread::clearRenderBoxCustomStyle(const RenderBox* box,
619     const RenderRegion* oldStartRegion, const RenderRegion* oldEndRegion,
620     const RenderRegion* newStartRegion, const RenderRegion* newEndRegion)
621 {
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;
628
629         if (oldStartRegion == region)
630             insideOldRegionRange = true;
631         if (newStartRegion == region)
632             insideNewRegionRange = true;
633
634         if (!(insideOldRegionRange && insideNewRegionRange))
635             region->clearBoxStyleInRegion(box);
636
637         if (oldEndRegion == region)
638             insideOldRegionRange = false;
639         if (newEndRegion == region)
640             insideNewRegionRange = false;
641     }
642 }
643
644 void RenderFlowThread::setRegionRangeForBox(const RenderBox* box, LayoutUnit offsetFromLogicalTopOfFirstPage)
645 {
646     if (!hasRegions())
647         return;
648
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));
655         return;
656     }
657
658     // If nothing changed, just bail.
659     RenderRegionRange& range = it->second;
660     if (range.startRegion() == startRegion && range.endRegion() == endRegion)
661         return;
662
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);
668             continue;
669         }
670
671         region->removeRenderBoxRegionInfo(box);
672
673         if (region == range.endRegion())
674             break;
675     }
676
677     clearRenderBoxCustomStyle(box, range.startRegion(), range.endRegion(), startRegion, endRegion);
678     range.setRange(startRegion, endRegion);
679 }
680
681 void RenderFlowThread::getRegionRangeForBox(const RenderBox* box, RenderRegion*& startRegion, RenderRegion*& endRegion) const
682 {
683     startRegion = 0;
684     endRegion = 0;
685     RenderRegionRangeMap::const_iterator it = m_regionRangeMap.find(box);
686     if (it == m_regionRangeMap.end())
687         return;
688
689     const RenderRegionRange& range = it->second;
690     startRegion = range.startRegion();
691     endRegion = range.endRegion();
692     ASSERT(m_regionList.contains(startRegion) && m_regionList.contains(endRegion));
693 }
694
695 void RenderFlowThread::computeOverflowStateForRegions(LayoutUnit oldClientAfterEdge)
696 {
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();
704
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);
709             continue;
710         }
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;
715         if (flowMin <= 0)
716             state = RenderRegion::RegionEmpty;
717         if (flowMax > 0)
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);
727     }
728
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);
732 }
733
734 void RenderFlowThread::regionLayoutUpdateEventTimerFired(Timer<RenderFlowThread>*)
735 {
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);
747         }
748     }
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();
752         if (!document)
753             continue;
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();
759         }
760     }
761 }
762
763 bool RenderFlowThread::regionInRange(const RenderRegion* targetRegion, const RenderRegion* startRegion, const RenderRegion* endRegion) const
764 {
765     ASSERT(targetRegion);
766
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())
770             continue;
771         if (targetRegion == currRegion)
772             return true;
773         if (currRegion == endRegion)
774             break;
775     }
776
777     return false;
778 }
779
780 bool RenderFlowThread::objectInFlowRegion(const RenderObject* object, const RenderRegion* region) const
781 {
782     ASSERT(object);
783     ASSERT(region);
784
785     if (!object->inRenderFlowThread())
786         return false;
787     if (object->enclosingRenderFlowThread() != this)
788         return false;
789     if (!m_regionList.contains(const_cast<RenderRegion*>(region)))
790         return false;
791
792     RenderBox* enclosingBox = object->enclosingBox();
793     RenderRegion* enclosingBoxStartRegion = 0;
794     RenderRegion* enclosingBoxEndRegion = 0;
795     getRegionRangeForBox(enclosingBox, enclosingBoxStartRegion, enclosingBoxEndRegion);
796     if (!regionInRange(region, enclosingBoxStartRegion, enclosingBoxEndRegion))
797        return false;
798
799     if (object->isBox())
800         return true;
801
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)))
808         return true;
809
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())
816                 continue;
817             if (currRegion == region)
818                 break;
819             if (objectABBRect.intersects(currRegion->absoluteBoundingBoxRect(true)))
820                 return false;
821         }
822         return true;
823     }
824
825     return false;
826 }
827
828 } // namespace WebCore