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