• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "core/rendering/RenderNamedFlowThread.h"
28 
29 #include "RuntimeEnabledFeatures.h"
30 #include "bindings/v8/ExceptionStatePlaceholder.h"
31 #include "core/dom/NamedFlow.h"
32 #include "core/dom/NodeRenderingTraversal.h"
33 #include "core/dom/NodeTraversal.h"
34 #include "core/dom/Position.h"
35 #include "core/dom/Range.h"
36 #include "core/dom/Text.h"
37 #include "core/inspector/InspectorInstrumentation.h"
38 #include "core/rendering/FlowThreadController.h"
39 #include "core/rendering/InlineTextBox.h"
40 #include "core/rendering/RenderInline.h"
41 #include "core/rendering/RenderRegion.h"
42 #include "core/rendering/RenderText.h"
43 #include "core/rendering/RenderView.h"
44 
45 namespace WebCore {
46 
createAnonymous(Document * document,PassRefPtr<NamedFlow> namedFlow)47 RenderNamedFlowThread* RenderNamedFlowThread::createAnonymous(Document* document, PassRefPtr<NamedFlow> namedFlow)
48 {
49     ASSERT(RuntimeEnabledFeatures::cssRegionsEnabled());
50     RenderNamedFlowThread* renderer = new RenderNamedFlowThread(namedFlow);
51     renderer->setDocumentForAnonymous(document);
52     return renderer;
53 }
54 
RenderNamedFlowThread(PassRefPtr<NamedFlow> namedFlow)55 RenderNamedFlowThread::RenderNamedFlowThread(PassRefPtr<NamedFlow> namedFlow)
56     : m_overset(true)
57     , m_namedFlow(namedFlow)
58     , m_regionLayoutUpdateEventTimer(this, &RenderNamedFlowThread::regionLayoutUpdateEventTimerFired)
59     , m_regionOversetChangeEventTimer(this, &RenderNamedFlowThread::regionOversetChangeEventTimerFired)
60 {
61 }
62 
~RenderNamedFlowThread()63 RenderNamedFlowThread::~RenderNamedFlowThread()
64 {
65     // The flow thread can be destroyed without unregistering the content nodes if the document is destroyed.
66     // This can lead to problems because the nodes are still marked as belonging to a flow thread.
67     clearContentNodes();
68 
69     // Also leave the NamedFlow object in a consistent state by calling mark for destruction.
70     setMarkForDestruction();
71 }
72 
renderName() const73 const char* RenderNamedFlowThread::renderName() const
74 {
75     return "RenderNamedFlowThread";
76 }
77 
clearContentNodes()78 void RenderNamedFlowThread::clearContentNodes()
79 {
80     for (NamedFlowContentNodes::iterator it = m_contentNodes.begin(); it != m_contentNodes.end(); ++it) {
81         Node* contentNode = *it;
82 
83         ASSERT(contentNode && contentNode->isElementNode());
84         ASSERT(contentNode->inNamedFlow());
85         ASSERT(contentNode->document() == document());
86 
87         contentNode->clearInNamedFlow();
88     }
89 
90     m_contentNodes.clear();
91 }
92 
updateWritingMode()93 void RenderNamedFlowThread::updateWritingMode()
94 {
95     RenderRegion* firstRegion = m_regionList.first();
96     if (!firstRegion)
97         return;
98     if (style()->writingMode() == firstRegion->style()->writingMode())
99         return;
100 
101     // The first region defines the principal writing mode for the entire flow.
102     RefPtr<RenderStyle> newStyle = RenderStyle::clone(style());
103     newStyle->setWritingMode(firstRegion->style()->writingMode());
104     setStyle(newStyle.release());
105 }
106 
nextRendererForNode(Node * node) const107 RenderObject* RenderNamedFlowThread::nextRendererForNode(Node* node) const
108 {
109     FlowThreadChildList::const_iterator it = m_flowThreadChildList.begin();
110     FlowThreadChildList::const_iterator end = m_flowThreadChildList.end();
111 
112     for (; it != end; ++it) {
113         RenderObject* child = *it;
114         ASSERT(child->node());
115         unsigned short position = node->compareDocumentPosition(child->node());
116         if (position & Node::DOCUMENT_POSITION_FOLLOWING)
117             return child;
118     }
119 
120     return 0;
121 }
122 
previousRendererForNode(Node * node) const123 RenderObject* RenderNamedFlowThread::previousRendererForNode(Node* node) const
124 {
125     if (m_flowThreadChildList.isEmpty())
126         return 0;
127 
128     FlowThreadChildList::const_iterator begin = m_flowThreadChildList.begin();
129     FlowThreadChildList::const_iterator end = m_flowThreadChildList.end();
130     FlowThreadChildList::const_iterator it = end;
131 
132     do {
133         --it;
134         RenderObject* child = *it;
135         ASSERT(child->node());
136         unsigned short position = node->compareDocumentPosition(child->node());
137         if (position & Node::DOCUMENT_POSITION_PRECEDING)
138             return child;
139     } while (it != begin);
140 
141     return 0;
142 }
143 
addFlowChild(RenderObject * newChild)144 void RenderNamedFlowThread::addFlowChild(RenderObject* newChild)
145 {
146     // The child list is used to sort the flow thread's children render objects
147     // based on their corresponding nodes DOM order. The list is needed to avoid searching the whole DOM.
148 
149     Node* childNode = newChild->node();
150 
151     // Do not add anonymous objects.
152     if (!childNode)
153         return;
154 
155     ASSERT(childNode->isElementNode());
156 
157     RenderObject* beforeChild = nextRendererForNode(childNode);
158     if (beforeChild)
159         m_flowThreadChildList.insertBefore(beforeChild, newChild);
160     else
161         m_flowThreadChildList.add(newChild);
162 }
163 
removeFlowChild(RenderObject * child)164 void RenderNamedFlowThread::removeFlowChild(RenderObject* child)
165 {
166     m_flowThreadChildList.remove(child);
167 }
168 
dependsOn(RenderNamedFlowThread * otherRenderFlowThread) const169 bool RenderNamedFlowThread::dependsOn(RenderNamedFlowThread* otherRenderFlowThread) const
170 {
171     if (m_layoutBeforeThreadsSet.contains(otherRenderFlowThread))
172         return true;
173 
174     // Recursively traverse the m_layoutBeforeThreadsSet.
175     RenderNamedFlowThreadCountedSet::const_iterator iterator = m_layoutBeforeThreadsSet.begin();
176     RenderNamedFlowThreadCountedSet::const_iterator end = m_layoutBeforeThreadsSet.end();
177     for (; iterator != end; ++iterator) {
178         const RenderNamedFlowThread* beforeFlowThread = (*iterator).key;
179         if (beforeFlowThread->dependsOn(otherRenderFlowThread))
180             return true;
181     }
182 
183     return false;
184 }
185 
186 // Compare two regions to determine in which one the content should flow first.
187 // The function returns true if the first passed region is "less" than the second passed region.
188 // If the first region appears before second region in DOM,
189 // the first region is "less" than the second region.
190 // If the first region is "less" than the second region, the first region receives content before second region.
compareRenderRegions(const RenderRegion * firstRegion,const RenderRegion * secondRegion)191 static bool compareRenderRegions(const RenderRegion* firstRegion, const RenderRegion* secondRegion)
192 {
193     ASSERT(firstRegion);
194     ASSERT(secondRegion);
195 
196     ASSERT(firstRegion->generatingNodeForRegion());
197     ASSERT(secondRegion->generatingNodeForRegion());
198 
199     // If the regions belong to different nodes, compare their position in the DOM.
200     if (firstRegion->generatingNodeForRegion() != secondRegion->generatingNodeForRegion()) {
201         unsigned short position = firstRegion->generatingNodeForRegion()->compareDocumentPosition(secondRegion->generatingNodeForRegion());
202 
203         // If the second region is contained in the first one, the first region is "less" if it's :before.
204         if (position & Node::DOCUMENT_POSITION_CONTAINED_BY) {
205             ASSERT(secondRegion->style()->styleType() == NOPSEUDO);
206             return firstRegion->style()->styleType() == BEFORE;
207         }
208 
209         // If the second region contains the first region, the first region is "less" if the second is :after.
210         if (position & Node::DOCUMENT_POSITION_CONTAINS) {
211             ASSERT(firstRegion->style()->styleType() == NOPSEUDO);
212             return secondRegion->style()->styleType() == AFTER;
213         }
214 
215         return (position & Node::DOCUMENT_POSITION_FOLLOWING);
216     }
217 
218     // FIXME: Currently it's not possible for an element to be both a region and have pseudo-children. The case is covered anyway.
219     switch (firstRegion->style()->styleType()) {
220     case BEFORE:
221         // The second region can be the node or the after pseudo-element (before is smaller than any of those).
222         return true;
223     case AFTER:
224         // The second region can be the node or the before pseudo-element (after is greater than any of those).
225         return false;
226     case NOPSEUDO:
227         // The second region can either be the before or the after pseudo-element (the node is only smaller than the after pseudo-element).
228         return firstRegion->style()->styleType() == AFTER;
229     default:
230         break;
231     }
232 
233     ASSERT_NOT_REACHED();
234     return true;
235 }
236 
237 // This helper function adds a region to a list preserving the order property of the list.
addRegionToList(RenderRegionList & regionList,RenderRegion * renderRegion)238 static void addRegionToList(RenderRegionList& regionList, RenderRegion* renderRegion)
239 {
240     if (regionList.isEmpty()) {
241         regionList.add(renderRegion);
242     } else {
243         // Find the first region "greater" than renderRegion.
244         RenderRegionList::iterator it = regionList.begin();
245         while (it != regionList.end() && !compareRenderRegions(renderRegion, *it))
246             ++it;
247         regionList.insertBefore(it, renderRegion);
248     }
249 }
250 
addRegionToNamedFlowThread(RenderRegion * renderRegion)251 void RenderNamedFlowThread::addRegionToNamedFlowThread(RenderRegion* renderRegion)
252 {
253     ASSERT(renderRegion);
254     ASSERT(!renderRegion->isValid());
255 
256     if (renderRegion->parentNamedFlowThread())
257         addDependencyOnFlowThread(renderRegion->parentNamedFlowThread());
258 
259     renderRegion->setIsValid(true);
260     addRegionToList(m_regionList, renderRegion);
261 
262     if (m_regionList.first() == renderRegion)
263         updateWritingMode();
264 }
265 
addRegionToThread(RenderRegion * renderRegion)266 void RenderNamedFlowThread::addRegionToThread(RenderRegion* renderRegion)
267 {
268     ASSERT(renderRegion);
269     ASSERT(!renderRegion->isValid());
270 
271     resetMarkForDestruction();
272 
273     if (renderRegion->parentNamedFlowThread() && renderRegion->parentNamedFlowThread()->dependsOn(this)) {
274         // The order of invalid regions is irrelevant.
275         m_invalidRegionList.add(renderRegion);
276         // Register ourself to get a notification when the state changes.
277         renderRegion->parentNamedFlowThread()->m_observerThreadsSet.add(this);
278         return;
279     }
280 
281     addRegionToNamedFlowThread(renderRegion);
282 
283     invalidateRegions();
284 }
285 
removeRegionFromThread(RenderRegion * renderRegion)286 void RenderNamedFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
287 {
288     ASSERT(renderRegion);
289 
290     if (renderRegion->parentNamedFlowThread()) {
291         if (!renderRegion->isValid()) {
292             ASSERT(m_invalidRegionList.contains(renderRegion));
293             m_invalidRegionList.remove(renderRegion);
294             renderRegion->parentNamedFlowThread()->m_observerThreadsSet.remove(this);
295             // No need to invalidate the regions rectangles. The removed region
296             // was not taken into account. Just return here.
297             return;
298         }
299         removeDependencyOnFlowThread(renderRegion->parentNamedFlowThread());
300     }
301 
302     ASSERT(m_regionList.contains(renderRegion));
303     bool wasFirst = m_regionList.first() == renderRegion;
304     m_regionList.remove(renderRegion);
305 
306     if (canBeDestroyed())
307         setMarkForDestruction();
308 
309     // After removing all the regions in the flow the following layout needs to dispatch the regionLayoutUpdate event
310     if (m_regionList.isEmpty())
311         setDispatchRegionLayoutUpdateEvent(true);
312     else if (wasFirst)
313         updateWritingMode();
314 
315     invalidateRegions();
316 }
317 
regionChangedWritingMode(RenderRegion * region)318 void RenderNamedFlowThread::regionChangedWritingMode(RenderRegion* region)
319 {
320     if (m_regionList.first() == region)
321         updateWritingMode();
322 }
323 
computeOversetStateForRegions(LayoutUnit oldClientAfterEdge)324 void RenderNamedFlowThread::computeOversetStateForRegions(LayoutUnit oldClientAfterEdge)
325 {
326     LayoutUnit height = oldClientAfterEdge;
327 
328     // FIXME: the visual overflow of middle region (if it is the last one to contain any content in a render flow thread)
329     // might not be taken into account because the render flow thread height is greater that that regions height + its visual overflow
330     // because of how computeLogicalHeight is implemented for RenderFlowThread (as a sum of all regions height).
331     // This means that the middle region will be marked as fit (even if it has visual overflow flowing into the next region)
332     if (hasRenderOverflow()
333         && ( (isHorizontalWritingMode() && visualOverflowRect().maxY() > clientBoxRect().maxY())
334             || (!isHorizontalWritingMode() && visualOverflowRect().maxX() > clientBoxRect().maxX())))
335         height = isHorizontalWritingMode() ? visualOverflowRect().maxY() : visualOverflowRect().maxX();
336 
337     RenderRegion* lastReg = lastRegion();
338     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
339         RenderRegion* region = *iter;
340         LayoutUnit flowMin = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().y() : region->flowThreadPortionRect().x());
341         LayoutUnit flowMax = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().maxY() : region->flowThreadPortionRect().maxX());
342         RegionOversetState previousState = region->regionOversetState();
343         RegionOversetState state = RegionFit;
344         if (flowMin <= 0)
345             state = RegionEmpty;
346         if (flowMax > 0 && region == lastReg)
347             state = RegionOverset;
348         region->setRegionOversetState(state);
349         // determine whether the NamedFlow object should dispatch a regionLayoutUpdate event
350         // FIXME: currently it cannot determine whether a region whose regionOverset state remained either "fit" or "overset" has actually
351         // changed, so it just assumes that the NamedFlow should dispatch the event
352         if (previousState != state
353             || state == RegionFit
354             || state == RegionOverset)
355             setDispatchRegionLayoutUpdateEvent(true);
356 
357         if (previousState != state)
358             setDispatchRegionOversetChangeEvent(true);
359     }
360 
361     // If the number of regions has changed since we last computed the overset property, schedule the regionOversetChange event.
362     if (previousRegionCountChanged()) {
363         setDispatchRegionOversetChangeEvent(true);
364         updatePreviousRegionCount();
365     }
366 
367     // With the regions overflow state computed we can also set the overset flag for the named flow.
368     // If there are no valid regions in the chain, overset is true.
369     m_overset = lastReg ? lastReg->regionOversetState() == RegionOverset : true;
370 }
371 
checkInvalidRegions()372 void RenderNamedFlowThread::checkInvalidRegions()
373 {
374     Vector<RenderRegion*> newValidRegions;
375     for (RenderRegionList::iterator iter = m_invalidRegionList.begin(); iter != m_invalidRegionList.end(); ++iter) {
376         RenderRegion* region = *iter;
377         // The only reason a region would be invalid is because it has a parent flow thread.
378         ASSERT(!region->isValid() && region->parentNamedFlowThread());
379         if (region->parentNamedFlowThread()->dependsOn(this))
380             continue;
381 
382         newValidRegions.append(region);
383     }
384 
385     for (Vector<RenderRegion*>::iterator iter = newValidRegions.begin(); iter != newValidRegions.end(); ++iter) {
386         RenderRegion* region = *iter;
387         m_invalidRegionList.remove(region);
388         region->parentNamedFlowThread()->m_observerThreadsSet.remove(this);
389         addRegionToNamedFlowThread(region);
390     }
391 
392     if (!newValidRegions.isEmpty())
393         invalidateRegions();
394 
395     if (m_observerThreadsSet.isEmpty())
396         return;
397 
398     // Notify all the flow threads that were dependent on this flow.
399 
400     // Create a copy of the list first. That's because observers might change the list when calling checkInvalidRegions.
401     Vector<RenderNamedFlowThread*> observers;
402     copyToVector(m_observerThreadsSet, observers);
403 
404     for (size_t i = 0; i < observers.size(); ++i) {
405         RenderNamedFlowThread* flowThread = observers.at(i);
406         flowThread->checkInvalidRegions();
407     }
408 }
409 
addDependencyOnFlowThread(RenderNamedFlowThread * otherFlowThread)410 void RenderNamedFlowThread::addDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread)
411 {
412     RenderNamedFlowThreadCountedSet::AddResult result = m_layoutBeforeThreadsSet.add(otherFlowThread);
413     if (result.isNewEntry) {
414         // This is the first time we see this dependency. Make sure we recalculate all the dependencies.
415         view()->flowThreadController()->setIsRenderNamedFlowThreadOrderDirty(true);
416     }
417 }
418 
removeDependencyOnFlowThread(RenderNamedFlowThread * otherFlowThread)419 void RenderNamedFlowThread::removeDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread)
420 {
421     bool removed = m_layoutBeforeThreadsSet.remove(otherFlowThread);
422     if (removed) {
423         checkInvalidRegions();
424         view()->flowThreadController()->setIsRenderNamedFlowThreadOrderDirty(true);
425     }
426 }
427 
pushDependencies(RenderNamedFlowThreadList & list)428 void RenderNamedFlowThread::pushDependencies(RenderNamedFlowThreadList& list)
429 {
430     for (RenderNamedFlowThreadCountedSet::iterator iter = m_layoutBeforeThreadsSet.begin(); iter != m_layoutBeforeThreadsSet.end(); ++iter) {
431         RenderNamedFlowThread* flowThread = (*iter).key;
432         if (list.contains(flowThread))
433             continue;
434         flowThread->pushDependencies(list);
435         list.add(flowThread);
436     }
437 }
438 
439 // The content nodes list contains those nodes with -webkit-flow-into: flow.
440 // An element with display:none should also be listed among those nodes.
441 // The list of nodes is ordered.
registerNamedFlowContentNode(Node * contentNode)442 void RenderNamedFlowThread::registerNamedFlowContentNode(Node* contentNode)
443 {
444     ASSERT(contentNode && contentNode->isElementNode());
445     ASSERT(contentNode->document() == document());
446 
447     contentNode->setInNamedFlow();
448 
449     resetMarkForDestruction();
450 
451     // Find the first content node following the new content node.
452     for (NamedFlowContentNodes::iterator it = m_contentNodes.begin(); it != m_contentNodes.end(); ++it) {
453         Node* node = *it;
454         unsigned short position = contentNode->compareDocumentPosition(node);
455         if (position & Node::DOCUMENT_POSITION_FOLLOWING) {
456             m_contentNodes.insertBefore(node, contentNode);
457             return;
458         }
459     }
460     m_contentNodes.add(contentNode);
461 }
462 
unregisterNamedFlowContentNode(Node * contentNode)463 void RenderNamedFlowThread::unregisterNamedFlowContentNode(Node* contentNode)
464 {
465     ASSERT(contentNode && contentNode->isElementNode());
466     ASSERT(m_contentNodes.contains(contentNode));
467     ASSERT(contentNode->inNamedFlow());
468     ASSERT(contentNode->document() == document());
469 
470     contentNode->clearInNamedFlow();
471     m_contentNodes.remove(contentNode);
472 
473     if (canBeDestroyed())
474         setMarkForDestruction();
475 }
476 
flowThreadName() const477 const AtomicString& RenderNamedFlowThread::flowThreadName() const
478 {
479     return m_namedFlow->name();
480 }
481 
isChildAllowed(RenderObject * child,RenderStyle * style) const482 bool RenderNamedFlowThread::isChildAllowed(RenderObject* child, RenderStyle* style) const
483 {
484     if (!child->node())
485         return true;
486 
487     ASSERT(child->node()->isElementNode());
488     Node* originalParent = NodeRenderingTraversal::parent(child->node());
489     if (!originalParent || !originalParent->renderer())
490         return true;
491 
492     return originalParent->renderer()->isChildAllowed(child, style);
493 }
494 
dispatchRegionLayoutUpdateEvent()495 void RenderNamedFlowThread::dispatchRegionLayoutUpdateEvent()
496 {
497     RenderFlowThread::dispatchRegionLayoutUpdateEvent();
498     InspectorInstrumentation::didUpdateRegionLayout(&document(), m_namedFlow.get());
499 
500     if (!m_regionLayoutUpdateEventTimer.isActive() && m_namedFlow->hasEventListeners())
501         m_regionLayoutUpdateEventTimer.startOneShot(0);
502 }
503 
dispatchRegionOversetChangeEvent()504 void RenderNamedFlowThread::dispatchRegionOversetChangeEvent()
505 {
506     RenderFlowThread::dispatchRegionOversetChangeEvent();
507     InspectorInstrumentation::didChangeRegionOverset(&document(), m_namedFlow.get());
508 
509     if (!m_regionOversetChangeEventTimer.isActive() && m_namedFlow->hasEventListeners())
510         m_regionOversetChangeEventTimer.startOneShot(0);
511 }
512 
regionLayoutUpdateEventTimerFired(Timer<RenderNamedFlowThread> *)513 void RenderNamedFlowThread::regionLayoutUpdateEventTimerFired(Timer<RenderNamedFlowThread>*)
514 {
515     ASSERT(m_namedFlow);
516 
517     m_namedFlow->dispatchRegionLayoutUpdateEvent();
518 }
519 
regionOversetChangeEventTimerFired(Timer<RenderNamedFlowThread> *)520 void RenderNamedFlowThread::regionOversetChangeEventTimerFired(Timer<RenderNamedFlowThread>*)
521 {
522     ASSERT(m_namedFlow);
523 
524     m_namedFlow->dispatchRegionOversetChangeEvent();
525 }
526 
setMarkForDestruction()527 void RenderNamedFlowThread::setMarkForDestruction()
528 {
529     if (m_namedFlow->flowState() == NamedFlow::FlowStateNull)
530         return;
531 
532     m_namedFlow->setRenderer(0);
533     // After this call ends, the renderer can be safely destroyed.
534     // The NamedFlow object may outlive its renderer if it's referenced from a script and may be reatached to one if the named flow is recreated in the stylesheet.
535 }
536 
resetMarkForDestruction()537 void RenderNamedFlowThread::resetMarkForDestruction()
538 {
539     if (m_namedFlow->flowState() == NamedFlow::FlowStateCreated)
540         return;
541 
542     m_namedFlow->setRenderer(this);
543 }
544 
isMarkedForDestruction() const545 bool RenderNamedFlowThread::isMarkedForDestruction() const
546 {
547     // Flow threads in the "NULL" state can be destroyed.
548     return m_namedFlow->flowState() == NamedFlow::FlowStateNull;
549 }
550 
isContainedInNodes(Vector<Node * > others,Node * node)551 static bool isContainedInNodes(Vector<Node*> others, Node* node)
552 {
553     for (size_t i = 0; i < others.size(); i++) {
554         Node* other = others.at(i);
555         if (other->contains(node))
556             return true;
557     }
558     return false;
559 }
560 
boxIntersectsRegion(LayoutUnit logicalTopForBox,LayoutUnit logicalBottomForBox,LayoutUnit logicalTopForRegion,LayoutUnit logicalBottomForRegion)561 static bool boxIntersectsRegion(LayoutUnit logicalTopForBox, LayoutUnit logicalBottomForBox, LayoutUnit logicalTopForRegion, LayoutUnit logicalBottomForRegion)
562 {
563     bool regionIsEmpty = logicalBottomForRegion != LayoutUnit::max() && logicalTopForRegion != LayoutUnit::min()
564         && (logicalBottomForRegion - logicalTopForRegion) <= 0;
565     return  (logicalBottomForBox - logicalTopForBox) > 0
566         && !regionIsEmpty
567         && logicalTopForBox < logicalBottomForRegion && logicalTopForRegion < logicalBottomForBox;
568 }
569 
570 // Retrieve the next node to be visited while computing the ranges inside a region.
nextNodeInsideContentNode(const Node & currNode,const Node * contentNode)571 static Node* nextNodeInsideContentNode(const Node& currNode, const Node* contentNode)
572 {
573     ASSERT(contentNode && contentNode->inNamedFlow());
574 
575     if (currNode.renderer() && currNode.renderer()->isSVGRoot())
576         return NodeTraversal::nextSkippingChildren(currNode, contentNode);
577     return NodeTraversal::next(currNode, contentNode);
578 }
579 
getRanges(Vector<RefPtr<Range>> & rangeObjects,const RenderRegion * region) const580 void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range> >& rangeObjects, const RenderRegion* region) const
581 {
582     LayoutUnit logicalTopForRegion;
583     LayoutUnit logicalBottomForRegion;
584 
585     // extend the first region top to contain everything up to its logical height
586     if (region->isFirstRegion())
587         logicalTopForRegion = LayoutUnit::min();
588     else
589         logicalTopForRegion =  region->logicalTopForFlowThreadContent();
590 
591     // extend the last region to contain everything above its y()
592     if (region->isLastRegion())
593         logicalBottomForRegion = LayoutUnit::max();
594     else
595         logicalBottomForRegion = region->logicalBottomForFlowThreadContent();
596 
597     Vector<Node*> nodes;
598     // eliminate the contentNodes that are descendants of other contentNodes
599     for (NamedFlowContentNodes::const_iterator it = contentNodes().begin(); it != contentNodes().end(); ++it) {
600         Node* node = *it;
601         if (!isContainedInNodes(nodes, node))
602             nodes.append(node);
603     }
604 
605     for (size_t i = 0; i < nodes.size(); i++) {
606         Node* contentNode = nodes.at(i);
607         if (!contentNode->renderer())
608             continue;
609 
610         RefPtr<Range> range = Range::create(contentNode->document());
611         bool foundStartPosition = false;
612         bool startsAboveRegion = true;
613         bool endsBelowRegion = true;
614         bool skipOverOutsideNodes = false;
615         Node* lastEndNode = 0;
616 
617         for (Node* node = contentNode; node; node = nextNodeInsideContentNode(*node, contentNode)) {
618             RenderObject* renderer = node->renderer();
619             if (!renderer)
620                 continue;
621 
622             LayoutRect boundingBox;
623             if (renderer->isRenderInline()) {
624                 boundingBox = toRenderInline(renderer)->linesBoundingBox();
625             } else if (renderer->isText()) {
626                 boundingBox = toRenderText(renderer)->linesBoundingBox();
627             } else {
628                 boundingBox =  toRenderBox(renderer)->frameRect();
629                 if (toRenderBox(renderer)->isRelPositioned())
630                     boundingBox.move(toRenderBox(renderer)->relativePositionLogicalOffset());
631             }
632 
633             LayoutUnit offsetTop = renderer->containingBlock()->offsetFromLogicalTopOfFirstPage();
634             const LayoutPoint logicalOffsetFromTop(isHorizontalWritingMode() ? LayoutUnit() :  offsetTop,
635                 isHorizontalWritingMode() ? offsetTop : LayoutUnit());
636 
637             boundingBox.moveBy(logicalOffsetFromTop);
638 
639             LayoutUnit logicalTopForRenderer = region->logicalTopOfFlowThreadContentRect(boundingBox);
640             LayoutUnit logicalBottomForRenderer = region->logicalBottomOfFlowThreadContentRect(boundingBox);
641 
642             // if the bounding box of the current element doesn't intersect the region box
643             // close the current range only if the start element began inside the region,
644             // otherwise just move the start position after this node and keep skipping them until we found a proper start position.
645             if (!boxIntersectsRegion(logicalTopForRenderer, logicalBottomForRenderer, logicalTopForRegion, logicalBottomForRegion)) {
646                 if (foundStartPosition) {
647                     if (!startsAboveRegion) {
648                         if (range->intersectsNode(node, IGNORE_EXCEPTION))
649                             range->setEndBefore(node, IGNORE_EXCEPTION);
650                         rangeObjects.append(range->cloneRange(IGNORE_EXCEPTION));
651                         range = Range::create(contentNode->document());
652                         startsAboveRegion = true;
653                     } else {
654                         skipOverOutsideNodes = true;
655                     }
656                 }
657                 if (skipOverOutsideNodes)
658                     range->setStartAfter(node, IGNORE_EXCEPTION);
659                 foundStartPosition = false;
660                 continue;
661             }
662 
663             // start position
664             if (logicalTopForRenderer < logicalTopForRegion && startsAboveRegion) {
665                 if (renderer->isText()) { // Text crosses region top
666                     // for Text elements, just find the last textbox that is contained inside the region and use its start() offset as start position
667                     RenderText* textRenderer = toRenderText(renderer);
668                     for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
669                         if (offsetTop + box->logicalBottom() < logicalTopForRegion)
670                             continue;
671                         range->setStart(Position(toText(node), box->start()));
672                         startsAboveRegion = false;
673                         break;
674                     }
675                 } else { // node crosses region top
676                     // for all elements, except Text, just set the start position to be before their children
677                     startsAboveRegion = true;
678                     range->setStart(Position(node, Position::PositionIsBeforeChildren));
679                 }
680             } else { // node starts inside region
681                 // for elements that start inside the region, set the start position to be before them. If we found one, we will just skip the others until
682                 // the range is closed.
683                 if (startsAboveRegion) {
684                     startsAboveRegion = false;
685                     range->setStartBefore(node, IGNORE_EXCEPTION);
686                 }
687             }
688             skipOverOutsideNodes  = false;
689             foundStartPosition = true;
690 
691             // end position
692             if (logicalBottomForRegion < logicalBottomForRenderer && (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode)))) {
693                 // for Text elements, just find just find the last textbox that is contained inside the region and use its start()+len() offset as end position
694                 if (renderer->isText()) { // Text crosses region bottom
695                     RenderText* textRenderer = toRenderText(renderer);
696                     InlineTextBox* lastBox = 0;
697                     for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
698                         if ((offsetTop + box->logicalTop()) < logicalBottomForRegion) {
699                             lastBox = box;
700                             continue;
701                         }
702                         ASSERT(lastBox);
703                         if (lastBox)
704                             range->setEnd(Position(toText(node), lastBox->start() + lastBox->len()));
705                         break;
706                     }
707                     endsBelowRegion = false;
708                     lastEndNode = node;
709                 } else { // node crosses region bottom
710                     // for all elements, except Text, just set the start position to be after their children
711                     range->setEnd(Position(node, Position::PositionIsAfterChildren));
712                     endsBelowRegion = true;
713                     lastEndNode = node;
714                 }
715             } else { // node ends inside region
716                 // for elements that ends inside the region, set the end position to be after them
717                 // allow this end position to be changed only by other elements that are not descendants of the current end node
718                 if (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode))) {
719                     range->setEndAfter(node, IGNORE_EXCEPTION);
720                     endsBelowRegion = false;
721                     lastEndNode = node;
722                 }
723             }
724         }
725         if (foundStartPosition || skipOverOutsideNodes)
726             rangeObjects.append(range);
727     }
728 }
729 
730 }
731