Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / RenderFlowThread.cpp
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
47 RenderFlowThread::RenderFlowThread()
48     : RenderBlockFlow(0)
49     , m_regionsInvalidated(false)
50     , m_regionsHaveUniformLogicalHeight(true)
51     , m_pageLogicalSizeChanged(false)
52 {
53     setFlowThreadState(InsideOutOfFlowThread);
54 }
55
56 void RenderFlowThread::removeRegionFromThread(RenderMultiColumnSet* columnSet)
57 {
58     ASSERT(columnSet);
59     m_multiColumnSetList.remove(columnSet);
60 }
61
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:
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     }
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
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
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
135 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->logicalHeightInFlowThread();
143     }
144 }
145
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
153 bool RenderFlowThread::shouldIssuePaintInvalidations(const LayoutRect& r) const
154 {
155     if (view()->document().printing() || r.isEmpty())
156         return false;
157
158     return true;
159 }
160
161 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
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
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
204 RenderRegion* RenderFlowThread::firstRegion() const
205 {
206     if (!hasValidRegionInfo())
207         return 0;
208     return m_multiColumnSetList.first();
209 }
210
211 RenderRegion* RenderFlowThread::lastRegion() const
212 {
213     if (!hasValidRegionInfo())
214         return 0;
215     return m_multiColumnSetList.last();
216 }
217
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->logicalHeightInFlowThread());
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
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
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
271 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
281 void RenderFlowThread::setOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit offset)
282 {
283     m_boxesToOffsetMap.set(box, offset);
284 }
285
286 void RenderFlowThread::clearOffsetFromLogicalTopOfFirstRegion(const RenderBox* box)
287 {
288     ASSERT(m_boxesToOffsetMap.contains(box));
289     m_boxesToOffsetMap.remove(box);
290 }
291
292 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
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
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
326 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()->slowIsFlippedBlocksWritingMode()) {
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
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
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
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