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