122b9e03b2e0098de8f6b60342a4bd250cc0e5f3
[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 "HitTestRequest.h"
35 #include "HitTestResult.h"
36 #include "Node.h"
37 #include "PaintInfo.h"
38 #include "RenderBoxRegionInfo.h"
39 #include "RenderLayer.h"
40 #include "RenderRegion.h"
41 #include "RenderView.h"
42 #include "TransformState.h"
43 #include "WebKitNamedFlow.h"
44
45 namespace WebCore {
46
47 RenderFlowThread::RenderFlowThread(Node* node)
48     : RenderBlock(node)
49     , m_hasValidRegions(false)
50     , m_regionsInvalidated(false)
51     , m_regionsHaveUniformLogicalWidth(true)
52     , m_regionsHaveUniformLogicalHeight(true)
53     , m_overflow(false)
54     , m_regionLayoutUpdateEventTimer(this, &RenderFlowThread::regionLayoutUpdateEventTimerFired)
55 {
56     ASSERT(node->document()->cssRegionsEnabled());
57     setIsAnonymous(false);
58     setInRenderFlowThread();
59 }
60
61 PassRefPtr<RenderStyle> RenderFlowThread::createFlowThreadStyle(RenderStyle* parentStyle)
62 {
63     RefPtr<RenderStyle> newStyle(RenderStyle::create());
64     newStyle->inheritFrom(parentStyle);
65     newStyle->setDisplay(BLOCK);
66     newStyle->setPosition(AbsolutePosition);
67     newStyle->setZIndex(0);
68     newStyle->setLeft(Length(0, Fixed));
69     newStyle->setTop(Length(0, Fixed));
70     newStyle->setWidth(Length(100, Percent));
71     newStyle->setHeight(Length(100, Percent));
72     newStyle->font().update(0);
73     
74     return newStyle.release();
75 }
76
77 void RenderFlowThread::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
78 {
79     RenderBlock::styleDidChange(diff, oldStyle);
80
81     if (oldStyle && oldStyle->writingMode() != style()->writingMode())
82         m_regionsInvalidated = true;
83 }
84
85 void RenderFlowThread::removeFlowChildInfo(RenderObject* child)
86 {
87     if (child->isBox()) {
88         removeRenderBoxRegionInfo(toRenderBox(child));
89         if (child->canHaveRegionStyle())
90             clearRenderBoxCustomStyle(toRenderBox(child));
91     }
92 }
93
94 void RenderFlowThread::addRegionToThread(RenderRegion* renderRegion)
95 {
96     ASSERT(renderRegion);
97     m_regionList.add(renderRegion);
98     renderRegion->setIsValid(true);
99     invalidateRegions();
100 }
101
102 void RenderFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
103 {
104     ASSERT(renderRegion);
105     m_regionRangeMap.clear();
106     m_regionList.remove(renderRegion);
107     invalidateRegions();
108 }
109
110 class CurrentRenderFlowThreadMaintainer {
111     WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadMaintainer);
112 public:
113     CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread)
114         : m_renderFlowThread(renderFlowThread)
115     {
116         RenderView* view = m_renderFlowThread->view();
117         ASSERT(!view->currentRenderFlowThread());
118         view->setCurrentRenderFlowThread(m_renderFlowThread);
119     }
120     ~CurrentRenderFlowThreadMaintainer()
121     {
122         RenderView* view = m_renderFlowThread->view();
123         ASSERT(view->currentRenderFlowThread() == m_renderFlowThread);
124         view->setCurrentRenderFlowThread(0);
125     }
126 private:
127     RenderFlowThread* m_renderFlowThread;
128 };
129
130 class CurrentRenderFlowThreadDisabler {
131     WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadDisabler);
132 public:
133     CurrentRenderFlowThreadDisabler(RenderView* view)
134         : m_view(view)
135         , m_renderFlowThread(0)
136     {
137         m_renderFlowThread = m_view->currentRenderFlowThread();
138         if (m_renderFlowThread)
139             view->setCurrentRenderFlowThread(0);
140     }
141     ~CurrentRenderFlowThreadDisabler()
142     {
143         if (m_renderFlowThread)
144             m_view->setCurrentRenderFlowThread(m_renderFlowThread);
145     }
146 private:
147     RenderView* m_view;
148     RenderFlowThread* m_renderFlowThread;
149 };
150
151 void RenderFlowThread::layout()
152 {
153     bool regionsChanged = m_regionsInvalidated && everHadLayout();
154     if (m_regionsInvalidated) {
155         m_regionsInvalidated = false;
156         m_hasValidRegions = false;
157         m_regionsHaveUniformLogicalWidth = true;
158         m_regionsHaveUniformLogicalHeight = true;
159         m_regionRangeMap.clear();
160         LayoutUnit previousRegionLogicalWidth = 0;
161         LayoutUnit previousRegionLogicalHeight = 0;
162         if (hasRegions()) {
163             for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
164                 RenderRegion* region = *iter;
165                 if (!region->isValid())
166                     continue;
167                 ASSERT(!region->needsLayout());
168                 
169                 region->deleteAllRenderBoxRegionInfo();
170
171                 LayoutUnit regionLogicalWidth;
172                 LayoutUnit regionLogicalHeight;
173
174                 if (isHorizontalWritingMode()) {
175                     regionLogicalWidth = region->contentWidth();
176                     regionLogicalHeight = region->contentHeight();
177                 } else {
178                     regionLogicalWidth = region->contentHeight();
179                     regionLogicalHeight = region->contentWidth();
180                 }
181
182                 if (!m_hasValidRegions)
183                     m_hasValidRegions = true;
184                 else {
185                     if (m_regionsHaveUniformLogicalWidth && previousRegionLogicalWidth != regionLogicalWidth)
186                         m_regionsHaveUniformLogicalWidth = false;
187                     if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
188                         m_regionsHaveUniformLogicalHeight = false;
189                 }
190
191                 previousRegionLogicalWidth = regionLogicalWidth;
192             }
193             
194             computeLogicalWidth(); // Called to get the maximum logical width for the region.
195             
196             LayoutUnit logicalHeight = 0;
197             for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
198                 RenderRegion* region = *iter;
199                 if (!region->isValid())
200                     continue;
201                 LayoutRect regionRect;
202                 if (isHorizontalWritingMode()) {
203                     regionRect = LayoutRect(style()->direction() == LTR ? ZERO_LAYOUT_UNIT : logicalWidth() - region->contentWidth(), logicalHeight, region->contentWidth(), region->contentHeight());
204                     logicalHeight += regionRect.height();
205                 } else {
206                     regionRect = LayoutRect(logicalHeight, style()->direction() == LTR ? ZERO_LAYOUT_UNIT : logicalWidth() - region->contentHeight(), region->contentWidth(), region->contentHeight());
207                     logicalHeight += regionRect.width();
208                 }
209                 region->setRegionRect(regionRect);
210             }
211         }
212     }
213
214     CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
215     LayoutStateMaintainer statePusher(view(), this, regionsChanged);
216     RenderBlock::layout();
217     statePusher.pop();
218     if (document()->hasListenerType(Document::REGIONLAYOUTUPDATE_LISTENER) && !m_regionLayoutUpdateEventTimer.isActive())
219         for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
220             RenderRegion* region = *iter;
221             if (region->shouldDispatchRegionLayoutUpdateEvent()) {
222                 // at least one region needs to dispatch the event
223                 m_regionLayoutUpdateEventTimer.startOneShot(0);
224                 break;
225             }
226         }
227 }
228
229 void RenderFlowThread::computeLogicalWidth()
230 {
231     LayoutUnit logicalWidth = 0;
232     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
233         RenderRegion* region = *iter;
234         if (!region->isValid())
235             continue;
236         ASSERT(!region->needsLayout());
237         logicalWidth = max(isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight(), logicalWidth);
238     }
239     setLogicalWidth(logicalWidth);
240
241     // If the regions have non-uniform logical widths, then insert inset information for the RenderFlowThread.
242     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
243         RenderRegion* region = *iter;
244         if (!region->isValid())
245             continue;
246         
247         LayoutUnit regionLogicalWidth = isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight();
248         if (regionLogicalWidth != logicalWidth) {
249             LayoutUnit logicalLeft = style()->direction() == LTR ? ZERO_LAYOUT_UNIT : logicalWidth - regionLogicalWidth;
250             region->setRenderBoxRegionInfo(this, logicalLeft, regionLogicalWidth, false);
251         }
252     }
253 }
254
255 void RenderFlowThread::computeLogicalHeight()
256 {
257     LayoutUnit logicalHeight = 0;
258
259     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
260         RenderRegion* region = *iter;
261         if (!region->isValid())
262             continue;
263         ASSERT(!region->needsLayout());
264         logicalHeight += isHorizontalWritingMode() ? region->contentHeight() : region->contentWidth();
265     }
266
267     setLogicalHeight(logicalHeight);
268 }
269
270 void RenderFlowThread::paintIntoRegion(PaintInfo& paintInfo, RenderRegion* region, const LayoutPoint& paintOffset)
271 {
272     GraphicsContext* context = paintInfo.context;
273     if (!context)
274         return;
275
276     // Adjust the clipping rect for the region.
277     // paintOffset contains the offset where the painting should occur
278     // adjusted with the region padding and border.
279     LayoutRect regionRect(region->regionRect());
280     LayoutRect regionOverflowRect(region->regionOverflowRect());
281     LayoutRect regionClippingRect(paintOffset + (regionOverflowRect.location() - regionRect.location()), regionOverflowRect.size());
282
283     PaintInfo info(paintInfo);
284     info.rect.intersect(pixelSnappedIntRect(regionClippingRect));
285
286     if (!info.rect.isEmpty()) {
287         context->save();
288
289         context->clip(regionClippingRect);
290
291         // RenderFlowThread should start painting its content in a position that is offset
292         // from the region rect's current position. The amount of offset is equal to the location of
293         // region in flow coordinates.
294         LayoutPoint renderFlowThreadOffset;
295         if (style()->isFlippedBlocksWritingMode()) {
296             LayoutRect flippedRegionRect(regionRect);
297             flipForWritingMode(flippedRegionRect);
298             renderFlowThreadOffset = LayoutPoint(paintOffset - flippedRegionRect.location());
299         } else
300             renderFlowThreadOffset = LayoutPoint(paintOffset - regionRect.location());
301
302         context->translate(renderFlowThreadOffset.x(), renderFlowThreadOffset.y());
303         info.rect.moveBy(-roundedIntPoint(renderFlowThreadOffset));
304         
305         layer()->paint(context, info.rect, 0, 0, region, RenderLayer::PaintLayerTemporaryClipRects);
306
307         context->restore();
308     }
309 }
310
311 bool RenderFlowThread::hitTestRegion(RenderRegion* region, const HitTestRequest& request, HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
312 {
313     LayoutRect regionRect(region->regionRect());
314     LayoutRect regionOverflowRect = region->regionOverflowRect();
315     LayoutRect regionClippingRect(accumulatedOffset + (regionOverflowRect.location() - regionRect.location()), regionOverflowRect.size());
316     if (!regionClippingRect.contains(pointInContainer))
317         return false;
318     
319     LayoutPoint renderFlowThreadOffset;
320     if (style()->isFlippedBlocksWritingMode()) {
321         LayoutRect flippedRegionRect(regionRect);
322         flipForWritingMode(flippedRegionRect);
323         renderFlowThreadOffset = LayoutPoint(accumulatedOffset - flippedRegionRect.location());
324     } else
325         renderFlowThreadOffset = LayoutPoint(accumulatedOffset - regionRect.location());
326
327     LayoutPoint transformedPoint(pointInContainer.x() - renderFlowThreadOffset.x(), pointInContainer.y() - renderFlowThreadOffset.y());
328     
329     // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView.
330     HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping);
331
332     RenderRegion* oldRegion = result.region();
333     result.setRegion(region);
334     LayoutPoint oldPoint = result.point();
335     result.setPoint(transformedPoint);
336     bool isPointInsideFlowThread = layer()->hitTest(newRequest, result);
337     result.setPoint(oldPoint);
338     result.setRegion(oldRegion);
339
340     // FIXME: Should we set result.m_localPoint back to the RenderRegion's coordinate space or leave it in the RenderFlowThread's coordinate
341     // 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
342     // patching positionForPoint.
343     return isPointInsideFlowThread;
344 }
345
346 bool RenderFlowThread::shouldRepaint(const LayoutRect& r) const
347 {
348     if (view()->printing() || r.isEmpty())
349         return false;
350
351     return true;
352 }
353
354 void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect, bool immediate)
355 {
356     if (!shouldRepaint(repaintRect) || !hasValidRegionInfo())
357         return;
358
359     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
360         RenderRegion* region = *iter;
361         if (!region->isValid())
362             continue;
363
364         // We only have to issue a repaint in this region if the region rect intersects the repaint rect.
365         LayoutRect flippedRegionRect(region->regionRect());
366         LayoutRect flippedRegionOverflowRect(region->regionOverflowRect());
367         flipForWritingMode(flippedRegionRect); // Put the region rects into physical coordinates.
368         flipForWritingMode(flippedRegionOverflowRect);
369
370         LayoutRect clippedRect(repaintRect);
371         clippedRect.intersect(flippedRegionOverflowRect);
372         if (clippedRect.isEmpty())
373             continue;
374
375         // Put the region rect into the region's physical coordinate space.
376         clippedRect.setLocation(region->contentBoxRect().location() + (clippedRect.location() - flippedRegionRect.location()));
377
378         // Now switch to the region's writing mode coordinate space and let it repaint itself.
379         region->flipForWritingMode(clippedRect);
380         LayoutStateDisabler layoutStateDisabler(view()); // We can't use layout state to repaint, since the region is somewhere else.
381
382         // Can't use currentFlowThread as it possible to have imbricated flow threads and the wrong one could be used,
383         // so, we let each region figure out the proper enclosing flow thread
384         CurrentRenderFlowThreadDisabler disabler(view());
385         region->repaintRectangle(clippedRect, immediate);
386     }
387 }
388
389 RenderRegion* RenderFlowThread::renderRegionForLine(LayoutUnit position, bool extendLastRegion) const
390 {
391     ASSERT(!m_regionsInvalidated);
392
393     // If no region matches the position and extendLastRegion is true, it will return
394     // the last valid region. It is similar to auto extending the size of the last region. 
395     RenderRegion* lastValidRegion = 0;
396     
397     // FIXME: The regions are always in order, optimize this search.
398     bool useHorizontalWritingMode = isHorizontalWritingMode();
399     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
400         RenderRegion* region = *iter;
401         if (!region->isValid())
402             continue;
403
404         if (position <= 0)
405             return region;
406
407         LayoutRect regionRect = region->regionRect();
408
409         if ((useHorizontalWritingMode && position < regionRect.maxY()) || (!useHorizontalWritingMode && position < regionRect.maxX()))
410             return region;
411
412         if (extendLastRegion)
413             lastValidRegion = region;
414     }
415
416     return lastValidRegion;
417 }
418
419 LayoutUnit RenderFlowThread::regionLogicalTopForLine(LayoutUnit position) const
420 {
421     RenderRegion* region = renderRegionForLine(position);
422     if (!region)
423         return 0;
424     return isHorizontalWritingMode() ? region->regionRect().y() : region->regionRect().x();
425 }
426
427 LayoutUnit RenderFlowThread::regionLogicalWidthForLine(LayoutUnit position) const
428 {
429     RenderRegion* region = renderRegionForLine(position, true);
430     if (!region)
431         return contentLogicalWidth();
432     return isHorizontalWritingMode() ? region->regionRect().width() : region->regionRect().height();
433 }
434
435 LayoutUnit RenderFlowThread::regionLogicalHeightForLine(LayoutUnit position) const
436 {
437     RenderRegion* region = renderRegionForLine(position);
438     if (!region)
439         return 0;
440     return isHorizontalWritingMode() ? region->regionRect().height() : region->regionRect().width();
441 }
442
443 LayoutUnit RenderFlowThread::regionRemainingLogicalHeightForLine(LayoutUnit position, PageBoundaryRule pageBoundaryRule) const
444 {
445     RenderRegion* region = renderRegionForLine(position);
446     if (!region)
447         return 0;
448
449     LayoutUnit regionLogicalBottom = isHorizontalWritingMode() ? region->regionRect().maxY() : region->regionRect().maxX();
450     LayoutUnit remainingHeight = regionLogicalBottom - position;
451     if (pageBoundaryRule == IncludePageBoundary) {
452         // If IncludePageBoundary is set, the line exactly on the top edge of a
453         // region will act as being part of the previous region.
454         LayoutUnit regionHeight = isHorizontalWritingMode() ? region->regionRect().height() : region->regionRect().width();
455         remainingHeight = layoutMod(remainingHeight, regionHeight);
456     }
457     return remainingHeight;
458 }
459
460 RenderRegion* RenderFlowThread::mapFromFlowToRegion(TransformState& transformState) const
461 {
462     if (!hasValidRegionInfo())
463         return 0;
464
465     LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox();
466     flipForWritingMode(boxRect);
467
468     // FIXME: We need to refactor RenderObject::absoluteQuads to be able to split the quads across regions,
469     // for now we just take the center of the mapped enclosing box and map it to a region.
470     // Note: Using the center in order to avoid rounding errors.
471
472     LayoutPoint center = boxRect.center();
473     RenderRegion* renderRegion = renderRegionForLine(isHorizontalWritingMode() ? center.y() : center.x(), true);
474     if (!renderRegion)
475         return 0;
476
477     LayoutRect flippedRegionRect(renderRegion->regionRect());
478     flipForWritingMode(flippedRegionRect);
479
480     transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location());
481
482     return renderRegion;
483 }
484
485 void RenderFlowThread::removeRenderBoxRegionInfo(RenderBox* box)
486 {
487     if (!hasRegions())
488         return;
489
490     RenderRegion* startRegion;
491     RenderRegion* endRegion;
492     getRegionRangeForBox(box, startRegion, endRegion);
493
494     for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) {
495         RenderRegion* region = *iter;
496         if (!region->isValid())
497             continue;
498         region->removeRenderBoxRegionInfo(box);
499         if (region == endRegion)
500             break;
501     }
502
503 #ifndef NDEBUG
504     // We have to make sure we did not leave any RenderBoxRegionInfo attached.
505     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
506         RenderRegion* region = *iter;
507         if (!region->isValid())
508             continue;
509         ASSERT(!region->renderBoxRegionInfo(box));
510     }
511 #endif
512
513     m_regionRangeMap.remove(box);
514 }
515
516 bool RenderFlowThread::logicalWidthChangedInRegions(const RenderBlock* block, LayoutUnit offsetFromLogicalTopOfFirstPage)
517 {
518     if (!hasRegions() || block == this) // Not necessary, since if any region changes, we do a full pagination relayout anyway.
519         return false;
520
521     RenderRegion* startRegion;
522     RenderRegion* endRegion;
523     getRegionRangeForBox(block, startRegion, endRegion);
524
525     for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) {
526         RenderRegion* region = *iter;
527         
528         if (!region->isValid())
529             continue;
530
531         ASSERT(!region->needsLayout());
532
533         OwnPtr<RenderBoxRegionInfo> oldInfo = region->takeRenderBoxRegionInfo(block);
534         if (!oldInfo)
535             continue;
536
537         LayoutUnit oldLogicalWidth = oldInfo->logicalWidth();
538         RenderBoxRegionInfo* newInfo = block->renderBoxRegionInfo(region, offsetFromLogicalTopOfFirstPage);
539         if (!newInfo || newInfo->logicalWidth() != oldLogicalWidth)
540             return true;
541
542         if (region == endRegion)
543             break;
544     }
545
546     return false;
547 }
548
549 LayoutUnit RenderFlowThread::contentLogicalWidthOfFirstRegion() const
550 {
551     if (!hasValidRegionInfo())
552         return 0;
553     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
554         RenderRegion* region = *iter;
555         if (!region->isValid())
556             continue;
557         return isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight();
558     }
559     ASSERT_NOT_REACHED();
560     return 0;
561 }
562
563 LayoutUnit RenderFlowThread::contentLogicalHeightOfFirstRegion() const
564 {
565     if (!hasValidRegionInfo())
566         return 0;
567     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
568         RenderRegion* region = *iter;
569         if (!region->isValid())
570             continue;
571         return isHorizontalWritingMode() ? region->contentHeight() : region->contentWidth();
572     }
573     ASSERT_NOT_REACHED();
574     return 0;
575 }
576  
577 LayoutUnit RenderFlowThread::contentLogicalLeftOfFirstRegion() const
578 {
579     if (!hasValidRegionInfo())
580         return 0;
581     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
582         RenderRegion* region = *iter;
583         if (!region->isValid())
584             continue;
585         return isHorizontalWritingMode() ? region->regionRect().x() : region->regionRect().y();
586     }
587     ASSERT_NOT_REACHED();
588     return 0;
589 }
590
591 RenderRegion* RenderFlowThread::firstRegion() const
592 {
593     if (!hasValidRegionInfo())
594         return 0;
595     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
596         RenderRegion* region = *iter;
597         if (!region->isValid())
598             continue;
599         return region;
600     }
601     return 0;
602 }
603
604 RenderRegion* RenderFlowThread::lastRegion() const
605 {
606     if (!hasValidRegionInfo())
607         return 0;
608     for (RenderRegionList::const_reverse_iterator iter = m_regionList.rbegin(); iter != m_regionList.rend(); ++iter) {
609         RenderRegion* region = *iter;
610         if (!region->isValid())
611             continue;
612         return region;
613     }
614     return 0;
615 }
616
617 void RenderFlowThread::clearRenderBoxCustomStyle(const RenderBox* box,
618     const RenderRegion* oldStartRegion, const RenderRegion* oldEndRegion,
619     const RenderRegion* newStartRegion, const RenderRegion* newEndRegion)
620 {
621     // Clear the styles for the object in the regions.
622     // The styles are not cleared for the regions that are contained in both ranges.
623     bool insideOldRegionRange = false;
624     bool insideNewRegionRange = false;
625     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
626         RenderRegion* region = *iter;
627
628         if (oldStartRegion == region)
629             insideOldRegionRange = true;
630         if (newStartRegion == region)
631             insideNewRegionRange = true;
632
633         if (!(insideOldRegionRange && insideNewRegionRange))
634             region->clearBoxStyleInRegion(box);
635
636         if (oldEndRegion == region)
637             insideOldRegionRange = false;
638         if (newEndRegion == region)
639             insideNewRegionRange = false;
640     }
641 }
642
643 void RenderFlowThread::setRegionRangeForBox(const RenderBox* box, LayoutUnit offsetFromLogicalTopOfFirstPage)
644 {
645     if (!hasRegions())
646         return;
647
648     // FIXME: Not right for differing writing-modes.
649     RenderRegion* startRegion = renderRegionForLine(offsetFromLogicalTopOfFirstPage, true);
650     RenderRegion* endRegion = renderRegionForLine(offsetFromLogicalTopOfFirstPage + box->logicalHeight(), true);
651     RenderRegionRangeMap::iterator it = m_regionRangeMap.find(box);
652     if (it == m_regionRangeMap.end()) {
653         m_regionRangeMap.set(box, RenderRegionRange(startRegion, endRegion));
654         return;
655     }
656
657     // If nothing changed, just bail.
658     RenderRegionRange& range = it->second;
659     if (range.startRegion() == startRegion && range.endRegion() == endRegion)
660         return;
661
662     // Delete any info that we find before our new startRegion and after our new endRegion.
663     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
664         RenderRegion* region = *iter;
665         if (region == startRegion) {
666             iter = m_regionList.find(endRegion);
667             continue;
668         }
669
670         region->removeRenderBoxRegionInfo(box);
671
672         if (region == range.endRegion())
673             break;
674     }
675
676     clearRenderBoxCustomStyle(box, range.startRegion(), range.endRegion(), startRegion, endRegion);
677     range.setRange(startRegion, endRegion);
678 }
679
680 void RenderFlowThread::getRegionRangeForBox(const RenderBox* box, RenderRegion*& startRegion, RenderRegion*& endRegion) const
681 {
682     startRegion = 0;
683     endRegion = 0;
684     RenderRegionRangeMap::const_iterator it = m_regionRangeMap.find(box);
685     if (it == m_regionRangeMap.end())
686         return;
687
688     const RenderRegionRange& range = it->second;
689     startRegion = range.startRegion();
690     endRegion = range.endRegion();
691     ASSERT(m_regionList.contains(startRegion) && m_regionList.contains(endRegion));
692 }
693
694 void RenderFlowThread::computeOverflowStateForRegions(LayoutUnit oldClientAfterEdge)
695 {
696     LayoutUnit height = oldClientAfterEdge;
697     // FIXME: the visual overflow of middle region (if it is the last one to contain any content in a render flow thread)
698     // might not be taken into account because the render flow thread height is greater that that regions height + its visual overflow
699     // because of how computeLogicalHeight is implemented for RenderFlowThread (as a sum of all regions height).
700     // This means that the middle region will be marked as fit (even if it has visual overflow flowing into the next region)
701     if (hasRenderOverflow())
702         height = isHorizontalWritingMode() ? visualOverflowRect().maxY() : visualOverflowRect().maxX();
703
704     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
705         RenderRegion* region = *iter;
706         if (!region->isValid()) {
707             region->setRegionState(RenderRegion::RegionUndefined);
708             continue;
709         }
710         LayoutUnit flowMin = height - (isHorizontalWritingMode() ? region->regionRect().y() : region->regionRect().x());
711         LayoutUnit flowMax = height - (isHorizontalWritingMode() ? region->regionRect().maxY() : region->regionRect().maxX());
712         RenderRegion::RegionState previousState = region->regionState();
713         RenderRegion::RegionState state = RenderRegion::RegionFit;
714         if (flowMin <= 0)
715             state = RenderRegion::RegionEmpty;
716         if (flowMax > 0)
717             state = RenderRegion::RegionOverflow;
718         region->setRegionState(state);
719         // determine whether this region should dispatch a regionLayoutUpdate event
720         // FIXME: currently it cannot determine whether a region whose regionOverflow state remained either "fit" or "overflow" has actually
721         // changed, so it just assumes that those region should dispatch the event
722         if (previousState != state
723             || state == RenderRegion::RegionFit
724             || state == RenderRegion::RegionOverflow)
725             region->setDispatchRegionLayoutUpdateEvent(true);
726     }
727
728     // With the regions overflow state computed we can also set the overflow for the named flow.
729     RenderRegion* lastReg = lastRegion();
730     m_overflow = lastReg && (lastReg->regionState() == RenderRegion::RegionOverflow);
731 }
732
733 void RenderFlowThread::regionLayoutUpdateEventTimerFired(Timer<RenderFlowThread>*)
734 {
735     // Create a copy of region nodes, to protect them for being destroyed in the event listener
736     Vector<RefPtr<Node> > regionNodes;
737     regionNodes.reserveCapacity(m_regionList.size());
738     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
739         RenderRegion* region = *iter;
740         ASSERT(region->node() && region->node()->isElementNode());
741         // dispatch the event only for marked regions and only for those who have a listener
742         if (region->shouldDispatchRegionLayoutUpdateEvent()) {
743             regionNodes.append(region->node());
744             // clear the dispatch flag here, as it is possible to be set again due to event listeners
745             region->setDispatchRegionLayoutUpdateEvent(false);
746         }
747     }
748     for (Vector<RefPtr<Node> >::const_iterator it = regionNodes.begin(); it != regionNodes.end(); ++it) {
749         RefPtr<Node> node = *it;
750         RefPtr<Document> document = node->document();
751         if (!document)
752             continue;
753         RenderObject* renderer = node->renderer();
754         if (renderer && renderer->isRenderRegion()) {
755             node->dispatchRegionLayoutUpdateEvent();
756             // Layout needs to be uptodate after each event listener
757             document->updateLayoutIgnorePendingStylesheets();
758         }
759     }
760 }
761
762 bool RenderFlowThread::regionInRange(const RenderRegion* targetRegion, const RenderRegion* startRegion, const RenderRegion* endRegion) const
763 {
764     ASSERT(targetRegion);
765
766     for (RenderRegionList::const_iterator it = m_regionList.find(const_cast<RenderRegion*>(startRegion)); it != m_regionList.end(); ++it) {
767         const RenderRegion* currRegion = *it;
768         if (!currRegion->isValid())
769             continue;
770         if (targetRegion == currRegion)
771             return true;
772         if (currRegion == endRegion)
773             break;
774     }
775
776     return false;
777 }
778
779 bool RenderFlowThread::objectInFlowRegion(const RenderObject* object, const RenderRegion* region) const
780 {
781     ASSERT(object);
782     ASSERT(region);
783
784     if (!object->inRenderFlowThread())
785         return false;
786     if (object->enclosingRenderFlowThread() != this)
787         return false;
788     if (!m_regionList.contains(const_cast<RenderRegion*>(region)))
789         return false;
790
791     RenderBox* enclosingBox = object->enclosingBox();
792     RenderRegion* enclosingBoxStartRegion = 0;
793     RenderRegion* enclosingBoxEndRegion = 0;
794     getRegionRangeForBox(enclosingBox, enclosingBoxStartRegion, enclosingBoxEndRegion);
795     if (!regionInRange(region, enclosingBoxStartRegion, enclosingBoxEndRegion))
796        return false;
797
798     if (object->isBox())
799         return true;
800
801     LayoutRect objectABBRect = object->absoluteBoundingBoxRect(true);
802     if (!objectABBRect.width())
803         objectABBRect.setWidth(1);
804     if (!objectABBRect.height())
805         objectABBRect.setHeight(1); 
806     if (objectABBRect.intersects(region->absoluteBoundingBoxRect(true)))
807         return true;
808
809     if (region == lastRegion()) {
810         // If the object does not intersect any of the enclosing box regions
811         // then the object is in last region.
812         for (RenderRegionList::const_iterator it = m_regionList.find(enclosingBoxStartRegion); it != m_regionList.end(); ++it) {
813             const RenderRegion* currRegion = *it;
814             if (!region->isValid())
815                 continue;
816             if (currRegion == region)
817                 break;
818             if (objectABBRect.intersects(currRegion->absoluteBoundingBoxRect(true)))
819                 return false;
820         }
821         return true;
822     }
823
824     return false;
825 }
826
827 } // namespace WebCore