1 /*
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/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"
44
45 namespace blink {
46
RenderFlowThread()47 RenderFlowThread::RenderFlowThread()
48 : RenderBlockFlow(0)
49 , m_regionsInvalidated(false)
50 , m_regionsHaveUniformLogicalHeight(true)
51 , m_pageLogicalSizeChanged(false)
52 {
53 setFlowThreadState(InsideOutOfFlowThread);
54 }
55
removeRegionFromThread(RenderMultiColumnSet * columnSet)56 void RenderFlowThread::removeRegionFromThread(RenderMultiColumnSet* columnSet)
57 {
58 ASSERT(columnSet);
59 m_multiColumnSetList.remove(columnSet);
60 }
61
invalidateRegions()62 void RenderFlowThread::invalidateRegions()
63 {
64 if (m_regionsInvalidated) {
65 ASSERT(selfNeedsLayout());
66 return;
67 }
68
69 setNeedsLayoutAndFullPaintInvalidation();
70
71 m_regionsInvalidated = true;
72 }
73
74 class CurrentRenderFlowThreadDisabler {
75 WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadDisabler);
76 public:
CurrentRenderFlowThreadDisabler(RenderView * view)77 CurrentRenderFlowThreadDisabler(RenderView* view)
78 : m_view(view)
79 , m_renderFlowThread(0)
80 {
81 m_renderFlowThread = m_view->flowThreadController()->currentRenderFlowThread();
82 if (m_renderFlowThread)
83 view->flowThreadController()->setCurrentRenderFlowThread(0);
84 }
~CurrentRenderFlowThreadDisabler()85 ~CurrentRenderFlowThreadDisabler()
86 {
87 if (m_renderFlowThread)
88 m_view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
89 }
90 private:
91 RenderView* m_view;
92 RenderFlowThread* m_renderFlowThread;
93 };
94
validateRegions()95 void RenderFlowThread::validateRegions()
96 {
97 if (m_regionsInvalidated) {
98 m_regionsInvalidated = false;
99 m_regionsHaveUniformLogicalHeight = true;
100
101 if (hasRegions()) {
102 LayoutUnit previousRegionLogicalHeight = 0;
103 bool firstRegionVisited = false;
104
105 for (RenderMultiColumnSetList::iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
106 RenderMultiColumnSet* columnSet = *iter;
107 LayoutUnit regionLogicalHeight = columnSet->pageLogicalHeight();
108
109 if (!firstRegionVisited) {
110 firstRegionVisited = true;
111 } else {
112 if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
113 m_regionsHaveUniformLogicalHeight = false;
114 }
115
116 previousRegionLogicalHeight = regionLogicalHeight;
117 }
118 }
119 }
120
121 updateLogicalWidth(); // Called to get the maximum logical width for the columnSet.
122 updateRegionsFlowThreadPortionRect();
123 }
124
layout()125 void RenderFlowThread::layout()
126 {
127 m_pageLogicalSizeChanged = m_regionsInvalidated && everHadLayout();
128
129 CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
130 RenderBlockFlow::layout();
131
132 m_pageLogicalSizeChanged = false;
133 }
134
computeLogicalHeight(LayoutUnit,LayoutUnit logicalTop,LogicalExtentComputedValues & computedValues) const135 void RenderFlowThread::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
136 {
137 computedValues.m_position = logicalTop;
138 computedValues.m_extent = 0;
139
140 for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
141 RenderMultiColumnSet* columnSet = *iter;
142 computedValues.m_extent += columnSet->logicalHeightOfAllFlowThreadContent();
143 }
144 }
145
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,const HitTestLocation & locationInContainer,const LayoutPoint & accumulatedOffset,HitTestAction hitTestAction)146 bool RenderFlowThread::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
147 {
148 if (hitTestAction == HitTestBlockBackground)
149 return false;
150 return RenderBlockFlow::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction);
151 }
152
shouldIssuePaintInvalidations(const LayoutRect & r) const153 bool RenderFlowThread::shouldIssuePaintInvalidations(const LayoutRect& r) const
154 {
155 if (view()->document().printing() || r.isEmpty())
156 return false;
157
158 return true;
159 }
160
paintInvalidationRectangleInRegions(const LayoutRect & paintInvalidationRect) const161 void RenderFlowThread::paintInvalidationRectangleInRegions(const LayoutRect& paintInvalidationRect) const
162 {
163 if (!shouldIssuePaintInvalidations(paintInvalidationRect) || !hasValidRegionInfo())
164 return;
165
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());
169
170 for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
171 RenderMultiColumnSet* columnSet = *iter;
172
173 columnSet->paintInvalidationForFlowThreadContent(paintInvalidationRect);
174 }
175 }
176
pageLogicalHeightForOffset(LayoutUnit offset)177 LayoutUnit RenderFlowThread::pageLogicalHeightForOffset(LayoutUnit offset)
178 {
179 RenderMultiColumnSet* columnSet = columnSetAtBlockOffset(offset);
180 if (!columnSet)
181 return 0;
182
183 return columnSet->pageLogicalHeight();
184 }
185
pageRemainingLogicalHeightForOffset(LayoutUnit offset,PageBoundaryRule pageBoundaryRule)186 LayoutUnit RenderFlowThread::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule)
187 {
188 RenderMultiColumnSet* columnSet = columnSetAtBlockOffset(offset);
189 if (!columnSet)
190 return 0;
191
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);
200 }
201 return remainingHeight;
202 }
203
firstRegion() const204 RenderRegion* RenderFlowThread::firstRegion() const
205 {
206 if (!hasValidRegionInfo())
207 return 0;
208 return m_multiColumnSetList.first();
209 }
210
lastRegion() const211 RenderRegion* RenderFlowThread::lastRegion() const
212 {
213 if (!hasValidRegionInfo())
214 return 0;
215 return m_multiColumnSetList.last();
216 }
217
updateRegionsFlowThreadPortionRect()218 void RenderFlowThread::updateRegionsFlowThreadPortionRect()
219 {
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;
226
227 LayoutUnit columnSetLogicalWidth = columnSet->pageLogicalWidth();
228 LayoutUnit columnSetLogicalHeight = std::min<LayoutUnit>(RenderFlowThread::maxLogicalHeight() - logicalHeight, columnSet->logicalHeightOfAllFlowThreadContent());
229
230 LayoutRect columnSetRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - columnSetLogicalWidth, logicalHeight, columnSetLogicalWidth, columnSetLogicalHeight);
231
232 columnSet->setFlowThreadPortionRect(isHorizontalWritingMode() ? columnSetRect : columnSetRect.transposedRect());
233
234 m_multiColumnSetIntervalTree.add(MultiColumnSetIntervalTree::createInterval(logicalHeight, logicalHeight + columnSetLogicalHeight, columnSet));
235
236 logicalHeight += columnSetLogicalHeight;
237 }
238 }
239
collectLayerFragments(LayerFragments & layerFragments,const LayoutRect & layerBoundingBox,const LayoutRect & dirtyRect)240 void RenderFlowThread::collectLayerFragments(LayerFragments& layerFragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
241 {
242 ASSERT(!m_regionsInvalidated);
243
244 for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
245 RenderMultiColumnSet* columnSet = *iter;
246 columnSet->collectLayerFragments(layerFragments, layerBoundingBox, dirtyRect);
247 }
248 }
249
fragmentsBoundingBox(const LayoutRect & layerBoundingBox)250 LayoutRect RenderFlowThread::fragmentsBoundingBox(const LayoutRect& layerBoundingBox)
251 {
252 ASSERT(!m_regionsInvalidated);
253
254 LayoutRect result;
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);
265 }
266 }
267
268 return result;
269 }
270
cachedOffsetFromLogicalTopOfFirstRegion(const RenderBox * box,LayoutUnit & result) const271 bool RenderFlowThread::cachedOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit& result) const
272 {
273 RenderBoxToOffsetMap::const_iterator offsetIterator = m_boxesToOffsetMap.find(box);
274 if (offsetIterator == m_boxesToOffsetMap.end())
275 return false;
276
277 result = offsetIterator->value;
278 return true;
279 }
280
setOffsetFromLogicalTopOfFirstRegion(const RenderBox * box,LayoutUnit offset)281 void RenderFlowThread::setOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit offset)
282 {
283 m_boxesToOffsetMap.set(box, offset);
284 }
285
clearOffsetFromLogicalTopOfFirstRegion(const RenderBox * box)286 void RenderFlowThread::clearOffsetFromLogicalTopOfFirstRegion(const RenderBox* box)
287 {
288 ASSERT(m_boxesToOffsetMap.contains(box));
289 m_boxesToOffsetMap.remove(box);
290 }
291
currentStatePusherRenderBox() const292 const RenderBox* RenderFlowThread::currentStatePusherRenderBox() const
293 {
294 const RenderObject* currentObject = m_statePusherObjectsStack.isEmpty() ? 0 : m_statePusherObjectsStack.last();
295 if (currentObject && currentObject->isBox())
296 return toRenderBox(currentObject);
297
298 return 0;
299 }
300
pushFlowThreadLayoutState(const RenderObject & object)301 void RenderFlowThread::pushFlowThreadLayoutState(const RenderObject& object)
302 {
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());
309 }
310 }
311
312 m_statePusherObjectsStack.add(&object);
313 }
314
popFlowThreadLayoutState()315 void RenderFlowThread::popFlowThreadLayoutState()
316 {
317 m_statePusherObjectsStack.removeLast();
318
319 if (const RenderBox* currentBoxDescendant = currentStatePusherRenderBox()) {
320 LayoutState* layoutState = currentBoxDescendant->view()->layoutState();
321 if (layoutState && layoutState->isPaginated())
322 clearOffsetFromLogicalTopOfFirstRegion(currentBoxDescendant);
323 }
324 }
325
offsetFromLogicalTopOfFirstRegion(const RenderBlock * currentBlock) const326 LayoutUnit RenderFlowThread::offsetFromLogicalTopOfFirstRegion(const RenderBlock* currentBlock) const
327 {
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.
330 LayoutUnit offset;
331 if (cachedOffsetFromLogicalTopOfFirstRegion(currentBlock, offset))
332 return offset;
333
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();
342 }
343
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);
349 if (!containerBlock)
350 return 0;
351 LayoutPoint currentBlockLocation = currentBlock->location();
352
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()->isFlippedBlocksWritingMode()) {
357 if (containerBlock->isHorizontalWritingMode())
358 blockRect.setY(currentBlock->height() - blockRect.maxY());
359 else
360 blockRect.setX(currentBlock->width() - blockRect.maxX());
361 }
362 currentBlock->flipForWritingMode(blockRect);
363 }
364 blockRect.moveBy(currentBlockLocation);
365 currentBlock = containerBlock;
366 }
367
368 return currentBlock->isHorizontalWritingMode() ? blockRect.y() : blockRect.x();
369 }
370
collectIfNeeded(const MultiColumnSetInterval & interval)371 void RenderFlowThread::RegionSearchAdapter::collectIfNeeded(const MultiColumnSetInterval& interval)
372 {
373 if (m_result)
374 return;
375 if (interval.low() <= m_offset && interval.high() > m_offset)
376 m_result = interval.data();
377 }
378
CurrentRenderFlowThreadMaintainer(RenderFlowThread * renderFlowThread)379 CurrentRenderFlowThreadMaintainer::CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread)
380 : m_renderFlowThread(renderFlowThread)
381 , m_previousRenderFlowThread(0)
382 {
383 if (!m_renderFlowThread)
384 return;
385 RenderView* view = m_renderFlowThread->view();
386 m_previousRenderFlowThread = view->flowThreadController()->currentRenderFlowThread();
387 view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
388 }
389
~CurrentRenderFlowThreadMaintainer()390 CurrentRenderFlowThreadMaintainer::~CurrentRenderFlowThreadMaintainer()
391 {
392 if (!m_renderFlowThread)
393 return;
394 RenderView* view = m_renderFlowThread->view();
395 ASSERT(view->flowThreadController()->currentRenderFlowThread() == m_renderFlowThread);
396 view->flowThreadController()->setCurrentRenderFlowThread(m_previousRenderFlowThread);
397 }
398
399
400 } // namespace blink
401