[CSS Regions]Add helper class for flow threads info in RenderView
[WebKit-https.git] / Source / WebCore / rendering / RenderNamedFlowThread.cpp
1 /*
2  * Copyright (C) 2012 Apple Inc.  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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS IN..0TERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "RenderNamedFlowThread.h"
28
29 #include "FlowThreadController.h"
30 #include "RenderRegion.h"
31 #include "RenderView.h"
32 #include "WebKitNamedFlow.h"
33
34 namespace WebCore {
35
36 RenderNamedFlowThread::RenderNamedFlowThread(Node* node, const AtomicString& name)
37     : RenderFlowThread(node)
38     , m_flowThreadName(name)
39 {
40 }
41
42 const char* RenderNamedFlowThread::renderName() const
43 {    
44     return "RenderNamedFlowThread";
45 }
46
47
48 RenderObject* RenderNamedFlowThread::nextRendererForNode(Node* node) const
49 {
50     FlowThreadChildList::const_iterator it = m_flowThreadChildList.begin();
51     FlowThreadChildList::const_iterator end = m_flowThreadChildList.end();
52
53     for (; it != end; ++it) {
54         RenderObject* child = *it;
55         ASSERT(child->node());
56         unsigned short position = node->compareDocumentPosition(child->node());
57         if (position & Node::DOCUMENT_POSITION_FOLLOWING)
58             return child;
59     }
60
61     return 0;
62 }
63
64 RenderObject* RenderNamedFlowThread::previousRendererForNode(Node* node) const
65 {
66     if (m_flowThreadChildList.isEmpty())
67         return 0;
68
69     FlowThreadChildList::const_iterator begin = m_flowThreadChildList.begin();
70     FlowThreadChildList::const_iterator end = m_flowThreadChildList.end();
71     FlowThreadChildList::const_iterator it = end;
72
73     do {
74         --it;
75         RenderObject* child = *it;
76         ASSERT(child->node());
77         unsigned short position = node->compareDocumentPosition(child->node());
78         if (position & Node::DOCUMENT_POSITION_PRECEDING)
79             return child;
80     } while (it != begin);
81
82     return 0;
83 }
84
85 void RenderNamedFlowThread::addFlowChild(RenderObject* newChild, RenderObject* beforeChild)
86 {
87     // The child list is used to sort the flow thread's children render objects 
88     // based on their corresponding nodes DOM order. The list is needed to avoid searching the whole DOM.
89
90     // Do not add anonymous objects.
91     if (!newChild->node())
92         return;
93
94     if (beforeChild)
95         m_flowThreadChildList.insertBefore(beforeChild, newChild);
96     else
97         m_flowThreadChildList.add(newChild);
98 }
99
100 void RenderNamedFlowThread::removeFlowChild(RenderObject* child)
101 {
102     m_flowThreadChildList.remove(child);
103 }
104
105 bool RenderNamedFlowThread::dependsOn(RenderNamedFlowThread* otherRenderFlowThread) const
106 {
107     if (m_layoutBeforeThreadsSet.contains(otherRenderFlowThread))
108         return true;
109
110     // Recursively traverse the m_layoutBeforeThreadsSet.
111     RenderNamedFlowThreadCountedSet::const_iterator iterator = m_layoutBeforeThreadsSet.begin();
112     RenderNamedFlowThreadCountedSet::const_iterator end = m_layoutBeforeThreadsSet.end();
113     for (; iterator != end; ++iterator) {
114         const RenderNamedFlowThread* beforeFlowThread = (*iterator).first;
115         if (beforeFlowThread->dependsOn(otherRenderFlowThread))
116             return true;
117     }
118
119     return false;
120 }
121
122 // Compare two regions to determine in which one the content should flow first.
123 // The function returns true if the first passed region is "less" than the second passed region.
124 // If the first region appears before second region in DOM,
125 // the first region is "less" than the second region.
126 // If the first region is "less" than the second region, the first region receives content before second region.
127 static bool compareRenderRegions(const RenderRegion* firstRegion, const RenderRegion* secondRegion)
128 {
129     ASSERT(firstRegion);
130     ASSERT(secondRegion);
131
132     // If the regions have the same region-index, compare their position in dom.
133     ASSERT(firstRegion->node());
134     ASSERT(secondRegion->node());
135
136     unsigned short position = firstRegion->node()->compareDocumentPosition(secondRegion->node());
137     return (position & Node::DOCUMENT_POSITION_FOLLOWING);
138 }
139
140 void RenderNamedFlowThread::addRegionToThread(RenderRegion* renderRegion)
141 {
142     ASSERT(renderRegion);
143     if (m_regionList.isEmpty())
144         m_regionList.add(renderRegion);
145     else {
146         // Find the first region "greater" than renderRegion.
147         RenderRegionList::iterator it = m_regionList.begin();
148         while (it != m_regionList.end() && !compareRenderRegions(renderRegion, *it))
149             ++it;
150         m_regionList.insertBefore(it, renderRegion);
151     }
152
153     ASSERT(!renderRegion->isValid());
154     if (renderRegion->parentNamedFlowThread()) {
155         if (renderRegion->parentNamedFlowThread()->dependsOn(this)) {
156             // Register ourself to get a notification when the state changes.
157             renderRegion->parentNamedFlowThread()->m_observerThreadsSet.add(this);
158             return;
159         }
160
161         addDependencyOnFlowThread(renderRegion->parentNamedFlowThread());
162     }
163
164     renderRegion->setIsValid(true);
165
166     invalidateRegions();
167 }
168
169 void RenderNamedFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
170 {
171     ASSERT(renderRegion);
172     m_regionRangeMap.clear();
173     m_regionList.remove(renderRegion);
174
175     if (renderRegion->parentNamedFlowThread()) {
176         if (!renderRegion->isValid()) {
177             renderRegion->parentNamedFlowThread()->m_observerThreadsSet.remove(this);
178             // No need to invalidate the regions rectangles. The removed region
179             // was not taken into account. Just return here.
180             return;
181         }
182         removeDependencyOnFlowThread(renderRegion->parentNamedFlowThread());
183     }
184
185     invalidateRegions();
186 }
187
188
189 void RenderNamedFlowThread::checkInvalidRegions()
190 {
191     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
192         RenderRegion* region = *iter;
193         // The only reason a region would be invalid is because it has a parent flow thread.
194         ASSERT(region->isValid() || region->parentNamedFlowThread());
195         if (region->isValid() || region->parentNamedFlowThread()->dependsOn(this))
196             continue;
197
198         region->parentNamedFlowThread()->m_observerThreadsSet.remove(this);
199         addDependencyOnFlowThread(region->parentNamedFlowThread());
200         region->setIsValid(true);
201         invalidateRegions();
202     }
203
204     if (m_observerThreadsSet.isEmpty())
205         return;
206
207     // Notify all the flow threads that were dependent on this flow.
208
209     // Create a copy of the list first. That's because observers might change the list when calling checkInvalidRegions.
210     Vector<RenderNamedFlowThread*> observers;
211     copyToVector(m_observerThreadsSet, observers);
212
213     for (size_t i = 0; i < observers.size(); ++i) {
214         RenderNamedFlowThread* flowThread = observers.at(i);
215         flowThread->checkInvalidRegions();
216     }
217 }
218
219 void RenderNamedFlowThread::addDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread)
220 {
221     RenderNamedFlowThreadCountedSet::AddResult result = m_layoutBeforeThreadsSet.add(otherFlowThread);
222     if (result.isNewEntry) {
223         // This is the first time we see this dependency. Make sure we recalculate all the dependencies.
224         view()->flowThreadController()->setIsRenderNamedFlowThreadOrderDirty(true);
225     }
226 }
227
228 void RenderNamedFlowThread::removeDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread)
229 {
230     bool removed = m_layoutBeforeThreadsSet.remove(otherFlowThread);
231     if (removed) {
232         checkInvalidRegions();
233         view()->flowThreadController()->setIsRenderNamedFlowThreadOrderDirty(true);
234     }
235 }
236
237 void RenderNamedFlowThread::pushDependencies(RenderNamedFlowThreadList& list)
238 {
239     for (RenderNamedFlowThreadCountedSet::iterator iter = m_layoutBeforeThreadsSet.begin(); iter != m_layoutBeforeThreadsSet.end(); ++iter) {
240         RenderNamedFlowThread* flowThread = (*iter).first;
241         if (list.contains(flowThread))
242             continue;
243         flowThread->pushDependencies(list);
244         list.add(flowThread);
245     }
246 }
247
248 WebKitNamedFlow* RenderNamedFlowThread::ensureNamedFlow()
249 {
250     if (!m_namedFlow)
251         m_namedFlow = WebKitNamedFlow::create(this);
252
253     return m_namedFlow.get();
254 }
255
256 }