initial import
[vuplus_webkit] / Source / WebCore / rendering / RenderFlowThread.cpp
1 /*
2  * Copyright 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 "RenderFlowThread.h"
33
34 #include "HitTestRequest.h"
35 #include "HitTestResult.h"
36 #include "Node.h"
37 #include "PaintInfo.h"
38 #include "RenderLayer.h"
39 #include "RenderRegion.h"
40 #include "RenderView.h"
41 #include "TransformState.h"
42
43 namespace WebCore {
44
45 RenderFlowThread::RenderFlowThread(Node* node, const AtomicString& flowThread)
46     : RenderBlock(node)
47     , m_flowThread(flowThread)
48     , m_hasValidRegions(false)
49     , m_regionsInvalidated(false)
50     , m_regionFittingDisableCount(0)
51 {
52     setIsAnonymous(false);
53 }
54
55 PassRefPtr<RenderStyle> RenderFlowThread::createFlowThreadStyle(RenderStyle* parentStyle)
56 {
57     RefPtr<RenderStyle> newStyle(RenderStyle::create());
58     newStyle->inheritFrom(parentStyle);
59     newStyle->setDisplay(BLOCK);
60     newStyle->setPosition(AbsolutePosition);
61     newStyle->setZIndex(0);
62     newStyle->setLeft(Length(0, Fixed));
63     newStyle->setTop(Length(0, Fixed));
64     newStyle->setWidth(Length(100, Percent));
65     newStyle->setHeight(Length(100, Percent));
66     newStyle->font().update(0);
67     
68     return newStyle.release();
69 }
70
71 void RenderFlowThread::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
72 {
73     RenderBlock::styleDidChange(diff, oldStyle);
74
75     if (oldStyle && oldStyle->writingMode() != style()->writingMode())
76         m_regionsInvalidated = true;
77 }
78
79 RenderObject* RenderFlowThread::nextRendererForNode(Node* node) const
80 {
81     FlowThreadChildList::const_iterator it = m_flowThreadChildList.begin();
82     FlowThreadChildList::const_iterator end = m_flowThreadChildList.end();
83     
84     for (; it != end; ++it) {
85         RenderObject* child = *it;
86         ASSERT(child->node());
87         unsigned short position = node->compareDocumentPosition(child->node());
88         if (position & Node::DOCUMENT_POSITION_FOLLOWING)
89             return child;
90     }
91     
92     return 0;
93 }
94
95 RenderObject* RenderFlowThread::previousRendererForNode(Node* node) const
96 {
97     if (m_flowThreadChildList.isEmpty())
98         return 0;
99     
100     FlowThreadChildList::const_iterator begin = m_flowThreadChildList.begin();
101     FlowThreadChildList::const_iterator end = m_flowThreadChildList.end();
102     FlowThreadChildList::const_iterator it = end;
103     
104     do {
105         --it;
106         RenderObject* child = *it;
107         ASSERT(child->node());
108         unsigned short position = node->compareDocumentPosition(child->node());
109         if (position & Node::DOCUMENT_POSITION_PRECEDING)
110             return child;
111     } while (it != begin);
112     
113     return 0;
114 }
115
116 void RenderFlowThread::addFlowChild(RenderObject* newChild, RenderObject* beforeChild)
117 {
118     // The child list is used to sort the flow thread's children render objects 
119     // based on their corresponding nodes DOM order. The list is needed to avoid searching the whole DOM.
120
121     // Do not add anonymous objects.
122     if (!newChild->node())
123         return;
124
125     if (beforeChild)
126         m_flowThreadChildList.insertBefore(beforeChild, newChild);
127     else
128         m_flowThreadChildList.add(newChild);
129 }
130
131 void RenderFlowThread::removeFlowChild(RenderObject* child)
132 {
133     m_flowThreadChildList.remove(child);
134 }
135
136 // Compare two regions to determine in which one the content should flow first.
137 // The function returns true if the first passed region is "less" than the second passed region.
138 // If the first region index < second region index, then the first region is "less" than the second region.
139 // If the first region index == second region index and first region appears before second region in DOM, 
140 // the first region is "less" than the second region.
141 // If the first region is "less" than the second region, the first region receives content before second region.
142 static bool compareRenderRegions(const RenderRegion* firstRegion, const RenderRegion* secondRegion)
143 {
144     ASSERT(firstRegion);
145     ASSERT(secondRegion);
146
147     // First, compare only region-index properties.
148     if (firstRegion->style()->regionIndex() != secondRegion->style()->regionIndex())
149         return (firstRegion->style()->regionIndex() < secondRegion->style()->regionIndex());
150
151     // If the regions have the same region-index, compare their position in dom.
152     ASSERT(firstRegion->node());
153     ASSERT(secondRegion->node());
154
155     unsigned short position = firstRegion->node()->compareDocumentPosition(secondRegion->node());
156     return (position & Node::DOCUMENT_POSITION_FOLLOWING);
157 }
158
159 bool RenderFlowThread::dependsOn(RenderFlowThread* otherRenderFlowThread) const
160 {
161     if (m_layoutBeforeThreadsSet.contains(otherRenderFlowThread))
162         return true;
163
164     // Recursively traverse the m_layoutBeforeThreadsSet.
165     RenderFlowThreadCountedSet::const_iterator iterator = m_layoutBeforeThreadsSet.begin();
166     RenderFlowThreadCountedSet::const_iterator end = m_layoutBeforeThreadsSet.end();
167     for (; iterator != end; ++iterator) {
168         const RenderFlowThread* beforeFlowThread = (*iterator).first;
169         if (beforeFlowThread->dependsOn(otherRenderFlowThread))
170             return true;
171     }
172
173     return false;
174 }
175
176 void RenderFlowThread::addRegionToThread(RenderRegion* renderRegion)
177 {
178     ASSERT(renderRegion);
179     if (m_regionList.isEmpty())
180         m_regionList.add(renderRegion);
181     else {
182         // Find the first region "greater" than renderRegion.
183         RenderRegionList::iterator it = m_regionList.begin();
184         while (it != m_regionList.end() && !compareRenderRegions(renderRegion, *it))
185             ++it;
186         m_regionList.insertBefore(it, renderRegion);
187     }
188
189     ASSERT(!renderRegion->isValid());
190     if (renderRegion->parentFlowThread()) {
191         if (renderRegion->parentFlowThread()->dependsOn(this)) {
192             // Register ourself to get a notification when the state changes.
193             renderRegion->parentFlowThread()->m_observerThreadsSet.add(this);
194             return;
195         }
196
197         addDependencyOnFlowThread(renderRegion->parentFlowThread());
198     }
199
200     renderRegion->setIsValid(true);
201
202     invalidateRegions();
203 }
204
205 void RenderFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
206 {
207     ASSERT(renderRegion);
208     m_regionList.remove(renderRegion);
209     if (renderRegion->parentFlowThread()) {
210         if (!renderRegion->isValid()) {
211             renderRegion->parentFlowThread()->m_observerThreadsSet.remove(this);
212             // No need to invalidate the regions rectangles. The removed region
213             // was not taken into account. Just return here.
214             return;
215         }
216         removeDependencyOnFlowThread(renderRegion->parentFlowThread());
217     }
218
219     invalidateRegions();
220 }
221
222 void RenderFlowThread::checkInvalidRegions()
223 {
224     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
225         RenderRegion* region = *iter;
226         // The only reason a region would be invalid is because it has a parent flow thread.
227         ASSERT(region->isValid() || region->parentFlowThread());
228         if (region->isValid() || region->parentFlowThread()->dependsOn(this))
229             continue;
230
231         region->parentFlowThread()->m_observerThreadsSet.remove(this);
232         addDependencyOnFlowThread(region->parentFlowThread());
233         region->setIsValid(true);
234         invalidateRegions();
235     }
236
237     if (m_observerThreadsSet.isEmpty())
238         return;
239
240     // Notify all the flow threads that were dependent on this flow.
241
242     // Create a copy of the list first. That's because observers might change the list when calling checkInvalidRegions.
243     Vector<RenderFlowThread*> observers;
244     copyToVector(m_observerThreadsSet, observers);
245
246     for (size_t i = 0; i < observers.size(); ++i) {
247         RenderFlowThread* flowThread = observers.at(i);
248         flowThread->checkInvalidRegions();
249     }
250 }
251
252 void RenderFlowThread::addDependencyOnFlowThread(RenderFlowThread* otherFlowThread)
253 {
254     std::pair<RenderFlowThreadCountedSet::iterator, bool> result = m_layoutBeforeThreadsSet.add(otherFlowThread);
255     if (result.second) {
256         // This is the first time we see this dependency. Make sure we recalculate all the dependencies.
257         view()->setIsRenderFlowThreadOrderDirty(true);
258     }
259 }
260
261 void RenderFlowThread::removeDependencyOnFlowThread(RenderFlowThread* otherFlowThread)
262 {
263     bool removed = m_layoutBeforeThreadsSet.remove(otherFlowThread);
264     if (removed) {
265         checkInvalidRegions();
266         view()->setIsRenderFlowThreadOrderDirty(true);
267     }
268 }
269
270 void RenderFlowThread::pushDependencies(RenderFlowThreadList& list)
271 {
272     for (RenderFlowThreadCountedSet::iterator iter = m_layoutBeforeThreadsSet.begin(); iter != m_layoutBeforeThreadsSet.end(); ++iter) {
273         RenderFlowThread* flowThread = (*iter).first;
274         if (list.contains(flowThread))
275             continue;
276         flowThread->pushDependencies(list);
277         list.add(flowThread);
278     }
279 }
280
281 class CurrentRenderFlowThreadMaintainer {
282     WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadMaintainer);
283 public:
284     CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread)
285         : m_renderFlowThread(renderFlowThread)
286     {
287         RenderView* view = m_renderFlowThread->view();
288         ASSERT(!view->currentRenderFlowThread());
289         view->setCurrentRenderFlowThread(m_renderFlowThread);
290     }
291     ~CurrentRenderFlowThreadMaintainer()
292     {
293         RenderView* view = m_renderFlowThread->view();
294         ASSERT(view->currentRenderFlowThread() == m_renderFlowThread);
295         view->setCurrentRenderFlowThread(0);
296     }
297 private:
298     RenderFlowThread* m_renderFlowThread;
299 };
300
301 void RenderFlowThread::layout()
302 {
303     CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
304
305     if (m_regionsInvalidated) {
306         m_regionsInvalidated = false;
307         if (hasRegions()) {
308             int logicalHeight = 0;
309             for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
310                 RenderRegion* region = *iter;
311
312                 if (!region->isValid())
313                     continue;
314
315                 ASSERT(!region->needsLayout());
316                 
317                 m_hasValidRegions = true;
318
319                 IntRect regionRect;
320                 if (isHorizontalWritingMode()) {
321                     regionRect = IntRect(0, logicalHeight, region->contentWidth(), region->contentHeight());
322                     logicalHeight += regionRect.height();
323                 } else {
324                     regionRect = IntRect(logicalHeight, 0, region->contentWidth(), region->contentHeight());
325                     logicalHeight += regionRect.width();
326                 }
327
328                 region->setRegionRect(regionRect);
329             }
330         }
331     }
332
333     RenderBlock::layout();
334 }
335
336 void RenderFlowThread::computeLogicalWidth()
337 {
338     int logicalWidth = 0;
339
340     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
341         RenderRegion* region = *iter;
342         if (!region->isValid())
343             continue;
344         ASSERT(!region->needsLayout());
345         logicalWidth = max(isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight(), logicalWidth);
346     }
347
348     setLogicalWidth(logicalWidth);
349 }
350
351 void RenderFlowThread::computeLogicalHeight()
352 {
353     int logicalHeight = 0;
354
355     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
356         RenderRegion* region = *iter;
357         if (!region->isValid())
358             continue;
359         ASSERT(!region->needsLayout());
360         logicalHeight += isHorizontalWritingMode() ? region->contentHeight() : region->contentWidth();
361     }
362
363     setLogicalHeight(logicalHeight);
364 }
365
366 void RenderFlowThread::paintIntoRegion(PaintInfo& paintInfo, const LayoutRect& regionRect, const LayoutPoint& paintOffset)
367 {
368     GraphicsContext* context = paintInfo.context;
369     if (!context)
370         return;
371
372     // Adjust the clipping rect for the region.
373     // paintOffset contains the offset where the painting should occur
374     // adjusted with the region padding and border.
375     LayoutRect regionClippingRect(paintOffset, regionRect.size());
376
377     PaintInfo info(paintInfo);
378     info.rect.intersect(regionClippingRect);
379
380     if (!info.rect.isEmpty()) {
381         context->save();
382
383         context->clip(regionClippingRect);
384
385         // RenderFlowThread should start painting its content in a position that is offset
386         // from the region rect's current position. The amount of offset is equal to the location of
387         // region in flow coordinates.
388         LayoutPoint renderFlowThreadOffset;
389         if (style()->isFlippedBlocksWritingMode()) {
390             LayoutRect flippedRegionRect(regionRect);
391             flipForWritingMode(flippedRegionRect);
392             renderFlowThreadOffset = LayoutPoint(regionClippingRect.location() - flippedRegionRect.location());
393         } else
394             renderFlowThreadOffset = LayoutPoint(regionClippingRect.location() - regionRect.location());
395
396         context->translate(renderFlowThreadOffset.x(), renderFlowThreadOffset.y());
397         info.rect.moveBy(-renderFlowThreadOffset);
398         
399         layer()->paint(context, info.rect);
400
401         context->restore();
402     }
403 }
404
405 bool RenderFlowThread::hitTestRegion(const LayoutRect& regionRect, const HitTestRequest& request, HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
406 {
407     LayoutRect regionClippingRect(accumulatedOffset, regionRect.size());
408     if (!regionClippingRect.contains(pointInContainer))
409         return false;
410     
411     LayoutPoint renderFlowThreadOffset;
412     if (style()->isFlippedBlocksWritingMode()) {
413         LayoutRect flippedRegionRect(regionRect);
414         flipForWritingMode(flippedRegionRect);
415         renderFlowThreadOffset = LayoutPoint(regionClippingRect.location() - flippedRegionRect.location());
416     } else
417         renderFlowThreadOffset = LayoutPoint(regionClippingRect.location() - regionRect.location());
418
419     LayoutPoint transformedPoint(pointInContainer.x() - renderFlowThreadOffset.x(), pointInContainer.y() - renderFlowThreadOffset.y());
420     
421     // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView.
422     HitTestRequest newRequest(request.type() & HitTestRequest::IgnoreClipping);
423
424     LayoutPoint oldPoint = result.point();
425     result.setPoint(transformedPoint);
426     bool isPointInsideFlowThread = layer()->hitTest(newRequest, result);
427     result.setPoint(oldPoint);
428     
429     // FIXME: Should we set result.m_localPoint back to the RenderRegion's coordinate space or leave it in the RenderFlowThread's coordinate
430     // space? Right now it's staying in the RenderFlowThread's coordinate space, which may end up being ok. We will know more when we get around to
431     // patching positionForPoint.
432     return isPointInsideFlowThread;
433 }
434
435 bool RenderFlowThread::shouldRepaint(const LayoutRect& r) const
436 {
437     if (view()->printing() || r.isEmpty())
438         return false;
439
440     return true;
441 }
442
443 void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect, bool immediate)
444 {
445     if (!shouldRepaint(repaintRect))
446         return;
447
448     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
449         RenderRegion* region = *iter;
450         if (!region->isValid())
451             continue;
452
453         // We only have to issue a repaint in this region if the region rect intersects the repaint rect.
454         LayoutRect flippedRegionRect(region->regionRect());
455         flipForWritingMode(flippedRegionRect); // Put the region rect into physical coordinates.
456         
457         IntRect clippedRect(flippedRegionRect);
458         clippedRect.intersect(repaintRect);
459         if (clippedRect.isEmpty())
460             continue;
461         
462         // Put the region rect into the region's physical coordinate space.
463         clippedRect.setLocation(region->contentBoxRect().location() + (repaintRect.location() - flippedRegionRect.location()));
464         
465         // Now switch to the region's writing mode coordinate space and let it repaint itself.
466         region->flipForWritingMode(clippedRect);
467         region->repaintRectangle(clippedRect, immediate);
468     }
469 }
470
471 RenderRegion* RenderFlowThread::renderRegionForLine(LayoutUnit position, bool extendLastRegion) const
472 {
473     ASSERT(!m_regionsInvalidated);
474     
475     // If no region matches the position and extendLastRegion is true, it will return
476     // the last valid region. It is similar to auto extending the size of the last region. 
477     RenderRegion* lastValidRegion = 0;
478     
479     // FIXME: The regions are always in order, optimize this search.
480     bool useHorizontalWritingMode = isHorizontalWritingMode();
481     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
482         RenderRegion* region = *iter;
483         if (!region->isValid())
484             continue;
485
486         if (position <= 0)
487             return region;
488
489         LayoutRect regionRect = region->regionRect();
490
491         if (useHorizontalWritingMode) {
492             if (regionRect.y() <= position && position < regionRect.maxY())
493                 return region;
494             continue;
495         }
496
497         if (regionRect.x() <= position && position < regionRect.maxX())
498             return region;
499         
500         if (extendLastRegion)
501             lastValidRegion = region;
502     }
503
504     return lastValidRegion;
505 }
506
507 LayoutUnit RenderFlowThread::regionLogicalWidthForLine(LayoutUnit position) const
508 {
509     const bool extendLastRegion = true;
510     RenderRegion* region = renderRegionForLine(position, extendLastRegion);
511     if (!region)
512         return 0;
513
514     return isHorizontalWritingMode() ? region->regionRect().width() : region->regionRect().height();
515 }
516
517
518 RenderRegion* RenderFlowThread::mapFromFlowToRegion(TransformState& transformState) const
519 {
520     if (!hasValidRegions())
521         return 0;
522
523     LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox();
524     flipForWritingMode(boxRect);
525
526     // FIXME: We need to refactor RenderObject::absoluteQuads to be able to split the quads across regions,
527     // for now we just take the center of the mapped enclosing box and map it to a region.
528     // Note: Using the center in order to avoid rounding errors.
529
530     const bool extendLastRegion = true;
531     LayoutPoint center = boxRect.center();
532     RenderRegion* renderRegion = renderRegionForLine(isHorizontalWritingMode() ? center.y() : center.x(), extendLastRegion);
533     if (!renderRegion)
534         return 0;
535
536     LayoutRect flippedRegionRect(renderRegion->regionRect());
537     flipForWritingMode(flippedRegionRect);
538
539     transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location());
540
541     return renderRegion;
542 }
543
544 } // namespace WebCore