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