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