2 * Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
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.
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
32 #include "core/rendering/RenderFlowThread.h"
34 #include "core/dom/Node.h"
35 #include "core/rendering/FlowThreadController.h"
36 #include "core/rendering/HitTestRequest.h"
37 #include "core/rendering/HitTestResult.h"
38 #include "core/rendering/PaintInfo.h"
39 #include "core/rendering/RenderInline.h"
40 #include "core/rendering/RenderLayer.h"
41 #include "core/rendering/RenderRegion.h"
42 #include "core/rendering/RenderView.h"
43 #include "platform/PODIntervalTree.h"
44 #include "platform/geometry/TransformState.h"
48 RenderFlowThread::RenderFlowThread()
50 , m_regionsInvalidated(false)
51 , m_regionsHaveUniformLogicalHeight(true)
52 , m_pageLogicalSizeChanged(false)
54 setFlowThreadState(InsideOutOfFlowThread);
57 void RenderFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
60 m_regionList.remove(renderRegion);
63 void RenderFlowThread::invalidateRegions()
65 if (m_regionsInvalidated) {
66 ASSERT(selfNeedsLayout());
70 m_regionRangeMap.clear();
73 m_regionsInvalidated = true;
76 class CurrentRenderFlowThreadDisabler {
77 WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadDisabler);
79 CurrentRenderFlowThreadDisabler(RenderView* view)
81 , m_renderFlowThread(0)
83 m_renderFlowThread = m_view->flowThreadController()->currentRenderFlowThread();
84 if (m_renderFlowThread)
85 view->flowThreadController()->setCurrentRenderFlowThread(0);
87 ~CurrentRenderFlowThreadDisabler()
89 if (m_renderFlowThread)
90 m_view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
94 RenderFlowThread* m_renderFlowThread;
97 void RenderFlowThread::validateRegions()
99 if (m_regionsInvalidated) {
100 m_regionsInvalidated = false;
101 m_regionsHaveUniformLogicalHeight = true;
104 LayoutUnit previousRegionLogicalHeight = 0;
105 bool firstRegionVisited = false;
107 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
108 RenderRegion* region = *iter;
109 LayoutUnit regionLogicalHeight = region->pageLogicalHeight();
111 if (!firstRegionVisited) {
112 firstRegionVisited = true;
114 if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
115 m_regionsHaveUniformLogicalHeight = false;
118 previousRegionLogicalHeight = regionLogicalHeight;
123 updateLogicalWidth(); // Called to get the maximum logical width for the region.
124 updateRegionsFlowThreadPortionRect();
127 void RenderFlowThread::layout()
129 m_pageLogicalSizeChanged = m_regionsInvalidated && everHadLayout();
133 CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
134 RenderBlockFlow::layout();
136 m_pageLogicalSizeChanged = false;
139 void RenderFlowThread::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
141 computedValues.m_position = logicalTop;
142 computedValues.m_extent = 0;
144 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
145 RenderRegion* region = *iter;
146 computedValues.m_extent += region->logicalHeightOfAllFlowThreadContent();
150 bool RenderFlowThread::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
152 if (hitTestAction == HitTestBlockBackground)
154 return RenderBlockFlow::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction);
157 bool RenderFlowThread::shouldRepaint(const LayoutRect& r) const
159 if (view()->document().printing() || r.isEmpty())
165 void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect) const
167 if (!shouldRepaint(repaintRect) || !hasValidRegionInfo())
170 LayoutStateDisabler layoutStateDisabler(*this); // We can't use layout state to repaint, since the regions are somewhere else.
172 // We can't use currentFlowThread as it is possible to have interleaved flow threads and the wrong one could be used.
173 // Let each region figure out the proper enclosing flow thread.
174 CurrentRenderFlowThreadDisabler disabler(view());
176 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
177 RenderRegion* region = *iter;
179 region->repaintFlowThreadContent(repaintRect);
183 RenderRegion* RenderFlowThread::regionAtBlockOffset(LayoutUnit offset) const
185 ASSERT(!m_regionsInvalidated);
188 return m_regionList.isEmpty() ? 0 : m_regionList.first();
190 RegionSearchAdapter adapter(offset);
191 m_regionIntervalTree.allOverlapsWithAdapter<RegionSearchAdapter>(adapter);
193 // If no region was found, the offset is in the flow thread overflow.
194 if (!adapter.result() && !m_regionList.isEmpty())
195 return m_regionList.last();
197 return adapter.result();
200 LayoutPoint RenderFlowThread::adjustedPositionRelativeToOffsetParent(const RenderBoxModelObject& boxModelObject, const LayoutPoint& startPoint)
202 LayoutPoint referencePoint = startPoint;
204 // FIXME: This needs to be adapted for different writing modes inside the flow thread.
205 RenderRegion* startRegion = regionAtBlockOffset(referencePoint.y());
207 // Take into account the offset coordinates of the region.
208 RenderObject* currObject = startRegion;
209 RenderObject* currOffsetParentRenderer;
210 Element* currOffsetParentElement;
211 while ((currOffsetParentElement = currObject->offsetParent()) && (currOffsetParentRenderer = currOffsetParentElement->renderer())) {
212 if (currObject->isBoxModelObject())
213 referencePoint.move(toRenderBoxModelObject(currObject)->offsetLeft(), toRenderBoxModelObject(currObject)->offsetTop());
215 // Since we're looking for the offset relative to the body, we must also
216 // take into consideration the borders of the region's offsetParent.
217 if (currOffsetParentRenderer->isBox() && !currOffsetParentRenderer->isBody())
218 referencePoint.move(toRenderBox(currOffsetParentRenderer)->borderLeft(), toRenderBox(currOffsetParentRenderer)->borderTop());
220 currObject = currOffsetParentRenderer;
223 // We need to check if any of this box's containing blocks start in a different region
224 // and if so, drop the object's top position (which was computed relative to its containing block
225 // and is no longer valid) and recompute it using the region in which it flows as reference.
226 bool wasComputedRelativeToOtherRegion = false;
227 const RenderBlock* objContainingBlock = boxModelObject.containingBlock();
228 while (objContainingBlock) {
229 // Check if this object is in a different region.
230 RenderRegion* parentStartRegion = 0;
231 RenderRegion* parentEndRegion = 0;
232 getRegionRangeForBox(objContainingBlock, parentStartRegion, parentEndRegion);
233 if (parentStartRegion && parentStartRegion != startRegion) {
234 wasComputedRelativeToOtherRegion = true;
237 objContainingBlock = objContainingBlock->containingBlock();
240 if (wasComputedRelativeToOtherRegion) {
241 // Get the logical top coordinate of the current object.
243 if (boxModelObject.isRenderBlock()) {
244 top = toRenderBlock(&boxModelObject)->offsetFromLogicalTopOfFirstPage();
246 if (boxModelObject.containingBlock())
247 top = boxModelObject.containingBlock()->offsetFromLogicalTopOfFirstPage();
249 if (boxModelObject.isBox())
250 top += toRenderBox(&boxModelObject)->topLeftLocation().y();
251 else if (boxModelObject.isRenderInline())
252 top -= toRenderInline(&boxModelObject)->borderTop();
255 // Get the logical top of the region this object starts in
256 // and compute the object's top, relative to the region's top.
257 LayoutUnit regionLogicalTop = startRegion->pageLogicalTopForOffset(top);
258 LayoutUnit topRelativeToRegion = top - regionLogicalTop;
259 referencePoint.setY(startRegion->offsetTop() + topRelativeToRegion);
261 // Since the top has been overriden, check if the
262 // relative/sticky positioning must be reconsidered.
263 if (boxModelObject.isRelPositioned())
264 referencePoint.move(0, boxModelObject.relativePositionOffset().height());
265 else if (boxModelObject.isStickyPositioned())
266 referencePoint.move(0, boxModelObject.stickyPositionOffset().height());
269 // Since we're looking for the offset relative to the body, we must also
270 // take into consideration the borders of the region.
271 referencePoint.move(startRegion->borderLeft(), startRegion->borderTop());
274 return referencePoint;
277 LayoutUnit RenderFlowThread::pageLogicalTopForOffset(LayoutUnit offset)
279 RenderRegion* region = regionAtBlockOffset(offset);
280 return region ? region->pageLogicalTopForOffset(offset) : LayoutUnit();
283 LayoutUnit RenderFlowThread::pageLogicalHeightForOffset(LayoutUnit offset)
285 RenderRegion* region = regionAtBlockOffset(offset);
289 return region->pageLogicalHeight();
292 LayoutUnit RenderFlowThread::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule)
294 RenderRegion* region = regionAtBlockOffset(offset);
298 LayoutUnit pageLogicalTop = region->pageLogicalTopForOffset(offset);
299 LayoutUnit pageLogicalHeight = region->pageLogicalHeight();
300 LayoutUnit pageLogicalBottom = pageLogicalTop + pageLogicalHeight;
301 LayoutUnit remainingHeight = pageLogicalBottom - offset;
302 if (pageBoundaryRule == IncludePageBoundary) {
303 // If IncludePageBoundary is set, the line exactly on the top edge of a
304 // region will act as being part of the previous region.
305 remainingHeight = intMod(remainingHeight, pageLogicalHeight);
307 return remainingHeight;
310 RenderRegion* RenderFlowThread::firstRegion() const
312 if (!hasValidRegionInfo())
314 return m_regionList.first();
317 RenderRegion* RenderFlowThread::lastRegion() const
319 if (!hasValidRegionInfo())
321 return m_regionList.last();
324 void RenderFlowThread::setRegionRangeForBox(const RenderBox* box, LayoutUnit offsetFromLogicalTopOfFirstPage)
329 // FIXME: Not right for differing writing-modes.
330 RenderRegion* startRegion = regionAtBlockOffset(offsetFromLogicalTopOfFirstPage);
331 RenderRegion* endRegion = regionAtBlockOffset(offsetFromLogicalTopOfFirstPage + box->logicalHeight());
332 RenderRegionRangeMap::iterator it = m_regionRangeMap.find(box);
333 if (it == m_regionRangeMap.end()) {
334 m_regionRangeMap.set(box, RenderRegionRange(startRegion, endRegion));
338 // If nothing changed, just bail.
339 RenderRegionRange& range = it->value;
340 if (range.startRegion() == startRegion && range.endRegion() == endRegion)
343 range.setRange(startRegion, endRegion);
346 void RenderFlowThread::getRegionRangeForBox(const RenderBox* box, RenderRegion*& startRegion, RenderRegion*& endRegion) const
350 RenderRegionRangeMap::const_iterator it = m_regionRangeMap.find(box);
351 if (it == m_regionRangeMap.end())
354 const RenderRegionRange& range = it->value;
355 startRegion = range.startRegion();
356 endRegion = range.endRegion();
357 ASSERT(m_regionList.contains(startRegion) && m_regionList.contains(endRegion));
360 void RenderFlowThread::updateRegionsFlowThreadPortionRect()
362 LayoutUnit logicalHeight = 0;
363 // FIXME: Optimize not to clear the interval all the time. This implies manually managing the tree nodes lifecycle.
364 m_regionIntervalTree.clear();
365 m_regionIntervalTree.initIfNeeded();
366 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
367 RenderRegion* region = *iter;
369 LayoutUnit regionLogicalWidth = region->pageLogicalWidth();
370 LayoutUnit regionLogicalHeight = std::min<LayoutUnit>(RenderFlowThread::maxLogicalHeight() - logicalHeight, region->logicalHeightOfAllFlowThreadContent());
372 LayoutRect regionRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - regionLogicalWidth, logicalHeight, regionLogicalWidth, regionLogicalHeight);
374 region->setFlowThreadPortionRect(isHorizontalWritingMode() ? regionRect : regionRect.transposedRect());
376 m_regionIntervalTree.add(RegionIntervalTree::createInterval(logicalHeight, logicalHeight + regionLogicalHeight, region));
378 logicalHeight += regionLogicalHeight;
382 void RenderFlowThread::collectLayerFragments(LayerFragments& layerFragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
384 ASSERT(!m_regionsInvalidated);
386 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
387 RenderRegion* region = *iter;
388 region->collectLayerFragments(layerFragments, layerBoundingBox, dirtyRect);
392 LayoutRect RenderFlowThread::fragmentsBoundingBox(const LayoutRect& layerBoundingBox)
394 ASSERT(!m_regionsInvalidated);
397 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
398 RenderRegion* region = *iter;
399 LayerFragments fragments;
400 region->collectLayerFragments(fragments, layerBoundingBox, PaintInfo::infiniteRect());
401 for (size_t i = 0; i < fragments.size(); ++i) {
402 const LayerFragment& fragment = fragments.at(i);
403 LayoutRect fragmentRect(layerBoundingBox);
404 fragmentRect.intersect(fragment.paginationClip);
405 fragmentRect.moveBy(fragment.paginationOffset);
406 result.unite(fragmentRect);
413 bool RenderFlowThread::cachedOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit& result) const
415 RenderBoxToOffsetMap::const_iterator offsetIterator = m_boxesToOffsetMap.find(box);
416 if (offsetIterator == m_boxesToOffsetMap.end())
419 result = offsetIterator->value;
423 void RenderFlowThread::setOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit offset)
425 m_boxesToOffsetMap.set(box, offset);
428 void RenderFlowThread::clearOffsetFromLogicalTopOfFirstRegion(const RenderBox* box)
430 ASSERT(m_boxesToOffsetMap.contains(box));
431 m_boxesToOffsetMap.remove(box);
434 const RenderBox* RenderFlowThread::currentStatePusherRenderBox() const
436 const RenderObject* currentObject = m_statePusherObjectsStack.isEmpty() ? 0 : m_statePusherObjectsStack.last();
437 if (currentObject && currentObject->isBox())
438 return toRenderBox(currentObject);
443 void RenderFlowThread::pushFlowThreadLayoutState(const RenderObject& object)
445 if (const RenderBox* currentBoxDescendant = currentStatePusherRenderBox()) {
446 LayoutState* layoutState = currentBoxDescendant->view()->layoutState();
447 if (layoutState && layoutState->isPaginated()) {
448 ASSERT(layoutState->renderer() == currentBoxDescendant);
449 LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset();
450 setOffsetFromLogicalTopOfFirstRegion(currentBoxDescendant, currentBoxDescendant->isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width());
454 m_statePusherObjectsStack.add(&object);
457 void RenderFlowThread::popFlowThreadLayoutState()
459 m_statePusherObjectsStack.removeLast();
461 if (const RenderBox* currentBoxDescendant = currentStatePusherRenderBox()) {
462 LayoutState* layoutState = currentBoxDescendant->view()->layoutState();
463 if (layoutState && layoutState->isPaginated())
464 clearOffsetFromLogicalTopOfFirstRegion(currentBoxDescendant);
468 LayoutUnit RenderFlowThread::offsetFromLogicalTopOfFirstRegion(const RenderBlock* currentBlock) const
470 // First check if we cached the offset for the block if it's an ancestor containing block of the box
471 // being currently laid out.
473 if (cachedOffsetFromLogicalTopOfFirstRegion(currentBlock, offset))
476 // If it's the current box being laid out, use the layout state.
477 const RenderBox* currentBoxDescendant = currentStatePusherRenderBox();
478 if (currentBlock == currentBoxDescendant) {
479 LayoutState* layoutState = view()->layoutState();
480 ASSERT(layoutState->renderer() == currentBlock);
481 ASSERT(layoutState && layoutState->isPaginated());
482 LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset();
483 return currentBoxDescendant->isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width();
486 // As a last resort, take the slow path.
487 LayoutRect blockRect(0, 0, currentBlock->width(), currentBlock->height());
488 while (currentBlock && !currentBlock->isRenderFlowThread()) {
489 RenderBlock* containerBlock = currentBlock->containingBlock();
490 ASSERT(containerBlock);
493 LayoutPoint currentBlockLocation = currentBlock->location();
495 if (containerBlock->style()->writingMode() != currentBlock->style()->writingMode()) {
496 // We have to put the block rect in container coordinates
497 // and we have to take into account both the container and current block flipping modes
498 if (containerBlock->style()->isFlippedBlocksWritingMode()) {
499 if (containerBlock->isHorizontalWritingMode())
500 blockRect.setY(currentBlock->height() - blockRect.maxY());
502 blockRect.setX(currentBlock->width() - blockRect.maxX());
504 currentBlock->flipForWritingMode(blockRect);
506 blockRect.moveBy(currentBlockLocation);
507 currentBlock = containerBlock;
510 return currentBlock->isHorizontalWritingMode() ? blockRect.y() : blockRect.x();
513 void RenderFlowThread::RegionSearchAdapter::collectIfNeeded(const RegionInterval& interval)
517 if (interval.low() <= m_offset && interval.high() > m_offset)
518 m_result = interval.data();
521 CurrentRenderFlowThreadMaintainer::CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread)
522 : m_renderFlowThread(renderFlowThread)
523 , m_previousRenderFlowThread(0)
525 if (!m_renderFlowThread)
527 RenderView* view = m_renderFlowThread->view();
528 m_previousRenderFlowThread = view->flowThreadController()->currentRenderFlowThread();
529 view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
532 CurrentRenderFlowThreadMaintainer::~CurrentRenderFlowThreadMaintainer()
534 if (!m_renderFlowThread)
536 RenderView* view = m_renderFlowThread->view();
537 ASSERT(view->flowThreadController()->currentRenderFlowThread() == m_renderFlowThread);
538 view->flowThreadController()->setCurrentRenderFlowThread(m_previousRenderFlowThread);
542 } // namespace WebCore