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