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