Upstream version 7.36.149.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/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"
45
46 namespace WebCore {
47
48 RenderFlowThread::RenderFlowThread()
49     : RenderBlockFlow(0)
50     , m_regionsInvalidated(false)
51     , m_regionsHaveUniformLogicalHeight(true)
52     , m_pageLogicalSizeChanged(false)
53 {
54     setFlowThreadState(InsideOutOfFlowThread);
55 }
56
57 void RenderFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
58 {
59     ASSERT(renderRegion);
60     m_regionList.remove(renderRegion);
61 }
62
63 void RenderFlowThread::invalidateRegions()
64 {
65     if (m_regionsInvalidated) {
66         ASSERT(selfNeedsLayout());
67         return;
68     }
69
70     m_regionRangeMap.clear();
71     setNeedsLayout();
72
73     m_regionsInvalidated = true;
74 }
75
76 class CurrentRenderFlowThreadDisabler {
77     WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadDisabler);
78 public:
79     CurrentRenderFlowThreadDisabler(RenderView* view)
80         : m_view(view)
81         , m_renderFlowThread(0)
82     {
83         m_renderFlowThread = m_view->flowThreadController()->currentRenderFlowThread();
84         if (m_renderFlowThread)
85             view->flowThreadController()->setCurrentRenderFlowThread(0);
86     }
87     ~CurrentRenderFlowThreadDisabler()
88     {
89         if (m_renderFlowThread)
90             m_view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
91     }
92 private:
93     RenderView* m_view;
94     RenderFlowThread* m_renderFlowThread;
95 };
96
97 void RenderFlowThread::validateRegions()
98 {
99     if (m_regionsInvalidated) {
100         m_regionsInvalidated = false;
101         m_regionsHaveUniformLogicalHeight = true;
102
103         if (hasRegions()) {
104             LayoutUnit previousRegionLogicalHeight = 0;
105             bool firstRegionVisited = false;
106
107             for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
108                 RenderRegion* region = *iter;
109                 LayoutUnit regionLogicalHeight = region->pageLogicalHeight();
110
111                 if (!firstRegionVisited) {
112                     firstRegionVisited = true;
113                 } else {
114                     if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
115                         m_regionsHaveUniformLogicalHeight = false;
116                 }
117
118                 previousRegionLogicalHeight = regionLogicalHeight;
119             }
120         }
121     }
122
123     updateLogicalWidth(); // Called to get the maximum logical width for the region.
124     updateRegionsFlowThreadPortionRect();
125 }
126
127 void RenderFlowThread::layout()
128 {
129     m_pageLogicalSizeChanged = m_regionsInvalidated && everHadLayout();
130
131     validateRegions();
132
133     CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
134     RenderBlockFlow::layout();
135
136     m_pageLogicalSizeChanged = false;
137 }
138
139 void RenderFlowThread::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
140 {
141     computedValues.m_position = logicalTop;
142     computedValues.m_extent = 0;
143
144     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
145         RenderRegion* region = *iter;
146         computedValues.m_extent += region->logicalHeightOfAllFlowThreadContent();
147     }
148 }
149
150 bool RenderFlowThread::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
151 {
152     if (hitTestAction == HitTestBlockBackground)
153         return false;
154     return RenderBlockFlow::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction);
155 }
156
157 bool RenderFlowThread::shouldRepaint(const LayoutRect& r) const
158 {
159     if (view()->document().printing() || r.isEmpty())
160         return false;
161
162     return true;
163 }
164
165 void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect) const
166 {
167     if (!shouldRepaint(repaintRect) || !hasValidRegionInfo())
168         return;
169
170     LayoutStateDisabler layoutStateDisabler(*this); // We can't use layout state to repaint, since the regions are somewhere else.
171
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());
175
176     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
177         RenderRegion* region = *iter;
178
179         region->repaintFlowThreadContent(repaintRect);
180     }
181 }
182
183 RenderRegion* RenderFlowThread::regionAtBlockOffset(LayoutUnit offset) const
184 {
185     ASSERT(!m_regionsInvalidated);
186
187     if (offset <= 0)
188         return m_regionList.isEmpty() ? 0 : m_regionList.first();
189
190     RegionSearchAdapter adapter(offset);
191     m_regionIntervalTree.allOverlapsWithAdapter<RegionSearchAdapter>(adapter);
192
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();
196
197     return adapter.result();
198 }
199
200 LayoutPoint RenderFlowThread::adjustedPositionRelativeToOffsetParent(const RenderBoxModelObject& boxModelObject, const LayoutPoint& startPoint)
201 {
202     LayoutPoint referencePoint = startPoint;
203
204     // FIXME: This needs to be adapted for different writing modes inside the flow thread.
205     RenderRegion* startRegion = regionAtBlockOffset(referencePoint.y());
206     if (startRegion) {
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());
214
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());
219
220             currObject = currOffsetParentRenderer;
221         }
222
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;
235                 break;
236             }
237             objContainingBlock = objContainingBlock->containingBlock();
238         }
239
240         if (wasComputedRelativeToOtherRegion) {
241             // Get the logical top coordinate of the current object.
242             LayoutUnit top = 0;
243             if (boxModelObject.isRenderBlock()) {
244                 top = toRenderBlock(&boxModelObject)->offsetFromLogicalTopOfFirstPage();
245             } else {
246                 if (boxModelObject.containingBlock())
247                     top = boxModelObject.containingBlock()->offsetFromLogicalTopOfFirstPage();
248
249                 if (boxModelObject.isBox())
250                     top += toRenderBox(&boxModelObject)->topLeftLocation().y();
251                 else if (boxModelObject.isRenderInline())
252                     top -= toRenderInline(&boxModelObject)->borderTop();
253             }
254
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);
260
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());
267         }
268
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());
272     }
273
274     return referencePoint;
275 }
276
277 LayoutUnit RenderFlowThread::pageLogicalTopForOffset(LayoutUnit offset)
278 {
279     RenderRegion* region = regionAtBlockOffset(offset);
280     return region ? region->pageLogicalTopForOffset(offset) : LayoutUnit();
281 }
282
283 LayoutUnit RenderFlowThread::pageLogicalHeightForOffset(LayoutUnit offset)
284 {
285     RenderRegion* region = regionAtBlockOffset(offset);
286     if (!region)
287         return 0;
288
289     return region->pageLogicalHeight();
290 }
291
292 LayoutUnit RenderFlowThread::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule)
293 {
294     RenderRegion* region = regionAtBlockOffset(offset);
295     if (!region)
296         return 0;
297
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);
306     }
307     return remainingHeight;
308 }
309
310 RenderRegion* RenderFlowThread::firstRegion() const
311 {
312     if (!hasValidRegionInfo())
313         return 0;
314     return m_regionList.first();
315 }
316
317 RenderRegion* RenderFlowThread::lastRegion() const
318 {
319     if (!hasValidRegionInfo())
320         return 0;
321     return m_regionList.last();
322 }
323
324 void RenderFlowThread::setRegionRangeForBox(const RenderBox* box, LayoutUnit offsetFromLogicalTopOfFirstPage)
325 {
326     if (!hasRegions())
327         return;
328
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));
335         return;
336     }
337
338     // If nothing changed, just bail.
339     RenderRegionRange& range = it->value;
340     if (range.startRegion() == startRegion && range.endRegion() == endRegion)
341         return;
342
343     range.setRange(startRegion, endRegion);
344 }
345
346 void RenderFlowThread::getRegionRangeForBox(const RenderBox* box, RenderRegion*& startRegion, RenderRegion*& endRegion) const
347 {
348     startRegion = 0;
349     endRegion = 0;
350     RenderRegionRangeMap::const_iterator it = m_regionRangeMap.find(box);
351     if (it == m_regionRangeMap.end())
352         return;
353
354     const RenderRegionRange& range = it->value;
355     startRegion = range.startRegion();
356     endRegion = range.endRegion();
357     ASSERT(m_regionList.contains(startRegion) && m_regionList.contains(endRegion));
358 }
359
360 void RenderFlowThread::updateRegionsFlowThreadPortionRect()
361 {
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;
368
369         LayoutUnit regionLogicalWidth = region->pageLogicalWidth();
370         LayoutUnit regionLogicalHeight = std::min<LayoutUnit>(RenderFlowThread::maxLogicalHeight() - logicalHeight, region->logicalHeightOfAllFlowThreadContent());
371
372         LayoutRect regionRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - regionLogicalWidth, logicalHeight, regionLogicalWidth, regionLogicalHeight);
373
374         region->setFlowThreadPortionRect(isHorizontalWritingMode() ? regionRect : regionRect.transposedRect());
375
376         m_regionIntervalTree.add(RegionIntervalTree::createInterval(logicalHeight, logicalHeight + regionLogicalHeight, region));
377
378         logicalHeight += regionLogicalHeight;
379     }
380 }
381
382 void RenderFlowThread::collectLayerFragments(LayerFragments& layerFragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
383 {
384     ASSERT(!m_regionsInvalidated);
385
386     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
387         RenderRegion* region = *iter;
388         region->collectLayerFragments(layerFragments, layerBoundingBox, dirtyRect);
389     }
390 }
391
392 LayoutRect RenderFlowThread::fragmentsBoundingBox(const LayoutRect& layerBoundingBox)
393 {
394     ASSERT(!m_regionsInvalidated);
395
396     LayoutRect result;
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);
407         }
408     }
409
410     return result;
411 }
412
413 bool RenderFlowThread::cachedOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit& result) const
414 {
415     RenderBoxToOffsetMap::const_iterator offsetIterator = m_boxesToOffsetMap.find(box);
416     if (offsetIterator == m_boxesToOffsetMap.end())
417         return false;
418
419     result = offsetIterator->value;
420     return true;
421 }
422
423 void RenderFlowThread::setOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit offset)
424 {
425     m_boxesToOffsetMap.set(box, offset);
426 }
427
428 void RenderFlowThread::clearOffsetFromLogicalTopOfFirstRegion(const RenderBox* box)
429 {
430     ASSERT(m_boxesToOffsetMap.contains(box));
431     m_boxesToOffsetMap.remove(box);
432 }
433
434 const RenderBox* RenderFlowThread::currentStatePusherRenderBox() const
435 {
436     const RenderObject* currentObject = m_statePusherObjectsStack.isEmpty() ? 0 : m_statePusherObjectsStack.last();
437     if (currentObject && currentObject->isBox())
438         return toRenderBox(currentObject);
439
440     return 0;
441 }
442
443 void RenderFlowThread::pushFlowThreadLayoutState(const RenderObject& object)
444 {
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());
451         }
452     }
453
454     m_statePusherObjectsStack.add(&object);
455 }
456
457 void RenderFlowThread::popFlowThreadLayoutState()
458 {
459     m_statePusherObjectsStack.removeLast();
460
461     if (const RenderBox* currentBoxDescendant = currentStatePusherRenderBox()) {
462         LayoutState* layoutState = currentBoxDescendant->view()->layoutState();
463         if (layoutState && layoutState->isPaginated())
464             clearOffsetFromLogicalTopOfFirstRegion(currentBoxDescendant);
465     }
466 }
467
468 LayoutUnit RenderFlowThread::offsetFromLogicalTopOfFirstRegion(const RenderBlock* currentBlock) const
469 {
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.
472     LayoutUnit offset;
473     if (cachedOffsetFromLogicalTopOfFirstRegion(currentBlock, offset))
474         return offset;
475
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();
484     }
485
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);
491         if (!containerBlock)
492             return 0;
493         LayoutPoint currentBlockLocation = currentBlock->location();
494
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());
501                 else
502                     blockRect.setX(currentBlock->width() - blockRect.maxX());
503             }
504             currentBlock->flipForWritingMode(blockRect);
505         }
506         blockRect.moveBy(currentBlockLocation);
507         currentBlock = containerBlock;
508     }
509
510     return currentBlock->isHorizontalWritingMode() ? blockRect.y() : blockRect.x();
511 }
512
513 void RenderFlowThread::RegionSearchAdapter::collectIfNeeded(const RegionInterval& interval)
514 {
515     if (m_result)
516         return;
517     if (interval.low() <= m_offset && interval.high() > m_offset)
518         m_result = interval.data();
519 }
520
521 CurrentRenderFlowThreadMaintainer::CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread)
522     : m_renderFlowThread(renderFlowThread)
523     , m_previousRenderFlowThread(0)
524 {
525     if (!m_renderFlowThread)
526         return;
527     RenderView* view = m_renderFlowThread->view();
528     m_previousRenderFlowThread = view->flowThreadController()->currentRenderFlowThread();
529     view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
530 }
531
532 CurrentRenderFlowThreadMaintainer::~CurrentRenderFlowThreadMaintainer()
533 {
534     if (!m_renderFlowThread)
535         return;
536     RenderView* view = m_renderFlowThread->view();
537     ASSERT(view->flowThreadController()->currentRenderFlowThread() == m_renderFlowThread);
538     view->flowThreadController()->setCurrentRenderFlowThread(m_previousRenderFlowThread);
539 }
540
541
542 } // namespace WebCore