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