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