blob: bff81ff3922460e12c36167cad11d093fa043b4c [file] [log] [blame]
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001/*
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 "core/rendering/RenderFlowThread.h"
33
34#include "core/dom/Node.h"
35#include "core/platform/PODIntervalTree.h"
36#include "core/platform/graphics/transforms/TransformState.h"
37#include "core/rendering/FlowThreadController.h"
38#include "core/rendering/HitTestRequest.h"
39#include "core/rendering/HitTestResult.h"
40#include "core/rendering/PaintInfo.h"
41#include "core/rendering/RenderBoxRegionInfo.h"
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +010042#include "core/rendering/RenderInline.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010043#include "core/rendering/RenderLayer.h"
44#include "core/rendering/RenderRegion.h"
45#include "core/rendering/RenderView.h"
46
47namespace WebCore {
48
49RenderFlowThread::RenderFlowThread()
50 : RenderBlock(0)
51 , m_autoLogicalHeightRegionsCount(0)
52 , m_regionsInvalidated(false)
53 , m_regionsHaveUniformLogicalWidth(true)
54 , m_regionsHaveUniformLogicalHeight(true)
55 , m_overset(true)
56 , m_hasRegionsWithStyling(false)
57 , m_dispatchRegionLayoutUpdateEvent(false)
58 , m_pageLogicalSizeChanged(false)
59 , m_inConstrainedLayoutPhase(false)
60 , m_needsTwoPhasesLayout(false)
61{
62 setFlowThreadState(InsideOutOfFlowThread);
63}
64
65PassRefPtr<RenderStyle> RenderFlowThread::createFlowThreadStyle(RenderStyle* parentStyle)
66{
67 RefPtr<RenderStyle> newStyle(RenderStyle::create());
68 newStyle->inheritFrom(parentStyle);
69 newStyle->setDisplay(BLOCK);
70 newStyle->setPosition(AbsolutePosition);
71 newStyle->setZIndex(0);
72 newStyle->setLeft(Length(0, Fixed));
73 newStyle->setTop(Length(0, Fixed));
74 newStyle->setWidth(Length(100, Percent));
75 newStyle->setHeight(Length(100, Percent));
76 newStyle->font().update(0);
77
78 return newStyle.release();
79}
80
81void RenderFlowThread::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
82{
83 RenderBlock::styleDidChange(diff, oldStyle);
84
85 if (oldStyle && oldStyle->writingMode() != style()->writingMode())
86 m_regionsInvalidated = true;
87}
88
89void RenderFlowThread::removeFlowChildInfo(RenderObject* child)
90{
91 if (child->isBox())
92 removeRenderBoxRegionInfo(toRenderBox(child));
93 clearRenderObjectCustomStyle(child);
94}
95
96void RenderFlowThread::addRegionToThread(RenderRegion* renderRegion)
97{
98 ASSERT(renderRegion);
99 m_regionList.add(renderRegion);
100 renderRegion->setIsValid(true);
101}
102
103void RenderFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
104{
105 ASSERT(renderRegion);
106 m_regionList.remove(renderRegion);
107}
108
109void RenderFlowThread::invalidateRegions()
110{
111 if (m_regionsInvalidated) {
112 ASSERT(selfNeedsLayout());
113 return;
114 }
115
116 m_regionRangeMap.clear();
117 m_breakBeforeToRegionMap.clear();
118 m_breakAfterToRegionMap.clear();
119 setNeedsLayout(true);
120
121 m_regionsInvalidated = true;
122}
123
124class CurrentRenderFlowThreadDisabler {
125 WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadDisabler);
126public:
127 CurrentRenderFlowThreadDisabler(RenderView* view)
128 : m_view(view)
129 , m_renderFlowThread(0)
130 {
131 m_renderFlowThread = m_view->flowThreadController()->currentRenderFlowThread();
132 if (m_renderFlowThread)
133 view->flowThreadController()->setCurrentRenderFlowThread(0);
134 }
135 ~CurrentRenderFlowThreadDisabler()
136 {
137 if (m_renderFlowThread)
138 m_view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
139 }
140private:
141 RenderView* m_view;
142 RenderFlowThread* m_renderFlowThread;
143};
144
145void RenderFlowThread::validateRegions()
146{
147 if (m_regionsInvalidated) {
148 m_regionsInvalidated = false;
149 m_regionsHaveUniformLogicalWidth = true;
150 m_regionsHaveUniformLogicalHeight = true;
151
152 if (hasRegions()) {
153 LayoutUnit previousRegionLogicalWidth = 0;
154 LayoutUnit previousRegionLogicalHeight = 0;
155 bool firstRegionVisited = false;
156
157 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
158 RenderRegion* region = *iter;
159 ASSERT(!region->needsLayout() || region->isRenderRegionSet());
160
161 region->deleteAllRenderBoxRegionInfo();
162
163 // In the normal layout phase we need to initialize the overrideLogicalContentHeight for auto-height regions.
164 // See initializeRegionsOverrideLogicalContentHeight for the explanation.
165 // Also, if we have auto-height regions we can't assume m_regionsHaveUniformLogicalHeight to be true in the first phase
166 // because the auto-height regions don't have their height computed yet.
167 if (!inConstrainedLayoutPhase() && region->hasAutoLogicalHeight()) {
168 region->setOverrideLogicalContentHeight(region->maxPageLogicalHeight());
169 m_regionsHaveUniformLogicalHeight = false;
170 }
171
172 LayoutUnit regionLogicalWidth = region->pageLogicalWidth();
173 LayoutUnit regionLogicalHeight = region->pageLogicalHeight();
174
175 if (!firstRegionVisited)
176 firstRegionVisited = true;
177 else {
178 if (m_regionsHaveUniformLogicalWidth && previousRegionLogicalWidth != regionLogicalWidth)
179 m_regionsHaveUniformLogicalWidth = false;
180 if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
181 m_regionsHaveUniformLogicalHeight = false;
182 }
183
184 previousRegionLogicalWidth = regionLogicalWidth;
185 }
186 }
187 }
188
189 updateLogicalWidth(); // Called to get the maximum logical width for the region.
190 updateRegionsFlowThreadPortionRect();
191}
192
193void RenderFlowThread::layout()
194{
195 StackStats::LayoutCheckPoint layoutCheckPoint;
196
197 m_pageLogicalSizeChanged = m_regionsInvalidated && everHadLayout();
198
199 // In case this is the second pass of the normal phase we need to update the auto-height regions to their initial value.
200 // If the region chain was invalidated this will happen anyway.
201 if (!m_regionsInvalidated && !inConstrainedLayoutPhase())
202 initializeRegionsOverrideLogicalContentHeight();
203
204 validateRegions();
205
206 // This is the first phase of the layout and because we have auto-height regions we'll need a second
207 // pass to update the flow with the computed auto-height regions.
208 m_needsTwoPhasesLayout = !inConstrainedLayoutPhase() && hasAutoLogicalHeightRegions();
209
210 CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
211 RenderBlock::layout();
212
213 m_pageLogicalSizeChanged = false;
214
215 if (lastRegion())
216 lastRegion()->expandToEncompassFlowThreadContentsIfNeeded();
217
218 if (shouldDispatchRegionLayoutUpdateEvent())
219 dispatchRegionLayoutUpdateEvent();
220}
221
222void RenderFlowThread::updateLogicalWidth()
223{
224 LayoutUnit logicalWidth = initialLogicalWidth();
225 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
226 RenderRegion* region = *iter;
227 ASSERT(!region->needsLayout() || region->isRenderRegionSet());
228 logicalWidth = max(region->pageLogicalWidth(), logicalWidth);
229 }
230 setLogicalWidth(logicalWidth);
231
232 // If the regions have non-uniform logical widths, then insert inset information for the RenderFlowThread.
233 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
234 RenderRegion* region = *iter;
235 LayoutUnit regionLogicalWidth = region->pageLogicalWidth();
236 if (regionLogicalWidth != logicalWidth) {
237 LayoutUnit logicalLeft = style()->direction() == LTR ? LayoutUnit() : logicalWidth - regionLogicalWidth;
238 region->setRenderBoxRegionInfo(this, logicalLeft, regionLogicalWidth, false);
239 }
240 }
241}
242
243void RenderFlowThread::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
244{
245 computedValues.m_position = logicalTop;
246 computedValues.m_extent = 0;
247
248 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
249 RenderRegion* region = *iter;
250 ASSERT(!region->needsLayout() || region->isRenderRegionSet());
251
252 computedValues.m_extent += region->logicalHeightOfAllFlowThreadContent();
253 }
254}
255
256LayoutRect RenderFlowThread::computeRegionClippingRect(const LayoutPoint& offset, const LayoutRect& flowThreadPortionRect, const LayoutRect& flowThreadPortionOverflowRect) const
257{
258 LayoutRect regionClippingRect(offset + (flowThreadPortionOverflowRect.location() - flowThreadPortionRect.location()), flowThreadPortionOverflowRect.size());
259 if (style()->isFlippedBlocksWritingMode())
260 regionClippingRect.move(flowThreadPortionRect.size() - flowThreadPortionOverflowRect.size());
261 return regionClippingRect;
262}
263
264void RenderFlowThread::paintFlowThreadPortionInRegion(PaintInfo& paintInfo, RenderRegion* region, LayoutRect flowThreadPortionRect, LayoutRect flowThreadPortionOverflowRect, const LayoutPoint& paintOffset) const
265{
266 GraphicsContext* context = paintInfo.context;
267 if (!context)
268 return;
269
270 // RenderFlowThread should start painting its content in a position that is offset
271 // from the region rect's current position. The amount of offset is equal to the location of
272 // the flow thread portion in the flow thread's local coordinates.
273 // Note that we have to pixel snap the location at which we're going to paint, since this is necessary
274 // to minimize the amount of incorrect snapping that would otherwise occur.
275 // If we tried to paint by applying a non-integral translation, then all the
276 // layout code that attempted to pixel snap would be incorrect.
277 IntPoint adjustedPaintOffset;
278 LayoutPoint portionLocation;
279 if (style()->isFlippedBlocksWritingMode()) {
280 LayoutRect flippedFlowThreadPortionRect(flowThreadPortionRect);
281 flipForWritingMode(flippedFlowThreadPortionRect);
282 portionLocation = flippedFlowThreadPortionRect.location();
283 } else
284 portionLocation = flowThreadPortionRect.location();
285 adjustedPaintOffset = roundedIntPoint(paintOffset - portionLocation);
286
287 // The clipping rect for the region is set up by assuming the flowThreadPortionRect is going to paint offset from adjustedPaintOffset.
288 // Remember that we pixel snapped and moved the paintOffset and stored the snapped result in adjustedPaintOffset. Now we add back in
289 // the flowThreadPortionRect's location to get the spot where we expect the portion to actually paint. This can be non-integral and
290 // that's ok. We then pixel snap the resulting clipping rect to account for snapping that will occur when the flow thread paints.
291 IntRect regionClippingRect = pixelSnappedIntRect(computeRegionClippingRect(adjustedPaintOffset + portionLocation, flowThreadPortionRect, flowThreadPortionOverflowRect));
292
293 PaintInfo info(paintInfo);
294 info.rect.intersect(regionClippingRect);
295
296 if (!info.rect.isEmpty()) {
297 context->save();
298
299 context->clip(regionClippingRect);
300
301 context->translate(adjustedPaintOffset.x(), adjustedPaintOffset.y());
302 info.rect.moveBy(-adjustedPaintOffset);
303
304 layer()->paint(context, info.rect, 0, 0, region, RenderLayer::PaintLayerTemporaryClipRects);
305
306 context->restore();
307 }
308}
309
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100310bool RenderFlowThread::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
311{
312 if (hitTestAction == HitTestBlockBackground)
313 return false;
314 return RenderBlock::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction);
315}
316
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100317bool RenderFlowThread::hitTestFlowThreadPortionInRegion(RenderRegion* region, LayoutRect flowThreadPortionRect, LayoutRect flowThreadPortionOverflowRect, const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const
318{
319 LayoutRect regionClippingRect = computeRegionClippingRect(accumulatedOffset, flowThreadPortionRect, flowThreadPortionOverflowRect);
320 if (!regionClippingRect.contains(locationInContainer.point()))
321 return false;
322
323 LayoutSize renderFlowThreadOffset;
324 if (style()->isFlippedBlocksWritingMode()) {
325 LayoutRect flippedFlowThreadPortionRect(flowThreadPortionRect);
326 flipForWritingMode(flippedFlowThreadPortionRect);
327 renderFlowThreadOffset = accumulatedOffset - flippedFlowThreadPortionRect.location();
328 } else
329 renderFlowThreadOffset = accumulatedOffset - flowThreadPortionRect.location();
330
331 // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView.
332 HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowShadowContent);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100333
334 // Make a new temporary HitTestLocation in the new region.
335 HitTestLocation newHitTestLocation(locationInContainer, -renderFlowThreadOffset, region);
336
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100337 bool isPointInsideFlowThread = layer()->hitTest(newRequest, newHitTestLocation, result);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100338
339 // FIXME: Should we set result.m_localPoint back to the RenderRegion's coordinate space or leave it in the RenderFlowThread's coordinate
340 // 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
341 // patching positionForPoint.
342 return isPointInsideFlowThread;
343}
344
345bool RenderFlowThread::shouldRepaint(const LayoutRect& r) const
346{
347 if (view()->printing() || r.isEmpty())
348 return false;
349
350 return true;
351}
352
353void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect) const
354{
355 if (!shouldRepaint(repaintRect) || !hasValidRegionInfo())
356 return;
357
358 LayoutStateDisabler layoutStateDisabler(view()); // We can't use layout state to repaint, since the regions are somewhere else.
359
360 // We can't use currentFlowThread as it is possible to have interleaved flow threads and the wrong one could be used.
361 // Let each region figure out the proper enclosing flow thread.
362 CurrentRenderFlowThreadDisabler disabler(view());
363
364 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
365 RenderRegion* region = *iter;
366
367 region->repaintFlowThreadContent(repaintRect);
368 }
369}
370
371RenderRegion* RenderFlowThread::regionAtBlockOffset(LayoutUnit offset, bool extendLastRegion, RegionAutoGenerationPolicy autoGenerationPolicy)
372{
373 ASSERT(!m_regionsInvalidated);
374
375 if (autoGenerationPolicy == AllowRegionAutoGeneration)
376 autoGenerateRegionsToBlockOffset(offset);
377
378 if (offset <= 0)
379 return m_regionList.isEmpty() ? 0 : m_regionList.first();
380
381 RegionSearchAdapter adapter(offset);
382 m_regionIntervalTree.allOverlapsWithAdapter<RegionSearchAdapter>(adapter);
383
384 // If no region was found, the offset is in the flow thread overflow.
385 // The last region will contain the offset if extendLastRegion is set or if the last region is a set.
386 if (!adapter.result() && !m_regionList.isEmpty() && (extendLastRegion || m_regionList.last()->isRenderRegionSet()))
387 return m_regionList.last();
388
389 return adapter.result();
390}
391
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100392LayoutPoint RenderFlowThread::adjustedPositionRelativeToOffsetParent(const RenderBoxModelObject& boxModelObject, const LayoutPoint& startPoint)
393{
394 LayoutPoint referencePoint = startPoint;
395
396 // FIXME: This needs to be adapted for different writing modes inside the flow thread.
397 RenderRegion* startRegion = regionAtBlockOffset(referencePoint.y());
398 if (startRegion) {
399 // Take into account the offset coordinates of the region.
400 RenderObject* currObject = startRegion;
401 RenderObject* currOffsetParentRenderer;
402 Element* currOffsetParentElement;
403 while ((currOffsetParentElement = currObject->offsetParent()) && (currOffsetParentRenderer = currOffsetParentElement->renderer())) {
404 if (currObject->isBoxModelObject())
405 referencePoint.move(toRenderBoxModelObject(currObject)->offsetLeft(), toRenderBoxModelObject(currObject)->offsetTop());
406
407 // Since we're looking for the offset relative to the body, we must also
408 // take into consideration the borders of the region's offsetParent.
409 if (currOffsetParentRenderer->isBox() && !currOffsetParentRenderer->isBody())
410 referencePoint.move(toRenderBox(currOffsetParentRenderer)->borderLeft(), toRenderBox(currOffsetParentRenderer)->borderTop());
411
412 currObject = currOffsetParentRenderer;
413 }
414
415 // We need to check if any of this box's containing blocks start in a different region
416 // and if so, drop the object's top position (which was computed relative to its containing block
417 // and is no longer valid) and recompute it using the region in which it flows as reference.
418 bool wasComputedRelativeToOtherRegion = false;
419 const RenderBlock* objContainingBlock = boxModelObject.containingBlock();
420 while (objContainingBlock && !objContainingBlock->isRenderNamedFlowThread()) {
421 // Check if this object is in a different region.
422 RenderRegion* parentStartRegion = 0;
423 RenderRegion* parentEndRegion = 0;
424 getRegionRangeForBox(objContainingBlock, parentStartRegion, parentEndRegion);
425 if (parentStartRegion && parentStartRegion != startRegion) {
426 wasComputedRelativeToOtherRegion = true;
427 break;
428 }
429 objContainingBlock = objContainingBlock->containingBlock();
430 }
431
432 if (wasComputedRelativeToOtherRegion) {
433 if (boxModelObject.isBox()) {
434 // Use borderBoxRectInRegion to account for variations such as percentage margins.
435 LayoutRect borderBoxRect = toRenderBox(&boxModelObject)->borderBoxRectInRegion(startRegion, 0, RenderBox::DoNotCacheRenderBoxRegionInfo);
436 referencePoint.move(borderBoxRect.location().x(), 0);
437 }
438
439 // Get the logical top coordinate of the current object.
440 LayoutUnit top = 0;
441 if (boxModelObject.isRenderBlock())
442 top = toRenderBlock(&boxModelObject)->offsetFromLogicalTopOfFirstPage();
443 else {
444 if (boxModelObject.containingBlock())
445 top = boxModelObject.containingBlock()->offsetFromLogicalTopOfFirstPage();
446
447 if (boxModelObject.isBox())
448 top += toRenderBox(&boxModelObject)->topLeftLocation().y();
449 else if (boxModelObject.isRenderInline())
450 top -= toRenderInline(&boxModelObject)->borderTop();
451 }
452
453 // Get the logical top of the region this object starts in
454 // and compute the object's top, relative to the region's top.
455 LayoutUnit regionLogicalTop = startRegion->pageLogicalTopForOffset(top);
456 LayoutUnit topRelativeToRegion = top - regionLogicalTop;
457 referencePoint.setY(startRegion->offsetTop() + topRelativeToRegion);
458
459 // Since the top has been overriden, check if the
460 // relative/sticky positioning must be reconsidered.
461 if (boxModelObject.isRelPositioned())
462 referencePoint.move(0, boxModelObject.relativePositionOffset().height());
463 else if (boxModelObject.isStickyPositioned())
464 referencePoint.move(0, boxModelObject.stickyPositionOffset().height());
465 }
466
467 // Since we're looking for the offset relative to the body, we must also
468 // take into consideration the borders of the region.
469 referencePoint.move(startRegion->borderLeft(), startRegion->borderTop());
470 }
471
472 return referencePoint;
473}
474
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100475LayoutUnit RenderFlowThread::pageLogicalTopForOffset(LayoutUnit offset)
476{
477 RenderRegion* region = regionAtBlockOffset(offset);
478 return region ? region->pageLogicalTopForOffset(offset) : LayoutUnit();
479}
480
481LayoutUnit RenderFlowThread::pageLogicalWidthForOffset(LayoutUnit offset)
482{
483 RenderRegion* region = regionAtBlockOffset(offset, true);
484 return region ? region->pageLogicalWidth() : contentLogicalWidth();
485}
486
487LayoutUnit RenderFlowThread::pageLogicalHeightForOffset(LayoutUnit offset)
488{
489 RenderRegion* region = regionAtBlockOffset(offset);
490 if (!region)
491 return 0;
492
493 return region->pageLogicalHeight();
494}
495
496LayoutUnit RenderFlowThread::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule)
497{
498 RenderRegion* region = regionAtBlockOffset(offset);
499 if (!region)
500 return 0;
501
502 LayoutUnit pageLogicalTop = region->pageLogicalTopForOffset(offset);
503 LayoutUnit pageLogicalHeight = region->pageLogicalHeight();
504 LayoutUnit pageLogicalBottom = pageLogicalTop + pageLogicalHeight;
505 LayoutUnit remainingHeight = pageLogicalBottom - offset;
506 if (pageBoundaryRule == IncludePageBoundary) {
507 // If IncludePageBoundary is set, the line exactly on the top edge of a
508 // region will act as being part of the previous region.
509 remainingHeight = intMod(remainingHeight, pageLogicalHeight);
510 }
511 return remainingHeight;
512}
513
514RenderRegion* RenderFlowThread::mapFromFlowToRegion(TransformState& transformState) const
515{
516 if (!hasValidRegionInfo())
517 return 0;
518
519 LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox();
520 flipForWritingMode(boxRect);
521
522 // FIXME: We need to refactor RenderObject::absoluteQuads to be able to split the quads across regions,
523 // for now we just take the center of the mapped enclosing box and map it to a region.
524 // Note: Using the center in order to avoid rounding errors.
525
526 LayoutPoint center = boxRect.center();
527 RenderRegion* renderRegion = const_cast<RenderFlowThread*>(this)->regionAtBlockOffset(isHorizontalWritingMode() ? center.y() : center.x(), true, DisallowRegionAutoGeneration);
528 if (!renderRegion)
529 return 0;
530
531 LayoutRect flippedRegionRect(renderRegion->flowThreadPortionRect());
532 flipForWritingMode(flippedRegionRect);
533
534 transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location());
535
536 return renderRegion;
537}
538
539void RenderFlowThread::removeRenderBoxRegionInfo(RenderBox* box)
540{
541 if (!hasRegions())
542 return;
543
544 // If the region chain was invalidated the next layout will clear the box information from all the regions.
545 if (m_regionsInvalidated) {
546 ASSERT(selfNeedsLayout());
547 return;
548 }
549
550 RenderRegion* startRegion;
551 RenderRegion* endRegion;
552 getRegionRangeForBox(box, startRegion, endRegion);
553
554 for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) {
555 RenderRegion* region = *iter;
556 region->removeRenderBoxRegionInfo(box);
557 if (region == endRegion)
558 break;
559 }
560
561#ifndef NDEBUG
562 // We have to make sure we did not leave any RenderBoxRegionInfo attached.
563 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
564 RenderRegion* region = *iter;
565 ASSERT(!region->renderBoxRegionInfo(box));
566 }
567#endif
568
569 m_regionRangeMap.remove(box);
570}
571
572bool RenderFlowThread::logicalWidthChangedInRegions(const RenderBlock* block, LayoutUnit offsetFromLogicalTopOfFirstPage)
573{
574 if (!hasRegions())
575 return false;
576
577 RenderRegion* startRegion;
578 RenderRegion* endRegion;
579 getRegionRangeForBox(block, startRegion, endRegion);
580
581 // When the region chain is invalidated the box information is discarded so we must assume the width has changed.
582 if (m_pageLogicalSizeChanged && !startRegion)
583 return true;
584
585 // Not necessary for the flow thread, since we already computed the correct info for it.
586 if (block == this)
587 return false;
588
589 for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) {
590 RenderRegion* region = *iter;
591 ASSERT(!region->needsLayout() || region->isRenderRegionSet());
592
593 OwnPtr<RenderBoxRegionInfo> oldInfo = region->takeRenderBoxRegionInfo(block);
594 if (!oldInfo)
595 continue;
596
597 LayoutUnit oldLogicalWidth = oldInfo->logicalWidth();
598 RenderBoxRegionInfo* newInfo = block->renderBoxRegionInfo(region, offsetFromLogicalTopOfFirstPage);
599 if (!newInfo || newInfo->logicalWidth() != oldLogicalWidth)
600 return true;
601
602 if (region == endRegion)
603 break;
604 }
605
606 return false;
607}
608
609LayoutUnit RenderFlowThread::contentLogicalWidthOfFirstRegion() const
610{
611 RenderRegion* firstValidRegionInFlow = firstRegion();
612 if (!firstValidRegionInFlow)
613 return 0;
614 return isHorizontalWritingMode() ? firstValidRegionInFlow->contentWidth() : firstValidRegionInFlow->contentHeight();
615}
616
617LayoutUnit RenderFlowThread::contentLogicalHeightOfFirstRegion() const
618{
619 RenderRegion* firstValidRegionInFlow = firstRegion();
620 if (!firstValidRegionInFlow)
621 return 0;
622 return isHorizontalWritingMode() ? firstValidRegionInFlow->contentHeight() : firstValidRegionInFlow->contentWidth();
623}
624
625LayoutUnit RenderFlowThread::contentLogicalLeftOfFirstRegion() const
626{
627 RenderRegion* firstValidRegionInFlow = firstRegion();
628 if (!firstValidRegionInFlow)
629 return 0;
630 return isHorizontalWritingMode() ? firstValidRegionInFlow->flowThreadPortionRect().x() : firstValidRegionInFlow->flowThreadPortionRect().y();
631}
632
633RenderRegion* RenderFlowThread::firstRegion() const
634{
635 if (!hasValidRegionInfo())
636 return 0;
637 return m_regionList.first();
638}
639
640RenderRegion* RenderFlowThread::lastRegion() const
641{
642 if (!hasValidRegionInfo())
643 return 0;
644 return m_regionList.last();
645}
646
647void RenderFlowThread::clearRenderObjectCustomStyle(const RenderObject* object,
648 const RenderRegion* oldStartRegion, const RenderRegion* oldEndRegion,
649 const RenderRegion* newStartRegion, const RenderRegion* newEndRegion)
650{
651 // Clear the styles for the object in the regions.
652 // The styles are not cleared for the regions that are contained in both ranges.
653 bool insideOldRegionRange = false;
654 bool insideNewRegionRange = false;
655 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
656 RenderRegion* region = *iter;
657
658 if (oldStartRegion == region)
659 insideOldRegionRange = true;
660 if (newStartRegion == region)
661 insideNewRegionRange = true;
662
663 if (!(insideOldRegionRange && insideNewRegionRange))
664 region->clearObjectStyleInRegion(object);
665
666 if (oldEndRegion == region)
667 insideOldRegionRange = false;
668 if (newEndRegion == region)
669 insideNewRegionRange = false;
670 }
671}
672
673void RenderFlowThread::setRegionRangeForBox(const RenderBox* box, LayoutUnit offsetFromLogicalTopOfFirstPage)
674{
675 if (!hasRegions())
676 return;
677
678 // FIXME: Not right for differing writing-modes.
679 RenderRegion* startRegion = regionAtBlockOffset(offsetFromLogicalTopOfFirstPage, true);
680 RenderRegion* endRegion = regionAtBlockOffset(offsetFromLogicalTopOfFirstPage + box->logicalHeight(), true);
681 RenderRegionRangeMap::iterator it = m_regionRangeMap.find(box);
682 if (it == m_regionRangeMap.end()) {
683 m_regionRangeMap.set(box, RenderRegionRange(startRegion, endRegion));
684 clearRenderObjectCustomStyle(box);
685 return;
686 }
687
688 // If nothing changed, just bail.
689 RenderRegionRange& range = it->value;
690 if (range.startRegion() == startRegion && range.endRegion() == endRegion)
691 return;
692
693 // Delete any info that we find before our new startRegion and after our new endRegion.
694 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
695 RenderRegion* region = *iter;
696 if (region == startRegion) {
697 iter = m_regionList.find(endRegion);
698 continue;
699 }
700
701 region->removeRenderBoxRegionInfo(box);
702
703 if (region == range.endRegion())
704 break;
705 }
706
707 clearRenderObjectCustomStyle(box, range.startRegion(), range.endRegion(), startRegion, endRegion);
708 range.setRange(startRegion, endRegion);
709}
710
711void RenderFlowThread::getRegionRangeForBox(const RenderBox* box, RenderRegion*& startRegion, RenderRegion*& endRegion) const
712{
713 startRegion = 0;
714 endRegion = 0;
715 RenderRegionRangeMap::const_iterator it = m_regionRangeMap.find(box);
716 if (it == m_regionRangeMap.end())
717 return;
718
719 const RenderRegionRange& range = it->value;
720 startRegion = range.startRegion();
721 endRegion = range.endRegion();
722 ASSERT(m_regionList.contains(startRegion) && m_regionList.contains(endRegion));
723}
724
725void RenderFlowThread::applyBreakAfterContent(LayoutUnit clientHeight)
726{
727 // Simulate a region break at height. If it points inside an auto logical height region,
728 // then it may determine the region override logical content height.
729 addForcedRegionBreak(clientHeight, this, false);
730}
731
732void RenderFlowThread::computeOverflowStateForRegions(LayoutUnit oldClientAfterEdge)
733{
734 LayoutUnit height = oldClientAfterEdge;
735
736 // FIXME: the visual overflow of middle region (if it is the last one to contain any content in a render flow thread)
737 // might not be taken into account because the render flow thread height is greater that that regions height + its visual overflow
738 // because of how computeLogicalHeight is implemented for RenderFlowThread (as a sum of all regions height).
739 // This means that the middle region will be marked as fit (even if it has visual overflow flowing into the next region)
740 if (hasRenderOverflow()
741 && ( (isHorizontalWritingMode() && visualOverflowRect().maxY() > clientBoxRect().maxY())
742 || (!isHorizontalWritingMode() && visualOverflowRect().maxX() > clientBoxRect().maxX())))
743 height = isHorizontalWritingMode() ? visualOverflowRect().maxY() : visualOverflowRect().maxX();
744
745 RenderRegion* lastReg = lastRegion();
746 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
747 RenderRegion* region = *iter;
748 LayoutUnit flowMin = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().y() : region->flowThreadPortionRect().x());
749 LayoutUnit flowMax = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().maxY() : region->flowThreadPortionRect().maxX());
750 RenderRegion::RegionState previousState = region->regionState();
751 RenderRegion::RegionState state = RenderRegion::RegionFit;
752 if (flowMin <= 0)
753 state = RenderRegion::RegionEmpty;
754 if (flowMax > 0 && region == lastReg)
755 state = RenderRegion::RegionOverset;
756 region->setRegionState(state);
757 // determine whether the NamedFlow object should dispatch a regionLayoutUpdate event
758 // FIXME: currently it cannot determine whether a region whose regionOverset state remained either "fit" or "overset" has actually
759 // changed, so it just assumes that the NamedFlow should dispatch the event
760 if (previousState != state
761 || state == RenderRegion::RegionFit
762 || state == RenderRegion::RegionOverset)
763 setDispatchRegionLayoutUpdateEvent(true);
764 }
765
766 // With the regions overflow state computed we can also set the overset flag for the named flow.
767 // If there are no valid regions in the chain, overset is true.
768 m_overset = lastReg ? lastReg->regionState() == RenderRegion::RegionOverset : true;
769}
770
771bool RenderFlowThread::regionInRange(const RenderRegion* targetRegion, const RenderRegion* startRegion, const RenderRegion* endRegion) const
772{
773 ASSERT(targetRegion);
774
775 for (RenderRegionList::const_iterator it = m_regionList.find(const_cast<RenderRegion*>(startRegion)); it != m_regionList.end(); ++it) {
776 const RenderRegion* currRegion = *it;
777 if (targetRegion == currRegion)
778 return true;
779 if (currRegion == endRegion)
780 break;
781 }
782
783 return false;
784}
785
786// Check if the content is flown into at least a region with region styling rules.
787void RenderFlowThread::checkRegionsWithStyling()
788{
789 bool hasRegionsWithStyling = false;
790 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
791 RenderRegion* region = *iter;
792 if (region->hasCustomRegionStyle()) {
793 hasRegionsWithStyling = true;
794 break;
795 }
796 }
797 m_hasRegionsWithStyling = hasRegionsWithStyling;
798}
799
800bool RenderFlowThread::objectInFlowRegion(const RenderObject* object, const RenderRegion* region) const
801{
802 ASSERT(object);
803 ASSERT(region);
804
805 RenderFlowThread* flowThread = object->flowThreadContainingBlock();
806 if (flowThread != this)
807 return false;
808 if (!m_regionList.contains(const_cast<RenderRegion*>(region)))
809 return false;
810
811 RenderBox* enclosingBox = object->enclosingBox();
812 RenderRegion* enclosingBoxStartRegion = 0;
813 RenderRegion* enclosingBoxEndRegion = 0;
814 getRegionRangeForBox(enclosingBox, enclosingBoxStartRegion, enclosingBoxEndRegion);
815 if (!regionInRange(region, enclosingBoxStartRegion, enclosingBoxEndRegion))
816 return false;
817
818 if (object->isBox())
819 return true;
820
821 LayoutRect objectABBRect = object->absoluteBoundingBoxRect(true);
822 if (!objectABBRect.width())
823 objectABBRect.setWidth(1);
824 if (!objectABBRect.height())
825 objectABBRect.setHeight(1);
826 if (objectABBRect.intersects(region->absoluteBoundingBoxRect(true)))
827 return true;
828
829 if (region == lastRegion()) {
830 // If the object does not intersect any of the enclosing box regions
831 // then the object is in last region.
832 for (RenderRegionList::const_iterator it = m_regionList.find(enclosingBoxStartRegion); it != m_regionList.end(); ++it) {
833 const RenderRegion* currRegion = *it;
834 if (currRegion == region)
835 break;
836 if (objectABBRect.intersects(currRegion->absoluteBoundingBoxRect(true)))
837 return false;
838 }
839 return true;
840 }
841
842 return false;
843}
844
845#ifndef NDEBUG
846bool RenderFlowThread::isAutoLogicalHeightRegionsCountConsistent() const
847{
848 unsigned autoLogicalHeightRegions = 0;
849 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
850 const RenderRegion* region = *iter;
851 if (region->hasAutoLogicalHeight())
852 autoLogicalHeightRegions++;
853 }
854
855 return autoLogicalHeightRegions == m_autoLogicalHeightRegionsCount;
856}
857#endif
858
859// During the normal layout phase of the named flow the regions are initialized with a height equal to their max-height.
860// This way unforced breaks are automatically placed when a region is full and the content height/position correctly estimated.
861// Also, the region where a forced break falls is exactly the region found at the forced break offset inside the flow content.
862void RenderFlowThread::initializeRegionsOverrideLogicalContentHeight(RenderRegion* startRegion)
863{
864 ASSERT(!inConstrainedLayoutPhase());
865 if (!hasAutoLogicalHeightRegions())
866 return;
867
868 RenderRegionList::iterator regionIter = startRegion ? m_regionList.find(startRegion) : m_regionList.begin();
869 for (; regionIter != m_regionList.end(); ++regionIter) {
870 RenderRegion* region = *regionIter;
871 if (region->hasAutoLogicalHeight())
872 region->setOverrideLogicalContentHeight(region->maxPageLogicalHeight());
873 }
874}
875
876void RenderFlowThread::markAutoLogicalHeightRegionsForLayout()
877{
878 ASSERT(hasAutoLogicalHeightRegions());
879
880 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
881 RenderRegion* region = *iter;
882 if (!region->hasAutoLogicalHeight())
883 continue;
884
885 // FIXME: We need to find a way to avoid marking all the regions ancestors for layout
886 // as we are already inside layout.
887 region->setNeedsLayout(true);
888 }
889}
890
891void RenderFlowThread::updateRegionsFlowThreadPortionRect(const RenderRegion* lastRegionWithContent)
892{
893 ASSERT(!lastRegionWithContent || (!inConstrainedLayoutPhase() && hasAutoLogicalHeightRegions()));
894 LayoutUnit logicalHeight = 0;
895 bool emptyRegionsSegment = false;
896 // FIXME: Optimize not to clear the interval all the time. This implies manually managing the tree nodes lifecycle.
897 m_regionIntervalTree.clear();
898 m_regionIntervalTree.initIfNeeded();
899 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
900 RenderRegion* region = *iter;
901
902 // If we find an empty auto-height region, clear the overrideLogicalContentHeight value.
903 if (emptyRegionsSegment && region->hasAutoLogicalHeight())
904 region->clearOverrideLogicalContentHeight();
905
906 LayoutUnit regionLogicalWidth = region->pageLogicalWidth();
907 LayoutUnit regionLogicalHeight = std::min<LayoutUnit>(LayoutUnit::max() / 2 - logicalHeight, region->logicalHeightOfAllFlowThreadContent());
908
909 LayoutRect regionRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - regionLogicalWidth, logicalHeight, regionLogicalWidth, regionLogicalHeight);
910
911 region->setFlowThreadPortionRect(isHorizontalWritingMode() ? regionRect : regionRect.transposedRect());
912
913 m_regionIntervalTree.add(RegionIntervalTree::createInterval(logicalHeight, logicalHeight + regionLogicalHeight, region));
914
915 logicalHeight += regionLogicalHeight;
916
917 // Once we find the last region with content the next regions are considered empty.
918 if (lastRegionWithContent == region)
919 emptyRegionsSegment = true;
920 }
921
922 ASSERT(!lastRegionWithContent || emptyRegionsSegment);
923}
924
925// Even if we require the break to occur at offsetBreakInFlowThread, because regions may have min/max-height values,
926// it is possible that the break will occur at a different offset than the original one required.
927// offsetBreakAdjustment measures the different between the requested break offset and the current break offset.
928bool RenderFlowThread::addForcedRegionBreak(LayoutUnit offsetBreakInFlowThread, RenderObject* breakChild, bool isBefore, LayoutUnit* offsetBreakAdjustment)
929{
930 // We take breaks into account for height computation for auto logical height regions
931 // only in the layout phase in which we lay out the flows threads unconstrained
932 // and we use the content breaks to determine the overrideContentLogicalHeight for
933 // auto logical height regions.
934 if (inConstrainedLayoutPhase())
935 return false;
936
937 // Breaks can come before or after some objects. We need to track these objects, so that if we get
938 // multiple breaks for the same object (for example because of multiple layouts on the same object),
939 // we need to invalidate every other region after the old one and start computing from fresh.
940 RenderObjectToRegionMap& mapToUse = isBefore ? m_breakBeforeToRegionMap : m_breakAfterToRegionMap;
941 RenderObjectToRegionMap::iterator iter = mapToUse.find(breakChild);
942 if (iter != mapToUse.end()) {
943 RenderRegionList::iterator regionIter = m_regionList.find(iter->value);
944 ASSERT(regionIter != m_regionList.end());
945 ASSERT((*regionIter)->hasAutoLogicalHeight());
946 initializeRegionsOverrideLogicalContentHeight(*regionIter);
947
948 // We need to update the regions flow thread portion rect because we are going to process
949 // a break on these regions.
950 updateRegionsFlowThreadPortionRect();
951 }
952
953 // Simulate a region break at offsetBreakInFlowThread. If it points inside an auto logical height region,
954 // then it determines the region override logical content height.
955 RenderRegion* region = regionAtBlockOffset(offsetBreakInFlowThread);
956 if (!region)
957 return false;
958
959 bool lastBreakAfterContent = breakChild == this;
960 bool overrideLogicalContentHeightComputed = false;
961
962 LayoutUnit currentRegionOffsetInFlowThread = isHorizontalWritingMode() ? region->flowThreadPortionRect().y() : region->flowThreadPortionRect().x();
963 LayoutUnit offsetBreakInCurrentRegion = offsetBreakInFlowThread - currentRegionOffsetInFlowThread;
964
965 if (region->hasAutoLogicalHeight()) {
966 // A forced break can appear only in an auto-height region that didn't have a forced break before.
967 // This ASSERT is a good-enough heuristic to verify the above condition.
968 ASSERT(region->maxPageLogicalHeight() == region->overrideLogicalContentHeight());
969
970 mapToUse.set(breakChild, region);
971
972 overrideLogicalContentHeightComputed = true;
973
974 // Compute the region height pretending that the offsetBreakInCurrentRegion is the logicalHeight for the auto-height region.
975 LayoutUnit regionOverrideLogicalContentHeight = region->computeReplacedLogicalHeightRespectingMinMaxHeight(offsetBreakInCurrentRegion);
976
977 // The new height of this region needs to be smaller than the initial value, the max height. A forced break is the only way to change the initial
978 // height of an auto-height region besides content ending.
979 ASSERT(regionOverrideLogicalContentHeight <= region->maxPageLogicalHeight());
980
981 region->setOverrideLogicalContentHeight(regionOverrideLogicalContentHeight);
982
983 currentRegionOffsetInFlowThread += regionOverrideLogicalContentHeight;
984 } else
985 currentRegionOffsetInFlowThread += isHorizontalWritingMode() ? region->flowThreadPortionRect().height() : region->flowThreadPortionRect().width();
986
987 // If the break was found inside an auto-height region its size changed so we need to recompute the flow thread portion rectangles.
988 // Also, if this is the last break after the content we need to clear the overrideLogicalContentHeight value on the last empty regions.
989 if (hasAutoLogicalHeightRegions() && lastBreakAfterContent)
990 updateRegionsFlowThreadPortionRect(region);
991 else if (overrideLogicalContentHeightComputed)
992 updateRegionsFlowThreadPortionRect();
993
994 if (offsetBreakAdjustment)
995 *offsetBreakAdjustment = max<LayoutUnit>(0, currentRegionOffsetInFlowThread - offsetBreakInFlowThread);
996
997 return overrideLogicalContentHeightComputed;
998}
999
1000void RenderFlowThread::incrementAutoLogicalHeightRegions()
1001{
1002 if (!m_autoLogicalHeightRegionsCount)
1003 view()->flowThreadController()->incrementFlowThreadsWithAutoLogicalHeightRegions();
1004 ++m_autoLogicalHeightRegionsCount;
1005}
1006
1007void RenderFlowThread::decrementAutoLogicalHeightRegions()
1008{
1009 ASSERT(m_autoLogicalHeightRegionsCount > 0);
1010 --m_autoLogicalHeightRegionsCount;
1011 if (!m_autoLogicalHeightRegionsCount)
1012 view()->flowThreadController()->decrementFlowThreadsWithAutoLogicalHeightRegions();
1013}
1014
1015void RenderFlowThread::collectLayerFragments(LayerFragments& layerFragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
1016{
1017 ASSERT(!m_regionsInvalidated);
1018
1019 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
1020 RenderRegion* region = *iter;
1021 region->collectLayerFragments(layerFragments, layerBoundingBox, dirtyRect);
1022 }
1023}
1024
1025LayoutRect RenderFlowThread::fragmentsBoundingBox(const LayoutRect& layerBoundingBox)
1026{
1027 ASSERT(!m_regionsInvalidated);
1028
1029 LayoutRect result;
1030 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
1031 RenderRegion* region = *iter;
1032 LayerFragments fragments;
1033 region->collectLayerFragments(fragments, layerBoundingBox, PaintInfo::infiniteRect());
1034 for (size_t i = 0; i < fragments.size(); ++i) {
1035 const LayerFragment& fragment = fragments.at(i);
1036 LayoutRect fragmentRect(layerBoundingBox);
1037 fragmentRect.intersect(fragment.paginationClip);
1038 fragmentRect.moveBy(fragment.paginationOffset);
1039 result.unite(fragmentRect);
1040 }
1041 }
1042
1043 return result;
1044}
1045
1046void RenderFlowThread::RegionSearchAdapter::collectIfNeeded(const RegionInterval& interval)
1047{
1048 if (m_result)
1049 return;
1050 if (interval.low() <= m_offset && interval.high() > m_offset)
1051 m_result = interval.data();
1052}
1053
1054CurrentRenderFlowThreadMaintainer::CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread)
1055 : m_renderFlowThread(renderFlowThread)
1056 , m_previousRenderFlowThread(0)
1057{
1058 if (!m_renderFlowThread)
1059 return;
1060 RenderView* view = m_renderFlowThread->view();
1061 m_previousRenderFlowThread = view->flowThreadController()->currentRenderFlowThread();
1062 ASSERT(!m_previousRenderFlowThread || !renderFlowThread->isRenderNamedFlowThread());
1063 view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
1064}
1065
1066CurrentRenderFlowThreadMaintainer::~CurrentRenderFlowThreadMaintainer()
1067{
1068 if (!m_renderFlowThread)
1069 return;
1070 RenderView* view = m_renderFlowThread->view();
1071 ASSERT(view->flowThreadController()->currentRenderFlowThread() == m_renderFlowThread);
1072 view->flowThreadController()->setCurrentRenderFlowThread(m_previousRenderFlowThread);
1073}
1074
1075
1076} // namespace WebCore