5cbcffd5cce6d218ea41c4b4cf8ee5c8d86698e1
[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 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 "RenderLayer.h"
39 #include "RenderLineBreak.h"
40 #include "RenderNamedFlowFragment.h"
41 #include "RenderText.h"
42 #include "RenderView.h"
43 #include "ShadowRoot.h"
44 #include "Text.h"
45 #include "WebKitNamedFlow.h"
46
47 namespace WebCore {
48
49 RenderNamedFlowThread::RenderNamedFlowThread(Document& document, PassRef<RenderStyle> style, PassRef<WebKitNamedFlow> namedFlow)
50     : RenderFlowThread(document, WTF::move(style))
51     , m_hasRegionsWithStyling(false)
52     , m_dispatchRegionOversetChangeEvent(false)
53     , m_namedFlow(WTF::move(namedFlow))
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->isNamedFlowContentNode());
78         ASSERT(&contentElement->document() == &document());
79         
80         contentElement->clearIsNamedFlowContentNode();
81     }
82     
83     m_contentElements.clear();
84 }
85
86 void RenderNamedFlowThread::updateWritingMode()
87 {
88     RenderNamedFlowFragment* firstFragment = toRenderNamedFlowFragment(m_regionList.first());
89     if (!firstFragment)
90         return;
91     if (style().writingMode() == firstFragment->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(firstFragment->style().writingMode());
97     setStyle(WTF::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 nullptr;
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 compareRenderNamedFlowFragments(const RenderNamedFlowFragment* firstFragment, const RenderNamedFlowFragment* secondFragment)
153 {
154     ASSERT(firstFragment);
155     ASSERT(secondFragment);
156
157     ASSERT(firstFragment->generatingElement());
158     ASSERT(secondFragment->generatingElement());
159
160     // If the regions belong to different nodes, compare their position in the DOM.
161     if (firstFragment->generatingElement() != secondFragment->generatingElement()) {
162         unsigned short position = firstFragment->generatingElement()->compareDocumentPosition(secondFragment->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(secondFragment->style().styleType() == NOPSEUDO);
167             return firstFragment->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(firstFragment->style().styleType() == NOPSEUDO);
173             return secondFragment->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 (firstFragment->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 firstFragment->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 addFragmentToList(RenderRegionList& regionList, RenderNamedFlowFragment* renderNamedFlowFragment)
200 {
201     if (regionList.isEmpty())
202         regionList.add(renderNamedFlowFragment);
203     else {
204         // Find the first region "greater" than renderNamedFlowFragment.
205         auto it = regionList.begin();
206         while (it != regionList.end() && !compareRenderNamedFlowFragments(renderNamedFlowFragment, toRenderNamedFlowFragment(*it)))
207             ++it;
208         regionList.insertBefore(it, renderNamedFlowFragment);
209     }
210 }
211
212 void RenderNamedFlowThread::addFragmentToNamedFlowThread(RenderNamedFlowFragment* renderNamedFlowFragment)
213 {
214     ASSERT(renderNamedFlowFragment);
215     ASSERT(!renderNamedFlowFragment->isValid());
216
217     if (renderNamedFlowFragment->parentNamedFlowThread())
218         addDependencyOnFlowThread(renderNamedFlowFragment->parentNamedFlowThread());
219
220     renderNamedFlowFragment->setIsValid(true);
221     renderNamedFlowFragment->updateRegionFlags();
222     addFragmentToList(m_regionList, renderNamedFlowFragment);
223
224     if (m_regionList.first() == renderNamedFlowFragment)
225         updateWritingMode();
226 }
227
228 void RenderNamedFlowThread::addRegionToThread(RenderRegion* renderRegion)
229 {
230     ASSERT(renderRegion);
231     ASSERT(!renderRegion->isValid());
232
233     RenderNamedFlowFragment* renderNamedFlowFragment = toRenderNamedFlowFragment(renderRegion);
234     resetMarkForDestruction();
235
236     if (renderNamedFlowFragment->parentNamedFlowThread() && renderNamedFlowFragment->parentNamedFlowThread()->dependsOn(this)) {
237         // The order of invalid regions is irrelevant.
238         m_invalidRegionList.add(renderNamedFlowFragment);
239         // Register ourself to get a notification when the state changes.
240         renderNamedFlowFragment->parentNamedFlowThread()->m_observerThreadsSet.add(this);
241         return;
242     }
243
244     addFragmentToNamedFlowThread(renderNamedFlowFragment);
245
246     invalidateRegions();
247 }
248
249 void RenderNamedFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
250 {
251     ASSERT(renderRegion);
252
253     RenderNamedFlowFragment* renderNamedFlowFragment = toRenderNamedFlowFragment(renderRegion);
254     if (renderNamedFlowFragment->parentNamedFlowThread()) {
255         if (!renderNamedFlowFragment->isValid()) {
256             ASSERT(m_invalidRegionList.contains(renderNamedFlowFragment));
257             m_invalidRegionList.remove(renderNamedFlowFragment);
258             renderNamedFlowFragment->parentNamedFlowThread()->m_observerThreadsSet.remove(this);
259             // No need to invalidate the regions rectangles. The removed region
260             // was not taken into account. Just return here.
261             return;
262         }
263         removeDependencyOnFlowThread(renderNamedFlowFragment->parentNamedFlowThread());
264     }
265
266     ASSERT(m_regionList.contains(renderNamedFlowFragment));
267     bool wasFirst = m_regionList.first() == renderNamedFlowFragment;
268     m_regionList.remove(renderNamedFlowFragment);
269
270     if (canBeDestroyed())
271         setMarkForDestruction();
272
273     if (!m_regionList.isEmpty() && 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 LayoutRect RenderNamedFlowThread::decorationsClipRectForBoxInNamedFlowFragment(const RenderBox& box, RenderNamedFlowFragment& fragment) const
286 {
287     LayoutRect visualOverflowRect = fragment.visualOverflowRectForBox(box);
288     LayoutUnit initialLogicalX = style().isHorizontalWritingMode() ? visualOverflowRect.x() : visualOverflowRect.y();
289
290     // The visual overflow rect returned by visualOverflowRectForBox is already flipped but the
291     // RenderRegion::rectFlowPortionForBox method expects it unflipped.
292     flipForWritingModeLocalCoordinates(visualOverflowRect);
293     visualOverflowRect = fragment.rectFlowPortionForBox(&box, visualOverflowRect);
294     
295     // Now flip it again.
296     flipForWritingModeLocalCoordinates(visualOverflowRect);
297
298     // Take the scrolled offset of this object's parents into consideration.
299     IntSize scrolledContentOffset;
300     RenderBlock* containingBlock = box.containingBlock();
301     while (containingBlock) {
302         if (containingBlock->isRenderNamedFlowThread()) {
303             // We've reached the flow thread, take the scrolled offset of the region into consideration.
304             ASSERT(containingBlock == this);
305             scrolledContentOffset += fragment.fragmentContainer().scrolledContentOffset();
306             break;
307         }
308         
309         scrolledContentOffset += containingBlock->scrolledContentOffset();
310         containingBlock = containingBlock->containingBlock();
311     }
312
313     if (!scrolledContentOffset.isZero()) {
314         if (style().isFlippedBlocksWritingMode())
315             scrolledContentOffset = -scrolledContentOffset;
316         
317         visualOverflowRect.inflateX(scrolledContentOffset.width());
318         visualOverflowRect.inflateY(scrolledContentOffset.height());
319     }
320     
321     // Layers are in physical coordinates so the origin must be moved to the physical top-left of the flowthread.
322     if (style().isFlippedBlocksWritingMode()) {
323         if (style().isHorizontalWritingMode())
324             visualOverflowRect.moveBy(LayoutPoint(0, height()));
325         else
326             visualOverflowRect.moveBy(LayoutPoint(width(), 0));
327     }
328
329     const RenderBox* iterBox = &box;
330     while (iterBox && iterBox != this) {
331         RenderBlock* containerBlock = iterBox->containingBlock();
332
333         // FIXME: This doesn't work properly with flipped writing modes.
334         // https://bugs.webkit.org/show_bug.cgi?id=125149
335         if (iterBox->isPositioned()) {
336             // For positioned elements, just use the layer's absolute bounding box.
337             visualOverflowRect.moveBy(iterBox->layer()->absoluteBoundingBox().location());
338             break;
339         }
340
341         LayoutRect currentBoxRect = iterBox->frameRect();
342         if (iterBox->style().isFlippedBlocksWritingMode()) {
343             if (iterBox->style().isHorizontalWritingMode())
344                 currentBoxRect.setY(currentBoxRect.height() - currentBoxRect.maxY());
345             else
346                 currentBoxRect.setX(currentBoxRect.width() - currentBoxRect.maxX());
347         }
348
349         if (containerBlock->style().writingMode() != iterBox->style().writingMode())
350             iterBox->flipForWritingMode(currentBoxRect);
351
352         visualOverflowRect.moveBy(currentBoxRect.location());
353         iterBox = containerBlock;
354     }
355
356     // Since the purpose of this method is to make sure the borders of a fragmented
357     // element don't overflow the region in the fragmentation direction, there's no
358     // point in restricting the clipping rect on the logical X axis. 
359     // This also saves us the trouble of handling percent-based widths and margins
360     // since the absolute bounding box of a positioned element would not contain
361     // the correct coordinates relative to the region we're interested in, but rather
362     // relative to the actual flow thread.
363     if (style().isHorizontalWritingMode()) {
364         if (initialLogicalX < visualOverflowRect.x())
365             visualOverflowRect.shiftXEdgeTo(initialLogicalX);
366         if (visualOverflowRect.width() < frameRect().width())
367             visualOverflowRect.setWidth(frameRect().width());
368     } else {
369         if (initialLogicalX < visualOverflowRect.y())
370             visualOverflowRect.shiftYEdgeTo(initialLogicalX);
371         if (visualOverflowRect.height() < frameRect().height())
372             visualOverflowRect.setHeight(frameRect().height());
373     }
374
375     return visualOverflowRect;
376 }
377
378 RenderBlock* RenderNamedFlowThread::fragmentFromRenderBoxAsRenderBlock(RenderBox* renderBox, const IntPoint& absolutePoint, const RenderBox& flowedBox)
379 {
380     return toRenderNamedFlowThread(renderBox)->fragmentFromAbsolutePointAndBox(absolutePoint, flowedBox);
381 }
382
383 RenderNamedFlowFragment* RenderNamedFlowThread::fragmentFromAbsolutePointAndBox(const IntPoint& absolutePoint, const RenderBox& flowedBox)
384 {
385     RenderRegion* startRegion = nullptr;
386     RenderRegion* endRegion = nullptr;
387     if (!getRegionRangeForBox(&flowedBox, startRegion, endRegion))
388         return nullptr;
389     
390     for (auto iter = m_regionList.find(startRegion), end = m_regionList.end(); iter != end; ++iter) {
391         RenderNamedFlowFragment* fragment = toRenderNamedFlowFragment(*iter);
392         RenderBlockFlow& fragmentContainer = fragment->fragmentContainer();
393         IntRect fragmentAbsoluteRect(roundedIntPoint(fragmentContainer.localToAbsolute()), roundedIntSize(fragmentContainer.paddingBoxRect().size()));
394         if (fragmentAbsoluteRect.contains(absolutePoint))
395             return fragment;
396         
397         if (fragment == endRegion)
398             break;
399     }
400     
401     return nullptr;
402 }
403
404 void RenderNamedFlowThread::computeOverflow(LayoutUnit oldClientAfterEdge, bool recomputeFloats)
405 {
406     RenderFlowThread::computeOverflow(oldClientAfterEdge, recomputeFloats);
407
408     m_flowContentBottom = oldClientAfterEdge;
409 }
410
411 void RenderNamedFlowThread::layout()
412 {
413     RenderFlowThread::layout();
414
415     // If the number of regions has changed since we last computed the overset property, schedule the regionOversetChange event.
416     if (previousRegionCountChanged()) {
417         setDispatchRegionOversetChangeEvent(true);
418         updatePreviousRegionCount();
419     }
420 }
421
422 void RenderNamedFlowThread::dispatchNamedFlowEvents()
423 {
424     ASSERT(inFinalLayoutPhase());
425
426     dispatchRegionOversetChangeEventIfNeeded();
427 }
428
429 void RenderNamedFlowThread::checkInvalidRegions()
430 {
431     Vector<RenderNamedFlowFragment*> newValidFragments;
432     for (auto& region : m_invalidRegionList) {
433         RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(region);
434         // The only reason a region would be invalid is because it has a parent flow thread.
435         ASSERT(!namedFlowFragment->isValid() && namedFlowFragment->parentNamedFlowThread());
436         if (namedFlowFragment->parentNamedFlowThread()->dependsOn(this))
437             continue;
438
439         newValidFragments.append(namedFlowFragment);
440     }
441
442     for (auto& namedFlowFragment : newValidFragments) {
443         m_invalidRegionList.remove(namedFlowFragment);
444         namedFlowFragment->parentNamedFlowThread()->m_observerThreadsSet.remove(this);
445         addFragmentToNamedFlowThread(namedFlowFragment);
446     }
447
448     if (!newValidFragments.isEmpty())
449         invalidateRegions();
450
451     if (m_observerThreadsSet.isEmpty())
452         return;
453
454     // Notify all the flow threads that were dependent on this flow.
455
456     // Create a copy of the list first. That's because observers might change the list when calling checkInvalidRegions.
457     Vector<RenderNamedFlowThread*> observers;
458     copyToVector(m_observerThreadsSet, observers);
459
460     for (auto& flowThread : observers)
461         flowThread->checkInvalidRegions();
462 }
463
464 void RenderNamedFlowThread::addDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread)
465 {
466     RenderNamedFlowThreadCountedSet::AddResult result = m_layoutBeforeThreadsSet.add(otherFlowThread);
467     if (result.isNewEntry) {
468         // This is the first time we see this dependency. Make sure we recalculate all the dependencies.
469         view().flowThreadController().setIsRenderNamedFlowThreadOrderDirty(true);
470     }
471 }
472
473 void RenderNamedFlowThread::removeDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread)
474 {
475     bool removed = m_layoutBeforeThreadsSet.remove(otherFlowThread);
476     if (removed) {
477         checkInvalidRegions();
478         view().flowThreadController().setIsRenderNamedFlowThreadOrderDirty(true);
479     }
480 }
481
482 void RenderNamedFlowThread::pushDependencies(RenderNamedFlowThreadList& list)
483 {
484     for (auto& flowThreadPair : m_layoutBeforeThreadsSet) {
485         auto& flowThread = flowThreadPair.key;
486         if (list.contains(flowThread))
487             continue;
488         flowThread->pushDependencies(list);
489         list.add(flowThread);
490     }
491 }
492
493 // The content nodes list contains those nodes with -webkit-flow-into: flow.
494 // An element with display:none should also be listed among those nodes.
495 // The list of nodes is ordered.
496 void RenderNamedFlowThread::registerNamedFlowContentElement(Element& contentElement)
497 {
498     ASSERT(&contentElement.document() == &document());
499
500     contentElement.setIsNamedFlowContentNode();
501
502     resetMarkForDestruction();
503
504     // Find the first content node following the new content node.
505     for (auto& element : m_contentElements) {
506         unsigned short position = contentElement.compareDocumentPosition(element);
507         if (position & Node::DOCUMENT_POSITION_FOLLOWING) {
508             m_contentElements.insertBefore(element, &contentElement);
509             InspectorInstrumentation::didRegisterNamedFlowContentElement(&document(), &namedFlow(), &contentElement, element);
510             return;
511         }
512     }
513
514     m_contentElements.add(&contentElement);
515     InspectorInstrumentation::didRegisterNamedFlowContentElement(&document(), &namedFlow(), &contentElement);
516 }
517
518 void RenderNamedFlowThread::unregisterNamedFlowContentElement(Element& contentElement)
519 {
520     ASSERT(m_contentElements.contains(&contentElement));
521     ASSERT(contentElement.isNamedFlowContentNode());
522     ASSERT(&contentElement.document() == &document());
523
524     contentElement.clearIsNamedFlowContentNode();
525     m_contentElements.remove(&contentElement);
526
527     if (canBeDestroyed())
528         setMarkForDestruction();
529
530     InspectorInstrumentation::didUnregisterNamedFlowContentElement(&document(), &namedFlow(), &contentElement);
531 }
532
533 bool RenderNamedFlowThread::hasContentElement(Element& contentElement) const
534 {
535     return m_contentElements.contains(&contentElement);
536 }
537     
538 const AtomicString& RenderNamedFlowThread::flowThreadName() const
539 {
540     return namedFlow().name();
541 }
542
543 bool RenderNamedFlowThread::isChildAllowed(const RenderObject& child, const RenderStyle& style) const
544 {
545     if (!child.node())
546         return true;
547
548     ASSERT(is<Element>(*child.node()));
549
550     Node* originalParent = NodeRenderingTraversal::parent(child.node());
551     if (!is<Element>(originalParent) || !originalParent->renderer())
552         return true;
553
554     return downcast<Element>(*originalParent).renderer()->isChildAllowed(child, style);
555 }
556
557 void RenderNamedFlowThread::dispatchRegionOversetChangeEventIfNeeded()
558 {
559     if (!m_dispatchRegionOversetChangeEvent)
560         return;
561
562     m_dispatchRegionOversetChangeEvent = false;
563     InspectorInstrumentation::didChangeRegionOverset(&document(), &namedFlow());
564     
565     if (!m_regionOversetChangeEventTimer.isActive() && namedFlow().hasEventListeners())
566         m_regionOversetChangeEventTimer.startOneShot(0);
567 }
568
569 void RenderNamedFlowThread::regionOversetChangeEventTimerFired(Timer<RenderNamedFlowThread>&)
570 {
571     namedFlow().dispatchRegionOversetChangeEvent();
572 }
573
574 void RenderNamedFlowThread::setMarkForDestruction()
575 {
576     if (namedFlow().flowState() == WebKitNamedFlow::FlowStateNull)
577         return;
578
579     namedFlow().setRenderer(nullptr);
580     // After this call ends, the renderer can be safely destroyed.
581     // 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.
582 }
583
584 void RenderNamedFlowThread::resetMarkForDestruction()
585 {
586     if (namedFlow().flowState() == WebKitNamedFlow::FlowStateCreated)
587         return;
588
589     namedFlow().setRenderer(this);
590 }
591
592 bool RenderNamedFlowThread::isMarkedForDestruction() const
593 {
594     // Flow threads in the "NULL" state can be destroyed.
595     return namedFlow().flowState() == WebKitNamedFlow::FlowStateNull;
596 }
597
598 static bool isContainedInElements(const Vector<Element*>& others, Element* element)
599 {
600     for (auto& other : others) {
601         if (other->contains(element))
602             return true;
603     }
604     return false;
605 }
606
607 static bool boxIntersectsRegion(LayoutUnit logicalTopForBox, LayoutUnit logicalBottomForBox, LayoutUnit logicalTopForRegion, LayoutUnit logicalBottomForRegion)
608 {
609     bool regionIsEmpty = logicalBottomForRegion != LayoutUnit::max() && logicalTopForRegion != LayoutUnit::min()
610         && (logicalBottomForRegion - logicalTopForRegion) <= 0;
611     return  (logicalBottomForBox - logicalTopForBox) > 0
612         && !regionIsEmpty
613         && logicalTopForBox < logicalBottomForRegion && logicalTopForRegion < logicalBottomForBox;
614 }
615
616 // Retrieve the next node to be visited while computing the ranges inside a region.
617 static Node* nextNodeInsideContentElement(const Node* currNode, const Element* contentElement)
618 {
619     ASSERT(currNode);
620     ASSERT(contentElement && contentElement->isNamedFlowContentNode());
621
622     if (currNode->renderer() && currNode->renderer()->isSVGRoot())
623         return NodeTraversal::nextSkippingChildren(currNode, contentElement);
624
625     return NodeTraversal::next(currNode, contentElement);
626 }
627
628 void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range>>& rangeObjects, const RenderNamedFlowFragment* namedFlowFragment) const
629 {
630     LayoutUnit logicalTopForRegion;
631     LayoutUnit logicalBottomForRegion;
632
633     // extend the first region top to contain everything up to its logical height
634     if (namedFlowFragment->isFirstRegion())
635         logicalTopForRegion = LayoutUnit::min();
636     else
637         logicalTopForRegion =  namedFlowFragment->logicalTopForFlowThreadContent();
638
639     // extend the last region to contain everything above its y()
640     if (namedFlowFragment->isLastRegion())
641         logicalBottomForRegion = LayoutUnit::max();
642     else
643         logicalBottomForRegion = namedFlowFragment->logicalBottomForFlowThreadContent();
644
645     Vector<Element*> elements;
646     // eliminate the contentElements that are descendants of other contentElements
647     for (auto& element : contentElements()) {
648         if (!isContainedInElements(elements, element))
649             elements.append(element);
650     }
651
652     for (auto& contentElement : elements) {
653         if (!contentElement->renderer())
654             continue;
655
656         RefPtr<Range> range = Range::create(contentElement->document());
657         bool foundStartPosition = false;
658         bool startsAboveRegion = true;
659         bool endsBelowRegion = true;
660         bool skipOverOutsideNodes = false;
661         Node* lastEndNode = nullptr;
662
663         for (Node* node = contentElement; node; node = nextNodeInsideContentElement(node, contentElement)) {
664             RenderObject* renderer = node->renderer();
665             if (!renderer)
666                 continue;
667
668             LayoutRect boundingBox;
669             if (is<RenderInline>(*renderer))
670                 boundingBox = downcast<RenderInline>(*renderer).linesBoundingBox();
671             else if (is<RenderText>(*renderer))
672                 boundingBox = downcast<RenderText>(*renderer).linesBoundingBox();
673             else if (is<RenderLineBreak>(*renderer))
674                 boundingBox = downcast<RenderLineBreak>(*renderer).linesBoundingBox();
675             else if (is<RenderBox>(*renderer)) {
676                 auto& renderBox = downcast<RenderBox>(*renderer);
677                 boundingBox = renderBox.frameRect();
678                 if (renderBox.isRelPositioned())
679                     boundingBox.move(renderBox.relativePositionLogicalOffset());
680             } else
681                 continue;
682
683             LayoutUnit offsetTop = renderer->containingBlock()->offsetFromLogicalTopOfFirstPage();
684             const LayoutPoint logicalOffsetFromTop(isHorizontalWritingMode() ? LayoutUnit() :  offsetTop,
685                 isHorizontalWritingMode() ? offsetTop : LayoutUnit());
686
687             boundingBox.moveBy(logicalOffsetFromTop);
688
689             LayoutUnit logicalTopForRenderer = namedFlowFragment->logicalTopOfFlowThreadContentRect(boundingBox);
690             LayoutUnit logicalBottomForRenderer = namedFlowFragment->logicalBottomOfFlowThreadContentRect(boundingBox);
691
692             // if the bounding box of the current element doesn't intersect the region box
693             // close the current range only if the start element began inside the region,
694             // otherwise just move the start position after this node and keep skipping them until we found a proper start position.
695             if (!boxIntersectsRegion(logicalTopForRenderer, logicalBottomForRenderer, logicalTopForRegion, logicalBottomForRegion)) {
696                 if (foundStartPosition) {
697                     if (!startsAboveRegion) {
698                         if (range->intersectsNode(node, IGNORE_EXCEPTION))
699                             range->setEndBefore(node, IGNORE_EXCEPTION);
700                         rangeObjects.append(range->cloneRange(IGNORE_EXCEPTION));
701                         range = Range::create(contentElement->document());
702                         startsAboveRegion = true;
703                     } else
704                         skipOverOutsideNodes = true;
705                 }
706                 if (skipOverOutsideNodes)
707                     range->setStartAfter(node, IGNORE_EXCEPTION);
708                 foundStartPosition = false;
709                 continue;
710             }
711
712             // start position
713             if (logicalTopForRenderer < logicalTopForRegion && startsAboveRegion) {
714                 if (is<RenderText>(*renderer)) {
715                     // Text crosses region top
716                     // for Text elements, just find the last textbox that is contained inside the region and use its start() offset as start position
717                     RenderText& textRenderer = downcast<RenderText>(*renderer);
718                     for (InlineTextBox* box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) {
719                         if (offsetTop + box->logicalBottom() < logicalTopForRegion)
720                             continue;
721                         range->setStart(Position(downcast<Text>(node), box->start()));
722                         startsAboveRegion = false;
723                         break;
724                     }
725                 } else {
726                     // node crosses region top
727                     // for all elements, except Text, just set the start position to be before their children
728                     startsAboveRegion = true;
729                     range->setStart(Position(node, Position::PositionIsBeforeChildren));
730                 }
731             } else {
732                 // node starts inside region
733                 // 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
734                 // the range is closed.
735                 if (startsAboveRegion) {
736                     startsAboveRegion = false;
737                     range->setStartBefore(node, IGNORE_EXCEPTION);
738                 }
739             }
740             skipOverOutsideNodes  = false;
741             foundStartPosition = true;
742
743             // end position
744             if (logicalBottomForRegion < logicalBottomForRenderer && (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode)))) {
745                 // 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
746                 if (is<RenderText>(*renderer)) {
747                     // Text crosses region bottom
748                     RenderText& textRenderer = downcast<RenderText>(*renderer);
749                     InlineTextBox* lastBox = nullptr;
750                     for (InlineTextBox* box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) {
751                         if ((offsetTop + box->logicalTop()) < logicalBottomForRegion) {
752                             lastBox = box;
753                             continue;
754                         }
755                         ASSERT(lastBox);
756                         if (lastBox)
757                             range->setEnd(Position(downcast<Text>(node), lastBox->start() + lastBox->len()));
758                         break;
759                     }
760                     endsBelowRegion = false;
761                     lastEndNode = node;
762                 } else {
763                     // node crosses region bottom
764                     // for all elements, except Text, just set the start position to be after their children
765                     range->setEnd(Position(node, Position::PositionIsAfterChildren));
766                     endsBelowRegion = true;
767                     lastEndNode = node;
768                 }
769             } else {
770                 // node ends inside region
771                 // for elements that ends inside the region, set the end position to be after them
772                 // allow this end position to be changed only by other elements that are not descendants of the current end node
773                 if (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode))) {
774                     range->setEndAfter(node, IGNORE_EXCEPTION);
775                     endsBelowRegion = false;
776                     lastEndNode = node;
777                 }
778             }
779         }
780         if (foundStartPosition || skipOverOutsideNodes)
781             rangeObjects.append(range);
782     }
783 }
784
785 void RenderNamedFlowThread::applyBreakAfterContent(LayoutUnit clientHeight)
786 {
787     // Simulate a region break at height. If it points inside an auto logical height region,
788     // then it may determine the region computed autoheight.
789     addForcedRegionBreak(this, clientHeight, this, false);
790 }
791
792 bool RenderNamedFlowThread::collectsGraphicsLayersUnderRegions() const
793 {
794     // We only need to map layers to regions for named flow threads.
795     // Multi-column threads are displayed on top of the regions and do not require
796     // distributing the layers.
797
798     return true;
799 }
800
801 // Check if the content is flown into at least a region with region styling rules.
802 void RenderNamedFlowThread::checkRegionsWithStyling()
803 {
804     bool hasRegionsWithStyling = false;
805     for (const auto& region : m_regionList) {
806         if (toRenderNamedFlowFragment(region)->hasCustomRegionStyle()) {
807             hasRegionsWithStyling = true;
808             break;
809         }
810     }
811     m_hasRegionsWithStyling = hasRegionsWithStyling;
812 }
813
814 void RenderNamedFlowThread::clearRenderObjectCustomStyle(const RenderObject* object)
815 {
816     // Clear the styles for the object in the regions.
817     // 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.
818     for (auto& region : m_regionList)
819         toRenderNamedFlowFragment(region)->clearObjectStyleInRegion(object);
820 }
821
822 void RenderNamedFlowThread::removeFlowChildInfo(RenderObject* child)
823 {
824     RenderFlowThread::removeFlowChildInfo(child);
825     clearRenderObjectCustomStyle(child);
826 }
827
828 bool RenderNamedFlowThread::absoluteQuadsForBox(Vector<FloatQuad>& quads, bool* wasFixed, const RenderBox* renderer, float localTop, float localBottom) const
829 {
830     RenderRegion* startRegion = nullptr;
831     RenderRegion* endRegion = nullptr;
832     // If the box doesn't have a range, we don't know how it is fragmented so fallback to the default behaviour.
833     if (!computedRegionRangeForBox(renderer, startRegion, endRegion))
834         return false;
835
836     for (auto iter = m_regionList.find(startRegion), end = m_regionList.end(); iter != end; ++iter) {
837         RenderRegion* region = *iter;
838
839         region->absoluteQuadsForBoxInRegion(quads, wasFixed, renderer, localTop, localBottom);
840
841         if (region == endRegion)
842             break;
843     }
844
845     return true;
846 }
847
848 }