Leaks seen in RenderRegion::setRenderBoxRegionInfo on Leaks bot
[WebKit-https.git] / Source / WebCore / rendering / RenderFlowThread.cpp
1 /*
2  * Copyright 2011 Adobe Systems Incorporated. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above
9  *    copyright notice, this list of conditions and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above
12  *    copyright notice, this list of conditions and the following
13  *    disclaimer in the documentation and/or other materials
14  *    provided with the distribution.
15  * 
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
26  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include "config.h"
31
32 #include "RenderFlowThread.h"
33
34 #include "HitTestRequest.h"
35 #include "HitTestResult.h"
36 #include "Node.h"
37 #include "PaintInfo.h"
38 #include "RenderBoxRegionInfo.h"
39 #include "RenderLayer.h"
40 #include "RenderRegion.h"
41 #include "RenderView.h"
42 #include "TransformState.h"
43
44 namespace WebCore {
45
46 RenderFlowThread::RenderFlowThread(Node* node, const AtomicString& flowThread)
47     : RenderBlock(node)
48     , m_flowThread(flowThread)
49     , m_hasValidRegions(false)
50     , m_regionsInvalidated(false)
51     , m_regionsHaveUniformLogicalWidth(true)
52     , m_regionsHaveUniformLogicalHeight(true)
53 {
54     setIsAnonymous(false);
55     setInRenderFlowThread();
56 }
57
58 RenderFlowThread::~RenderFlowThread()
59 {
60     deleteAllValues(m_regionRangeMap);
61     m_regionRangeMap.clear();
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 RenderObject* RenderFlowThread::nextRendererForNode(Node* node) const
89 {
90     FlowThreadChildList::const_iterator it = m_flowThreadChildList.begin();
91     FlowThreadChildList::const_iterator end = m_flowThreadChildList.end();
92     
93     for (; it != end; ++it) {
94         RenderObject* child = *it;
95         ASSERT(child->node());
96         unsigned short position = node->compareDocumentPosition(child->node());
97         if (position & Node::DOCUMENT_POSITION_FOLLOWING)
98             return child;
99     }
100     
101     return 0;
102 }
103
104 RenderObject* RenderFlowThread::previousRendererForNode(Node* node) const
105 {
106     if (m_flowThreadChildList.isEmpty())
107         return 0;
108     
109     FlowThreadChildList::const_iterator begin = m_flowThreadChildList.begin();
110     FlowThreadChildList::const_iterator end = m_flowThreadChildList.end();
111     FlowThreadChildList::const_iterator it = end;
112     
113     do {
114         --it;
115         RenderObject* child = *it;
116         ASSERT(child->node());
117         unsigned short position = node->compareDocumentPosition(child->node());
118         if (position & Node::DOCUMENT_POSITION_PRECEDING)
119             return child;
120     } while (it != begin);
121     
122     return 0;
123 }
124
125 void RenderFlowThread::addFlowChild(RenderObject* newChild, RenderObject* beforeChild)
126 {
127     // The child list is used to sort the flow thread's children render objects 
128     // based on their corresponding nodes DOM order. The list is needed to avoid searching the whole DOM.
129
130     // Do not add anonymous objects.
131     if (!newChild->node())
132         return;
133
134     if (beforeChild)
135         m_flowThreadChildList.insertBefore(beforeChild, newChild);
136     else
137         m_flowThreadChildList.add(newChild);
138 }
139
140 void RenderFlowThread::removeFlowChild(RenderObject* child)
141 {
142     m_flowThreadChildList.remove(child);
143 }
144
145 // Compare two regions to determine in which one the content should flow first.
146 // The function returns true if the first passed region is "less" than the second passed region.
147 // If the first region appears before second region in DOM,
148 // the first region is "less" than the second region.
149 // If the first region is "less" than the second region, the first region receives content before second region.
150 static bool compareRenderRegions(const RenderRegion* firstRegion, const RenderRegion* secondRegion)
151 {
152     ASSERT(firstRegion);
153     ASSERT(secondRegion);
154
155     // If the regions have the same region-index, compare their position in dom.
156     ASSERT(firstRegion->node());
157     ASSERT(secondRegion->node());
158
159     unsigned short position = firstRegion->node()->compareDocumentPosition(secondRegion->node());
160     return (position & Node::DOCUMENT_POSITION_FOLLOWING);
161 }
162
163 bool RenderFlowThread::dependsOn(RenderFlowThread* otherRenderFlowThread) const
164 {
165     if (m_layoutBeforeThreadsSet.contains(otherRenderFlowThread))
166         return true;
167
168     // Recursively traverse the m_layoutBeforeThreadsSet.
169     RenderFlowThreadCountedSet::const_iterator iterator = m_layoutBeforeThreadsSet.begin();
170     RenderFlowThreadCountedSet::const_iterator end = m_layoutBeforeThreadsSet.end();
171     for (; iterator != end; ++iterator) {
172         const RenderFlowThread* beforeFlowThread = (*iterator).first;
173         if (beforeFlowThread->dependsOn(otherRenderFlowThread))
174             return true;
175     }
176
177     return false;
178 }
179
180 void RenderFlowThread::addRegionToThread(RenderRegion* renderRegion)
181 {
182     ASSERT(renderRegion);
183     if (m_regionList.isEmpty())
184         m_regionList.add(renderRegion);
185     else {
186         // Find the first region "greater" than renderRegion.
187         RenderRegionList::iterator it = m_regionList.begin();
188         while (it != m_regionList.end() && !compareRenderRegions(renderRegion, *it))
189             ++it;
190         m_regionList.insertBefore(it, renderRegion);
191     }
192
193     ASSERT(!renderRegion->isValid());
194     if (renderRegion->parentFlowThread()) {
195         if (renderRegion->parentFlowThread()->dependsOn(this)) {
196             // Register ourself to get a notification when the state changes.
197             renderRegion->parentFlowThread()->m_observerThreadsSet.add(this);
198             return;
199         }
200
201         addDependencyOnFlowThread(renderRegion->parentFlowThread());
202     }
203
204     renderRegion->setIsValid(true);
205
206     invalidateRegions();
207 }
208
209 void RenderFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
210 {
211     ASSERT(renderRegion);
212     m_regionList.remove(renderRegion);
213     if (renderRegion->parentFlowThread()) {
214         if (!renderRegion->isValid()) {
215             renderRegion->parentFlowThread()->m_observerThreadsSet.remove(this);
216             // No need to invalidate the regions rectangles. The removed region
217             // was not taken into account. Just return here.
218             return;
219         }
220         removeDependencyOnFlowThread(renderRegion->parentFlowThread());
221     }
222
223     invalidateRegions();
224 }
225
226 void RenderFlowThread::checkInvalidRegions()
227 {
228     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
229         RenderRegion* region = *iter;
230         // The only reason a region would be invalid is because it has a parent flow thread.
231         ASSERT(region->isValid() || region->parentFlowThread());
232         if (region->isValid() || region->parentFlowThread()->dependsOn(this))
233             continue;
234
235         region->parentFlowThread()->m_observerThreadsSet.remove(this);
236         addDependencyOnFlowThread(region->parentFlowThread());
237         region->setIsValid(true);
238         invalidateRegions();
239     }
240
241     if (m_observerThreadsSet.isEmpty())
242         return;
243
244     // Notify all the flow threads that were dependent on this flow.
245
246     // Create a copy of the list first. That's because observers might change the list when calling checkInvalidRegions.
247     Vector<RenderFlowThread*> observers;
248     copyToVector(m_observerThreadsSet, observers);
249
250     for (size_t i = 0; i < observers.size(); ++i) {
251         RenderFlowThread* flowThread = observers.at(i);
252         flowThread->checkInvalidRegions();
253     }
254 }
255
256 void RenderFlowThread::addDependencyOnFlowThread(RenderFlowThread* otherFlowThread)
257 {
258     std::pair<RenderFlowThreadCountedSet::iterator, bool> result = m_layoutBeforeThreadsSet.add(otherFlowThread);
259     if (result.second) {
260         // This is the first time we see this dependency. Make sure we recalculate all the dependencies.
261         view()->setIsRenderFlowThreadOrderDirty(true);
262     }
263 }
264
265 void RenderFlowThread::removeDependencyOnFlowThread(RenderFlowThread* otherFlowThread)
266 {
267     bool removed = m_layoutBeforeThreadsSet.remove(otherFlowThread);
268     if (removed) {
269         checkInvalidRegions();
270         view()->setIsRenderFlowThreadOrderDirty(true);
271     }
272 }
273
274 void RenderFlowThread::pushDependencies(RenderFlowThreadList& list)
275 {
276     for (RenderFlowThreadCountedSet::iterator iter = m_layoutBeforeThreadsSet.begin(); iter != m_layoutBeforeThreadsSet.end(); ++iter) {
277         RenderFlowThread* flowThread = (*iter).first;
278         if (list.contains(flowThread))
279             continue;
280         flowThread->pushDependencies(list);
281         list.add(flowThread);
282     }
283 }
284
285 class CurrentRenderFlowThreadMaintainer {
286     WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadMaintainer);
287 public:
288     CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread)
289         : m_renderFlowThread(renderFlowThread)
290     {
291         RenderView* view = m_renderFlowThread->view();
292         ASSERT(!view->currentRenderFlowThread());
293         view->setCurrentRenderFlowThread(m_renderFlowThread);
294     }
295     ~CurrentRenderFlowThreadMaintainer()
296     {
297         RenderView* view = m_renderFlowThread->view();
298         ASSERT(view->currentRenderFlowThread() == m_renderFlowThread);
299         view->setCurrentRenderFlowThread(0);
300     }
301 private:
302     RenderFlowThread* m_renderFlowThread;
303 };
304
305 void RenderFlowThread::layout()
306 {
307     bool regionsChanged = m_regionsInvalidated && m_everHadLayout;
308     if (m_regionsInvalidated) {
309         m_regionsInvalidated = false;
310         m_hasValidRegions = false;
311         m_regionsHaveUniformLogicalWidth = true;
312         m_regionsHaveUniformLogicalHeight = true;
313         deleteAllValues(m_regionRangeMap);
314         m_regionRangeMap.clear();
315         LayoutUnit previousRegionLogicalWidth = 0;
316         LayoutUnit previousRegionLogicalHeight = 0;
317         if (hasRegions()) {
318             for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
319                 RenderRegion* region = *iter;
320                 if (!region->isValid())
321                     continue;
322                 ASSERT(!region->needsLayout());
323                 
324                 region->deleteAllRenderBoxRegionInfo();
325
326                 LayoutUnit regionLogicalWidth;
327                 LayoutUnit regionLogicalHeight;
328
329                 if (isHorizontalWritingMode()) {
330                     regionLogicalWidth = region->contentWidth();
331                     regionLogicalHeight = region->contentHeight();
332                 } else {
333                     regionLogicalWidth = region->contentHeight();
334                     regionLogicalHeight = region->contentWidth();
335                 }
336
337                 if (!m_hasValidRegions)
338                     m_hasValidRegions = true;
339                 else {
340                     if (m_regionsHaveUniformLogicalWidth && previousRegionLogicalWidth != regionLogicalWidth)
341                         m_regionsHaveUniformLogicalWidth = false;
342                     if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
343                         m_regionsHaveUniformLogicalHeight = false;
344                 }
345
346                 previousRegionLogicalWidth = regionLogicalWidth;
347             }
348             
349             computeLogicalWidth(); // Called to get the maximum logical width for the region.
350             
351             LayoutUnit logicalHeight = 0;
352             for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
353                 RenderRegion* region = *iter;
354                 if (!region->isValid())
355                     continue;
356                 LayoutRect regionRect;
357                 if (isHorizontalWritingMode()) {
358                     regionRect = LayoutRect(style()->direction() == LTR ? 0 : logicalWidth() - region->contentWidth(), logicalHeight, region->contentWidth(), region->contentHeight());
359                     logicalHeight += regionRect.height();
360                 } else {
361                     regionRect = LayoutRect(logicalHeight, style()->direction() == LTR ? 0 : logicalWidth() - region->contentHeight(), region->contentWidth(), region->contentHeight());
362                     logicalHeight += regionRect.width();
363                 }
364                 region->setRegionRect(regionRect);
365             }
366         }
367     }
368
369     CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
370     LayoutStateMaintainer statePusher(view(), this, regionsChanged);
371     RenderBlock::layout();
372     statePusher.pop();
373 }
374
375 void RenderFlowThread::computeLogicalWidth()
376 {
377     LayoutUnit logicalWidth = 0;
378     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
379         RenderRegion* region = *iter;
380         if (!region->isValid())
381             continue;
382         ASSERT(!region->needsLayout());
383         logicalWidth = max(isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight(), logicalWidth);
384     }
385     setLogicalWidth(logicalWidth);
386
387     // If the regions have non-uniform logical widths, then insert inset information for the RenderFlowThread.
388     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
389         RenderRegion* region = *iter;
390         if (!region->isValid())
391             continue;
392         
393         LayoutUnit regionLogicalWidth = isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight();
394         if (regionLogicalWidth != logicalWidth) {
395             LayoutUnit logicalLeft = style()->direction() == LTR ? 0 : logicalWidth - regionLogicalWidth;
396             region->setRenderBoxRegionInfo(this, logicalLeft, regionLogicalWidth, false);
397         }
398     }
399 }
400
401 void RenderFlowThread::computeLogicalHeight()
402 {
403     int logicalHeight = 0;
404
405     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
406         RenderRegion* region = *iter;
407         if (!region->isValid())
408             continue;
409         ASSERT(!region->needsLayout());
410         logicalHeight += isHorizontalWritingMode() ? region->contentHeight() : region->contentWidth();
411     }
412
413     setLogicalHeight(logicalHeight);
414 }
415
416 void RenderFlowThread::paintIntoRegion(PaintInfo& paintInfo, RenderRegion* region, const LayoutPoint& paintOffset)
417 {
418     GraphicsContext* context = paintInfo.context;
419     if (!context)
420         return;
421
422     // Adjust the clipping rect for the region.
423     // paintOffset contains the offset where the painting should occur
424     // adjusted with the region padding and border.
425     LayoutRect regionRect(region->regionRect());
426     LayoutRect regionOverflowRect(region->regionOverflowRect());
427     LayoutRect regionClippingRect(paintOffset + (regionOverflowRect.location() - regionRect.location()), regionOverflowRect.size());
428
429     PaintInfo info(paintInfo);
430     info.rect.intersect(regionClippingRect);
431
432     if (!info.rect.isEmpty()) {
433         context->save();
434
435         context->clip(regionClippingRect);
436
437         // RenderFlowThread should start painting its content in a position that is offset
438         // from the region rect's current position. The amount of offset is equal to the location of
439         // region in flow coordinates.
440         LayoutPoint renderFlowThreadOffset;
441         if (style()->isFlippedBlocksWritingMode()) {
442             LayoutRect flippedRegionRect(regionRect);
443             flipForWritingMode(flippedRegionRect);
444             renderFlowThreadOffset = LayoutPoint(paintOffset - flippedRegionRect.location());
445         } else
446             renderFlowThreadOffset = LayoutPoint(paintOffset - regionRect.location());
447
448         context->translate(renderFlowThreadOffset.x(), renderFlowThreadOffset.y());
449         info.rect.moveBy(-renderFlowThreadOffset);
450         
451         layer()->paint(context, info.rect, 0, 0, region, RenderLayer::PaintLayerTemporaryClipRects);
452
453         context->restore();
454     }
455 }
456
457 bool RenderFlowThread::hitTestRegion(RenderRegion* region, const HitTestRequest& request, HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
458 {
459     LayoutRect regionRect(region->regionRect());
460     LayoutRect regionOverflowRect = region->regionOverflowRect();
461     LayoutRect regionClippingRect(accumulatedOffset + (regionOverflowRect.location() - regionRect.location()), regionOverflowRect.size());
462     if (!regionClippingRect.contains(pointInContainer))
463         return false;
464     
465     LayoutPoint renderFlowThreadOffset;
466     if (style()->isFlippedBlocksWritingMode()) {
467         LayoutRect flippedRegionRect(regionRect);
468         flipForWritingMode(flippedRegionRect);
469         renderFlowThreadOffset = LayoutPoint(accumulatedOffset - flippedRegionRect.location());
470     } else
471         renderFlowThreadOffset = LayoutPoint(accumulatedOffset - regionRect.location());
472
473     LayoutPoint transformedPoint(pointInContainer.x() - renderFlowThreadOffset.x(), pointInContainer.y() - renderFlowThreadOffset.y());
474     
475     // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView.
476     HitTestRequest newRequest(request.type() & HitTestRequest::IgnoreClipping);
477
478     RenderRegion* oldRegion = result.region();
479     result.setRegion(region);
480     LayoutPoint oldPoint = result.point();
481     result.setPoint(transformedPoint);
482     bool isPointInsideFlowThread = layer()->hitTest(newRequest, result);
483     result.setPoint(oldPoint);
484     result.setRegion(oldRegion);
485
486     // FIXME: Should we set result.m_localPoint back to the RenderRegion's coordinate space or leave it in the RenderFlowThread's coordinate
487     // 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
488     // patching positionForPoint.
489     return isPointInsideFlowThread;
490 }
491
492 bool RenderFlowThread::shouldRepaint(const LayoutRect& r) const
493 {
494     if (view()->printing() || r.isEmpty())
495         return false;
496
497     return true;
498 }
499
500 void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect, bool immediate)
501 {
502     if (!shouldRepaint(repaintRect))
503         return;
504
505     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
506         RenderRegion* region = *iter;
507         if (!region->isValid())
508             continue;
509
510         // We only have to issue a repaint in this region if the region rect intersects the repaint rect.
511         LayoutRect flippedRegionRect(region->regionRect());
512         LayoutRect flippedRegionOverflowRect(region->regionOverflowRect());
513         flipForWritingMode(flippedRegionRect); // Put the region rects into physical coordinates.
514         flipForWritingMode(flippedRegionOverflowRect);
515
516         LayoutRect clippedRect(flippedRegionOverflowRect);
517         clippedRect.intersect(repaintRect);
518         if (clippedRect.isEmpty())
519             continue;
520         
521         // Put the region rect into the region's physical coordinate space.
522         clippedRect.setLocation(region->contentBoxRect().location() + (repaintRect.location() - flippedRegionRect.location()));
523         
524         // Now switch to the region's writing mode coordinate space and let it repaint itself.
525         region->flipForWritingMode(clippedRect);
526         LayoutStateDisabler layoutStateDisabler(view()); // We can't use layout state to repaint, since the region is somewhere else.
527         region->repaintRectangle(clippedRect, immediate);
528     }
529 }
530
531 RenderRegion* RenderFlowThread::renderRegionForLine(LayoutUnit position, bool extendLastRegion) const
532 {
533     ASSERT(!m_regionsInvalidated);
534
535     // If no region matches the position and extendLastRegion is true, it will return
536     // the last valid region. It is similar to auto extending the size of the last region. 
537     RenderRegion* lastValidRegion = 0;
538     
539     // FIXME: The regions are always in order, optimize this search.
540     bool useHorizontalWritingMode = isHorizontalWritingMode();
541     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
542         RenderRegion* region = *iter;
543         if (!region->isValid())
544             continue;
545
546         if (position <= 0)
547             return region;
548
549         LayoutRect regionRect = region->regionRect();
550
551         if ((useHorizontalWritingMode && position < regionRect.maxY()) || (!useHorizontalWritingMode && position < regionRect.maxX()))
552             return region;
553
554         if (extendLastRegion)
555             lastValidRegion = region;
556     }
557
558     return lastValidRegion;
559 }
560
561 LayoutUnit RenderFlowThread::regionLogicalWidthForLine(LayoutUnit position) const
562 {
563     RenderRegion* region = renderRegionForLine(position, true);
564     if (!region)
565         return contentLogicalWidth();
566     return isHorizontalWritingMode() ? region->regionRect().width() : region->regionRect().height();
567 }
568
569 LayoutUnit RenderFlowThread::regionLogicalHeightForLine(LayoutUnit position) const
570 {
571     RenderRegion* region = renderRegionForLine(position);
572     if (!region)
573         return 0;
574     return isHorizontalWritingMode() ? region->regionRect().height() : region->regionRect().width();
575 }
576
577 LayoutUnit RenderFlowThread::regionRemainingLogicalHeightForLine(LayoutUnit position, PageBoundaryRule pageBoundaryRule) const
578 {
579     RenderRegion* region = renderRegionForLine(position);
580     if (!region)
581         return 0;
582
583     LayoutUnit regionLogicalBottom = isHorizontalWritingMode() ? region->regionRect().maxY() : region->regionRect().maxX();
584     LayoutUnit remainingHeight = regionLogicalBottom - position;
585     if (pageBoundaryRule == IncludePageBoundary) {
586         // If IncludePageBoundary is set, the line exactly on the top edge of a
587         // region will act as being part of the previous region.
588         LayoutUnit regionHeight = isHorizontalWritingMode() ? region->regionRect().height() : region->regionRect().width();
589         remainingHeight = layoutMod(remainingHeight, regionHeight);
590     }
591     return remainingHeight;
592 }
593
594 RenderRegion* RenderFlowThread::mapFromFlowToRegion(TransformState& transformState) const
595 {
596     if (!hasValidRegions())
597         return 0;
598
599     LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox();
600     flipForWritingMode(boxRect);
601
602     // FIXME: We need to refactor RenderObject::absoluteQuads to be able to split the quads across regions,
603     // for now we just take the center of the mapped enclosing box and map it to a region.
604     // Note: Using the center in order to avoid rounding errors.
605
606     LayoutPoint center = boxRect.center();
607     RenderRegion* renderRegion = renderRegionForLine(isHorizontalWritingMode() ? center.y() : center.x(), true);
608     if (!renderRegion)
609         return 0;
610
611     LayoutRect flippedRegionRect(renderRegion->regionRect());
612     flipForWritingMode(flippedRegionRect);
613
614     transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location());
615
616     return renderRegion;
617 }
618
619 void RenderFlowThread::removeRenderBoxRegionInfo(RenderBox* box)
620 {
621     if (!hasRegions())
622         return;
623
624     RenderRegion* startRegion;
625     RenderRegion* endRegion;
626     getRegionRangeForBox(box, startRegion, endRegion);
627     
628     for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) {
629         RenderRegion* region = *iter;
630         if (!region->isValid())
631             continue;
632         region->removeRenderBoxRegionInfo(box);
633         if (region == endRegion)
634             break;
635     }
636     
637     delete m_regionRangeMap.take(box);
638 }
639
640 bool RenderFlowThread::logicalWidthChangedInRegions(const RenderBlock* block, LayoutUnit offsetFromLogicalTopOfFirstPage)
641 {
642     if (!hasRegions() || block == this) // Not necessary, since if any region changes, we do a full pagination relayout anyway.
643         return false;
644
645     RenderRegion* startRegion;
646     RenderRegion* endRegion;
647     getRegionRangeForBox(block, startRegion, endRegion);
648
649     for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) {
650         RenderRegion* region = *iter;
651         
652         if (!region->isValid())
653             continue;
654
655         ASSERT(!region->needsLayout());
656
657         RenderBoxRegionInfo* oldInfo = region->takeRenderBoxRegionInfo(block);
658         if (!oldInfo)
659             continue;
660
661         LayoutUnit oldLogicalWidth = oldInfo->logicalWidth();
662         delete oldInfo;
663
664         RenderBoxRegionInfo* newInfo = block->renderBoxRegionInfo(region, offsetFromLogicalTopOfFirstPage);
665         if (!newInfo || newInfo->logicalWidth() != oldLogicalWidth)
666             return true;
667
668         if (region == endRegion)
669             break;
670     }
671
672     return false;
673 }
674
675 LayoutUnit RenderFlowThread::contentLogicalWidthOfFirstRegion() const
676 {
677     if (!hasValidRegions())
678         return 0;
679     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
680         RenderRegion* region = *iter;
681         if (!region->isValid())
682             continue;
683         return isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight();
684     }
685     ASSERT_NOT_REACHED();
686     return 0;
687 }
688
689 LayoutUnit RenderFlowThread::contentLogicalHeightOfFirstRegion() const
690 {
691     if (!hasValidRegions())
692         return 0;
693     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
694         RenderRegion* region = *iter;
695         if (!region->isValid())
696             continue;
697         return isHorizontalWritingMode() ? region->contentHeight() : region->contentWidth();
698     }
699     ASSERT_NOT_REACHED();
700     return 0;
701 }
702  
703 LayoutUnit RenderFlowThread::contentLogicalLeftOfFirstRegion() const
704 {
705     if (!hasValidRegions())
706         return 0;
707     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
708         RenderRegion* region = *iter;
709         if (!region->isValid())
710             continue;
711         return isHorizontalWritingMode() ? region->regionRect().x() : region->regionRect().y();
712     }
713     ASSERT_NOT_REACHED();
714     return 0;
715 }
716
717 RenderRegion* RenderFlowThread::firstRegion() const
718 {
719     if (!hasValidRegions())
720         return 0;
721     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
722         RenderRegion* region = *iter;
723         if (!region->isValid())
724             continue;
725         return region;
726     }
727     return 0;
728 }
729
730 RenderRegion* RenderFlowThread::lastRegion() const
731 {
732     if (!hasValidRegions())
733         return 0;
734     for (RenderRegionList::const_reverse_iterator iter = m_regionList.rbegin(); iter != m_regionList.rend(); ++iter) {
735         RenderRegion* region = *iter;
736         if (!region->isValid())
737             continue;
738         return region;
739     }
740     return 0;
741 }
742
743 void RenderFlowThread::setRegionRangeForBox(const RenderBox* box, LayoutUnit offsetFromLogicalTopOfFirstPage)
744 {
745     // FIXME: Not right for differing writing-modes.
746     RenderRegion* startRegion = renderRegionForLine(offsetFromLogicalTopOfFirstPage, true);
747     RenderRegion* endRegion = renderRegionForLine(offsetFromLogicalTopOfFirstPage + box->logicalHeight(), true);
748     RenderRegionRange* range = m_regionRangeMap.get(box);
749     if (range) {
750         // If nothing changed, just bail.
751         if (range->startRegion() == startRegion && range->endRegion() == endRegion)
752             return;
753
754         // Delete any info that we find before our new startRegion and after our new endRegion.
755         for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
756             RenderRegion* region = *iter;
757             if (region == startRegion) {
758                 iter = m_regionList.find(endRegion);
759                 continue;
760             }
761             
762             region->removeRenderBoxRegionInfo(box);
763
764             if (region == range->endRegion())
765                 break;
766         }
767         
768         range->setRange(startRegion, endRegion);
769         return;
770     }
771     range = new RenderRegionRange(startRegion, endRegion);
772     m_regionRangeMap.set(box, range);
773 }
774
775 void RenderFlowThread::getRegionRangeForBox(const RenderBox* box, RenderRegion*& startRegion, RenderRegion*& endRegion) const
776 {
777     startRegion = 0;
778     endRegion = 0;
779     RenderRegionRange* range = m_regionRangeMap.get(box);
780     if (!range)
781         return;
782     startRegion = range->startRegion();
783     endRegion = range->endRegion();
784 }
785     
786 } // namespace WebCore