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/RenderLayer.h"
40 #include "core/rendering/RenderMultiColumnSet.h"
41 #include "core/rendering/RenderView.h"
42 #include "platform/PODIntervalTree.h"
43 #include "platform/geometry/TransformState.h"
47 RenderFlowThread::RenderFlowThread()
49 , m_regionsInvalidated(false)
50 , m_regionsHaveUniformLogicalHeight(true)
51 , m_pageLogicalSizeChanged(false)
53 setFlowThreadState(InsideOutOfFlowThread);
56 void RenderFlowThread::removeRegionFromThread(RenderMultiColumnSet* columnSet)
59 m_multiColumnSetList.remove(columnSet);
62 void RenderFlowThread::invalidateRegions()
64 if (m_regionsInvalidated) {
65 ASSERT(selfNeedsLayout());
69 setNeedsLayoutAndFullPaintInvalidation();
71 m_regionsInvalidated = true;
74 class CurrentRenderFlowThreadDisabler {
75 WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadDisabler);
77 CurrentRenderFlowThreadDisabler(RenderView* view)
79 , m_renderFlowThread(0)
81 m_renderFlowThread = m_view->flowThreadController()->currentRenderFlowThread();
82 if (m_renderFlowThread)
83 view->flowThreadController()->setCurrentRenderFlowThread(0);
85 ~CurrentRenderFlowThreadDisabler()
87 if (m_renderFlowThread)
88 m_view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
92 RenderFlowThread* m_renderFlowThread;
95 void RenderFlowThread::validateRegions()
97 if (m_regionsInvalidated) {
98 m_regionsInvalidated = false;
99 m_regionsHaveUniformLogicalHeight = true;
102 LayoutUnit previousRegionLogicalHeight = 0;
103 bool firstRegionVisited = false;
105 for (RenderMultiColumnSetList::iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
106 RenderMultiColumnSet* columnSet = *iter;
107 LayoutUnit regionLogicalHeight = columnSet->pageLogicalHeight();
109 if (!firstRegionVisited) {
110 firstRegionVisited = true;
112 if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
113 m_regionsHaveUniformLogicalHeight = false;
116 previousRegionLogicalHeight = regionLogicalHeight;
121 updateLogicalWidth(); // Called to get the maximum logical width for the columnSet.
122 updateRegionsFlowThreadPortionRect();
125 void RenderFlowThread::layout()
127 m_pageLogicalSizeChanged = m_regionsInvalidated && everHadLayout();
129 CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
130 RenderBlockFlow::layout();
132 m_pageLogicalSizeChanged = false;
135 void RenderFlowThread::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
137 computedValues.m_position = logicalTop;
138 computedValues.m_extent = 0;
140 for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
141 RenderMultiColumnSet* columnSet = *iter;
142 computedValues.m_extent += columnSet->logicalHeightInFlowThread();
146 bool RenderFlowThread::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
148 if (hitTestAction == HitTestBlockBackground)
150 return RenderBlockFlow::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction);
153 bool RenderFlowThread::shouldIssuePaintInvalidations(const LayoutRect& r) const
155 if (view()->document().printing() || r.isEmpty())
161 void RenderFlowThread::paintInvalidationRectangleInRegions(const LayoutRect& paintInvalidationRect) const
163 if (!shouldIssuePaintInvalidations(paintInvalidationRect) || !hasValidRegionInfo())
166 // We can't use currentFlowThread as it is possible to have interleaved flow threads and the wrong one could be used.
167 // Let each columnSet figure out the proper enclosing flow thread.
168 CurrentRenderFlowThreadDisabler disabler(view());
170 for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
171 RenderMultiColumnSet* columnSet = *iter;
173 columnSet->paintInvalidationForFlowThreadContent(paintInvalidationRect);
177 LayoutUnit RenderFlowThread::pageLogicalHeightForOffset(LayoutUnit offset)
179 RenderMultiColumnSet* columnSet = columnSetAtBlockOffset(offset);
183 return columnSet->pageLogicalHeight();
186 LayoutUnit RenderFlowThread::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule)
188 RenderMultiColumnSet* columnSet = columnSetAtBlockOffset(offset);
192 LayoutUnit pageLogicalTop = columnSet->pageLogicalTopForOffset(offset);
193 LayoutUnit pageLogicalHeight = columnSet->pageLogicalHeight();
194 LayoutUnit pageLogicalBottom = pageLogicalTop + pageLogicalHeight;
195 LayoutUnit remainingHeight = pageLogicalBottom - offset;
196 if (pageBoundaryRule == IncludePageBoundary) {
197 // If IncludePageBoundary is set, the line exactly on the top edge of a
198 // columnSet will act as being part of the previous columnSet.
199 remainingHeight = intMod(remainingHeight, pageLogicalHeight);
201 return remainingHeight;
204 RenderRegion* RenderFlowThread::firstRegion() const
206 if (!hasValidRegionInfo())
208 return m_multiColumnSetList.first();
211 RenderRegion* RenderFlowThread::lastRegion() const
213 if (!hasValidRegionInfo())
215 return m_multiColumnSetList.last();
218 void RenderFlowThread::updateRegionsFlowThreadPortionRect()
220 LayoutUnit logicalHeight = 0;
221 // FIXME: Optimize not to clear the interval all the time. This implies manually managing the tree nodes lifecycle.
222 m_multiColumnSetIntervalTree.clear();
223 m_multiColumnSetIntervalTree.initIfNeeded();
224 for (RenderMultiColumnSetList::iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
225 RenderMultiColumnSet* columnSet = *iter;
227 LayoutUnit columnSetLogicalWidth = columnSet->pageLogicalWidth();
228 LayoutUnit columnSetLogicalHeight = std::min<LayoutUnit>(RenderFlowThread::maxLogicalHeight() - logicalHeight, columnSet->logicalHeightInFlowThread());
230 LayoutRect columnSetRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - columnSetLogicalWidth, logicalHeight, columnSetLogicalWidth, columnSetLogicalHeight);
232 columnSet->setFlowThreadPortionRect(isHorizontalWritingMode() ? columnSetRect : columnSetRect.transposedRect());
234 m_multiColumnSetIntervalTree.add(MultiColumnSetIntervalTree::createInterval(logicalHeight, logicalHeight + columnSetLogicalHeight, columnSet));
236 logicalHeight += columnSetLogicalHeight;
240 void RenderFlowThread::collectLayerFragments(LayerFragments& layerFragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
242 ASSERT(!m_regionsInvalidated);
244 for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
245 RenderMultiColumnSet* columnSet = *iter;
246 columnSet->collectLayerFragments(layerFragments, layerBoundingBox, dirtyRect);
250 LayoutRect RenderFlowThread::fragmentsBoundingBox(const LayoutRect& layerBoundingBox)
252 ASSERT(!m_regionsInvalidated);
255 for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
256 RenderMultiColumnSet* columnSet = *iter;
257 LayerFragments fragments;
258 columnSet->collectLayerFragments(fragments, layerBoundingBox, PaintInfo::infiniteRect());
259 for (size_t i = 0; i < fragments.size(); ++i) {
260 const LayerFragment& fragment = fragments.at(i);
261 LayoutRect fragmentRect(layerBoundingBox);
262 fragmentRect.intersect(fragment.paginationClip);
263 fragmentRect.moveBy(fragment.paginationOffset);
264 result.unite(fragmentRect);
271 bool RenderFlowThread::cachedOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit& result) const
273 RenderBoxToOffsetMap::const_iterator offsetIterator = m_boxesToOffsetMap.find(box);
274 if (offsetIterator == m_boxesToOffsetMap.end())
277 result = offsetIterator->value;
281 void RenderFlowThread::setOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit offset)
283 m_boxesToOffsetMap.set(box, offset);
286 void RenderFlowThread::clearOffsetFromLogicalTopOfFirstRegion(const RenderBox* box)
288 ASSERT(m_boxesToOffsetMap.contains(box));
289 m_boxesToOffsetMap.remove(box);
292 const RenderBox* RenderFlowThread::currentStatePusherRenderBox() const
294 const RenderObject* currentObject = m_statePusherObjectsStack.isEmpty() ? 0 : m_statePusherObjectsStack.last();
295 if (currentObject && currentObject->isBox())
296 return toRenderBox(currentObject);
301 void RenderFlowThread::pushFlowThreadLayoutState(const RenderObject& object)
303 if (const RenderBox* currentBoxDescendant = currentStatePusherRenderBox()) {
304 LayoutState* layoutState = currentBoxDescendant->view()->layoutState();
305 if (layoutState && layoutState->isPaginated()) {
306 ASSERT(layoutState->renderer() == currentBoxDescendant);
307 LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset();
308 setOffsetFromLogicalTopOfFirstRegion(currentBoxDescendant, currentBoxDescendant->isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width());
312 m_statePusherObjectsStack.add(&object);
315 void RenderFlowThread::popFlowThreadLayoutState()
317 m_statePusherObjectsStack.removeLast();
319 if (const RenderBox* currentBoxDescendant = currentStatePusherRenderBox()) {
320 LayoutState* layoutState = currentBoxDescendant->view()->layoutState();
321 if (layoutState && layoutState->isPaginated())
322 clearOffsetFromLogicalTopOfFirstRegion(currentBoxDescendant);
326 LayoutUnit RenderFlowThread::offsetFromLogicalTopOfFirstRegion(const RenderBlock* currentBlock) const
328 // First check if we cached the offset for the block if it's an ancestor containing block of the box
329 // being currently laid out.
331 if (cachedOffsetFromLogicalTopOfFirstRegion(currentBlock, offset))
334 // If it's the current box being laid out, use the layout state.
335 const RenderBox* currentBoxDescendant = currentStatePusherRenderBox();
336 if (currentBlock == currentBoxDescendant) {
337 LayoutState* layoutState = view()->layoutState();
338 ASSERT(layoutState->renderer() == currentBlock);
339 ASSERT(layoutState && layoutState->isPaginated());
340 LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset();
341 return currentBoxDescendant->isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width();
344 // As a last resort, take the slow path.
345 LayoutRect blockRect(0, 0, currentBlock->width(), currentBlock->height());
346 while (currentBlock && !currentBlock->isRenderFlowThread()) {
347 RenderBlock* containerBlock = currentBlock->containingBlock();
348 ASSERT(containerBlock);
351 LayoutPoint currentBlockLocation = currentBlock->location();
353 if (containerBlock->style()->writingMode() != currentBlock->style()->writingMode()) {
354 // We have to put the block rect in container coordinates
355 // and we have to take into account both the container and current block flipping modes
356 if (containerBlock->style()->slowIsFlippedBlocksWritingMode()) {
357 if (containerBlock->isHorizontalWritingMode())
358 blockRect.setY(currentBlock->height() - blockRect.maxY());
360 blockRect.setX(currentBlock->width() - blockRect.maxX());
362 currentBlock->flipForWritingMode(blockRect);
364 blockRect.moveBy(currentBlockLocation);
365 currentBlock = containerBlock;
368 return currentBlock->isHorizontalWritingMode() ? blockRect.y() : blockRect.x();
371 void RenderFlowThread::RegionSearchAdapter::collectIfNeeded(const MultiColumnSetInterval& interval)
375 if (interval.low() <= m_offset && interval.high() > m_offset)
376 m_result = interval.data();
379 CurrentRenderFlowThreadMaintainer::CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread)
380 : m_renderFlowThread(renderFlowThread)
381 , m_previousRenderFlowThread(0)
383 if (!m_renderFlowThread)
385 RenderView* view = m_renderFlowThread->view();
386 m_previousRenderFlowThread = view->flowThreadController()->currentRenderFlowThread();
387 view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
390 CurrentRenderFlowThreadMaintainer::~CurrentRenderFlowThreadMaintainer()
392 if (!m_renderFlowThread)
394 RenderView* view = m_renderFlowThread->view();
395 ASSERT(view->flowThreadController()->currentRenderFlowThread() == m_renderFlowThread);
396 view->flowThreadController()->setCurrentRenderFlowThread(m_previousRenderFlowThread);