CTTE: RenderNamedFlowThread always has a WebKitNamedFlow.
[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, PassRef<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(std::move(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(), &namedFlow(), &contentElement, element);
440             return;
441         }
442     }
443
444     m_contentElements.add(&contentElement);
445     InspectorInstrumentation::didRegisterNamedFlowContentElement(&document(), &namedFlow(), &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(), &namedFlow(), &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 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(), &namedFlow());
491
492     if (!m_regionLayoutUpdateEventTimer.isActive() && namedFlow().hasEventListeners())
493         m_regionLayoutUpdateEventTimer.startOneShot(0);
494 }
495
496 void RenderNamedFlowThread::dispatchRegionOversetChangeEvent()
497 {
498     RenderFlowThread::dispatchRegionOversetChangeEvent();
499     InspectorInstrumentation::didChangeRegionOverset(&document(), &namedFlow());
500     
501     if (!m_regionOversetChangeEventTimer.isActive() && namedFlow().hasEventListeners())
502         m_regionOversetChangeEventTimer.startOneShot(0);
503 }
504
505 void RenderNamedFlowThread::regionLayoutUpdateEventTimerFired(Timer<RenderNamedFlowThread>&)
506 {
507     namedFlow().dispatchRegionLayoutUpdateEvent();
508 }
509
510 void RenderNamedFlowThread::regionOversetChangeEventTimerFired(Timer<RenderNamedFlowThread>&)
511 {
512     namedFlow().dispatchRegionOversetChangeEvent();
513 }
514
515 void RenderNamedFlowThread::setMarkForDestruction()
516 {
517     if (namedFlow().flowState() == WebKitNamedFlow::FlowStateNull)
518         return;
519
520     namedFlow().setRenderer(nullptr);
521     // After this call ends, the renderer can be safely destroyed.
522     // 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.
523 }
524
525 void RenderNamedFlowThread::resetMarkForDestruction()
526 {
527     if (namedFlow().flowState() == WebKitNamedFlow::FlowStateCreated)
528         return;
529
530     namedFlow().setRenderer(this);
531 }
532
533 bool RenderNamedFlowThread::isMarkedForDestruction() const
534 {
535     // Flow threads in the "NULL" state can be destroyed.
536     return namedFlow().flowState() == WebKitNamedFlow::FlowStateNull;
537 }
538
539 static bool isContainedInElements(const Vector<Element*>& others, Element* element)
540 {
541     for (auto& other : others) {
542         if (other->contains(element))
543             return true;
544     }
545     return false;
546 }
547
548 static bool boxIntersectsRegion(LayoutUnit logicalTopForBox, LayoutUnit logicalBottomForBox, LayoutUnit logicalTopForRegion, LayoutUnit logicalBottomForRegion)
549 {
550     bool regionIsEmpty = logicalBottomForRegion != LayoutUnit::max() && logicalTopForRegion != LayoutUnit::min()
551         && (logicalBottomForRegion - logicalTopForRegion) <= 0;
552     return  (logicalBottomForBox - logicalTopForBox) > 0
553         && !regionIsEmpty
554         && logicalTopForBox < logicalBottomForRegion && logicalTopForRegion < logicalBottomForBox;
555 }
556
557 // Retrieve the next node to be visited while computing the ranges inside a region.
558 static Node* nextNodeInsideContentElement(const Node* currNode, const Element* contentElement)
559 {
560     ASSERT(currNode);
561     ASSERT(contentElement && contentElement->inNamedFlow());
562
563     if (currNode->renderer() && currNode->renderer()->isSVGRoot())
564         return NodeTraversal::nextSkippingChildren(currNode, contentElement);
565
566     return NodeTraversal::next(currNode, contentElement);
567 }
568
569 void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range>>& rangeObjects, const RenderRegion* region) const
570 {
571     LayoutUnit logicalTopForRegion;
572     LayoutUnit logicalBottomForRegion;
573
574     // extend the first region top to contain everything up to its logical height
575     if (region->isFirstRegion())
576         logicalTopForRegion = LayoutUnit::min();
577     else
578         logicalTopForRegion =  region->logicalTopForFlowThreadContent();
579
580     // extend the last region to contain everything above its y()
581     if (region->isLastRegion())
582         logicalBottomForRegion = LayoutUnit::max();
583     else
584         logicalBottomForRegion = region->logicalBottomForFlowThreadContent();
585
586     Vector<Element*> elements;
587     // eliminate the contentElements that are descendants of other contentElements
588     for (auto& element : contentElements()) {
589         if (!isContainedInElements(elements, element))
590             elements.append(element);
591     }
592
593     for (auto& contentElement : elements) {
594         if (!contentElement->renderer())
595             continue;
596
597         RefPtr<Range> range = Range::create(contentElement->document());
598         bool foundStartPosition = false;
599         bool startsAboveRegion = true;
600         bool endsBelowRegion = true;
601         bool skipOverOutsideNodes = false;
602         Node* lastEndNode = 0;
603
604         for (Node* node = contentElement; node; node = nextNodeInsideContentElement(node, contentElement)) {
605             RenderObject* renderer = node->renderer();
606             if (!renderer)
607                 continue;
608
609             LayoutRect boundingBox;
610             if (renderer->isRenderInline())
611                 boundingBox = toRenderInline(renderer)->linesBoundingBox();
612             else if (renderer->isText())
613                 boundingBox = toRenderText(renderer)->linesBoundingBox();
614             else {
615                 boundingBox =  toRenderBox(renderer)->frameRect();
616                 if (toRenderBox(renderer)->isRelPositioned())
617                     boundingBox.move(toRenderBox(renderer)->relativePositionLogicalOffset());
618             }
619
620             LayoutUnit offsetTop = renderer->containingBlock()->offsetFromLogicalTopOfFirstPage();
621             const LayoutPoint logicalOffsetFromTop(isHorizontalWritingMode() ? LayoutUnit() :  offsetTop,
622                 isHorizontalWritingMode() ? offsetTop : LayoutUnit());
623
624             boundingBox.moveBy(logicalOffsetFromTop);
625
626             LayoutUnit logicalTopForRenderer = region->logicalTopOfFlowThreadContentRect(boundingBox);
627             LayoutUnit logicalBottomForRenderer = region->logicalBottomOfFlowThreadContentRect(boundingBox);
628
629             // if the bounding box of the current element doesn't intersect the region box
630             // close the current range only if the start element began inside the region,
631             // otherwise just move the start position after this node and keep skipping them until we found a proper start position.
632             if (!boxIntersectsRegion(logicalTopForRenderer, logicalBottomForRenderer, logicalTopForRegion, logicalBottomForRegion)) {
633                 if (foundStartPosition) {
634                     if (!startsAboveRegion) {
635                         if (range->intersectsNode(node, IGNORE_EXCEPTION))
636                             range->setEndBefore(node, IGNORE_EXCEPTION);
637                         rangeObjects.append(range->cloneRange(IGNORE_EXCEPTION));
638                         range = Range::create(contentElement->document());
639                         startsAboveRegion = true;
640                     } else
641                         skipOverOutsideNodes = true;
642                 }
643                 if (skipOverOutsideNodes)
644                     range->setStartAfter(node, IGNORE_EXCEPTION);
645                 foundStartPosition = false;
646                 continue;
647             }
648
649             // start position
650             if (logicalTopForRenderer < logicalTopForRegion && startsAboveRegion) {
651                 if (renderer->isText()) { // Text crosses region top
652                     // for Text elements, just find the last textbox that is contained inside the region and use its start() offset as start position
653                     RenderText* textRenderer = toRenderText(renderer);
654                     for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
655                         if (offsetTop + box->logicalBottom() < logicalTopForRegion)
656                             continue;
657                         range->setStart(Position(toText(node), box->start()));
658                         startsAboveRegion = false;
659                         break;
660                     }
661                 } else { // node crosses region top
662                     // for all elements, except Text, just set the start position to be before their children
663                     startsAboveRegion = true;
664                     range->setStart(Position(node, Position::PositionIsBeforeChildren));
665                 }
666             } else { // node starts inside region
667                 // 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
668                 // the range is closed.
669                 if (startsAboveRegion) {
670                     startsAboveRegion = false;
671                     range->setStartBefore(node, IGNORE_EXCEPTION);
672                 }
673             }
674             skipOverOutsideNodes  = false;
675             foundStartPosition = true;
676
677             // end position
678             if (logicalBottomForRegion < logicalBottomForRenderer && (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode)))) {
679                 // 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
680                 if (renderer->isText()) { // Text crosses region bottom
681                     RenderText* textRenderer = toRenderText(renderer);
682                     InlineTextBox* lastBox = 0;
683                     for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
684                         if ((offsetTop + box->logicalTop()) < logicalBottomForRegion) {
685                             lastBox = box;
686                             continue;
687                         }
688                         ASSERT(lastBox);
689                         if (lastBox)
690                             range->setEnd(Position(toText(node), lastBox->start() + lastBox->len()));
691                         break;
692                     }
693                     endsBelowRegion = false;
694                     lastEndNode = node;
695                 } else { // node crosses region bottom
696                     // for all elements, except Text, just set the start position to be after their children
697                     range->setEnd(Position(node, Position::PositionIsAfterChildren));
698                     endsBelowRegion = true;
699                     lastEndNode = node;
700                 }
701             } else { // node ends inside region
702                 // for elements that ends inside the region, set the end position to be after them
703                 // allow this end position to be changed only by other elements that are not descendants of the current end node
704                 if (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode))) {
705                     range->setEndAfter(node, IGNORE_EXCEPTION);
706                     endsBelowRegion = false;
707                     lastEndNode = node;
708                 }
709             }
710         }
711         if (foundStartPosition || skipOverOutsideNodes)
712             rangeObjects.append(range);
713     }
714 }
715
716 bool RenderNamedFlowThread::collectsGraphicsLayersUnderRegions() const
717 {
718     // We only need to map layers to regions for named flow threads.
719     // Multi-column threads are displayed on top of the regions and do not require
720     // distributing the layers.
721
722     return true;
723 }
724
725 // Check if the content is flown into at least a region with region styling rules.
726 void RenderNamedFlowThread::checkRegionsWithStyling()
727 {
728     bool hasRegionsWithStyling = false;
729     for (const auto& region : m_regionList) {
730         const RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(region);
731         if (namedFlowFragment->hasCustomRegionStyle()) {
732             hasRegionsWithStyling = true;
733             break;
734         }
735     }
736     m_hasRegionsWithStyling = hasRegionsWithStyling;
737 }
738
739 void RenderNamedFlowThread::clearRenderObjectCustomStyle(const RenderObject* object)
740 {
741     // Clear the styles for the object in the regions.
742     // 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.
743     for (auto& region : m_regionList) {
744         RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(region);
745         namedFlowFragment->clearObjectStyleInRegion(object);
746     }
747 }
748
749 void RenderNamedFlowThread::removeFlowChildInfo(RenderObject* child)
750 {
751     RenderFlowThread::removeFlowChildInfo(child);
752     clearRenderObjectCustomStyle(child);
753 }
754
755 }