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