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