diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/rendering/RenderNamedFlowThread.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/rendering/RenderNamedFlowThread.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderNamedFlowThread.cpp | 580 |
1 files changed, 321 insertions, 259 deletions
diff --git a/Source/WebCore/rendering/RenderNamedFlowThread.cpp b/Source/WebCore/rendering/RenderNamedFlowThread.cpp index 1fb5ba318..5df7e310d 100644 --- a/Source/WebCore/rendering/RenderNamedFlowThread.cpp +++ b/Source/WebCore/rendering/RenderNamedFlowThread.cpp @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,17 +26,17 @@ #include "config.h" #include "RenderNamedFlowThread.h" -#include "ExceptionCodePlaceholder.h" +#include "ComposedTreeAncestorIterator.h" #include "FlowThreadController.h" #include "InlineTextBox.h" #include "InspectorInstrumentation.h" -#include "NodeRenderingTraversal.h" #include "NodeTraversal.h" #include "Position.h" #include "Range.h" #include "RenderInline.h" +#include "RenderLayer.h" +#include "RenderLineBreak.h" #include "RenderNamedFlowFragment.h" -#include "RenderRegion.h" #include "RenderText.h" #include "RenderView.h" #include "ShadowRoot.h" @@ -45,25 +45,32 @@ namespace WebCore { -RenderNamedFlowThread::RenderNamedFlowThread(Document& document, PassRef<RenderStyle> style, PassRefPtr<WebKitNamedFlow> namedFlow) - : RenderFlowThread(document, std::move(style)) - , m_flowThreadChildList(adoptPtr(new FlowThreadChildList())) - , m_overset(true) +RenderNamedFlowThread::RenderNamedFlowThread(Document& document, RenderStyle&& style, Ref<WebKitNamedFlow>&& namedFlow) + : RenderFlowThread(document, WTFMove(style)) , m_hasRegionsWithStyling(false) - , m_namedFlow(namedFlow) - , m_regionLayoutUpdateEventTimer(this, &RenderNamedFlowThread::regionLayoutUpdateEventTimerFired) - , m_regionOversetChangeEventTimer(this, &RenderNamedFlowThread::regionOversetChangeEventTimerFired) + , m_dispatchRegionOversetChangeEvent(false) + , m_namedFlow(WTFMove(namedFlow)) + , m_regionOversetChangeEventTimer(*this, &RenderNamedFlowThread::regionOversetChangeEventTimerFired) { } RenderNamedFlowThread::~RenderNamedFlowThread() { + // Do not add any code here. Add it to willBeDestroyed() instead. +} + +void RenderNamedFlowThread::willBeDestroyed() +{ + WTFLogAlways("RenderNamedFlowThread %p willBeDestroyed", this); + // The flow thread can be destroyed without unregistering the content nodes if the document is destroyed. // This can lead to problems because the nodes are still marked as belonging to a flow thread. clearContentElements(); // Also leave the NamedFlow object in a consistent state by calling mark for destruction. setMarkForDestruction(); + + RenderFlowThread::willBeDestroyed(); } const char* RenderNamedFlowThread::renderName() const @@ -75,10 +82,10 @@ void RenderNamedFlowThread::clearContentElements() { for (auto& contentElement : m_contentElements) { ASSERT(contentElement); - ASSERT(contentElement->inNamedFlow()); + ASSERT(contentElement->isNamedFlowContentElement()); ASSERT(&contentElement->document() == &document()); - contentElement->clearInNamedFlow(); + contentElement->clearIsNamedFlowContentElement(); } m_contentElements.clear(); @@ -86,75 +93,16 @@ void RenderNamedFlowThread::clearContentElements() void RenderNamedFlowThread::updateWritingMode() { - RenderRegion* firstRegion = m_regionList.first(); - if (!firstRegion) + auto* firstFragment = downcast<RenderNamedFlowFragment>(m_regionList.first()); + if (!firstFragment) return; - if (style().writingMode() == firstRegion->style().writingMode()) + if (style().writingMode() == firstFragment->style().writingMode()) return; // The first region defines the principal writing mode for the entire flow. - auto newStyle = RenderStyle::clone(&style()); - newStyle.get().setWritingMode(firstRegion->style().writingMode()); - setStyle(std::move(newStyle)); -} - -RenderObject* RenderNamedFlowThread::nextRendererForNode(Node* node) const -{ - const FlowThreadChildList& childList = *(m_flowThreadChildList.get()); - for (auto& child : childList) { - ASSERT(child->node()); - unsigned short position = node->compareDocumentPosition(child->node()); - if (position & Node::DOCUMENT_POSITION_FOLLOWING) - return child; - } - - return 0; -} - -RenderObject* RenderNamedFlowThread::previousRendererForNode(Node* node) const -{ - if (m_flowThreadChildList->isEmpty()) - return 0; - - auto begin = m_flowThreadChildList->begin(); - auto end = m_flowThreadChildList->end(); - auto it = end; - - do { - --it; - RenderObject* child = *it; - ASSERT(child->node()); - unsigned short position = node->compareDocumentPosition(child->node()); - if (position & Node::DOCUMENT_POSITION_PRECEDING) - return child; - } while (it != begin); - - return 0; -} - -void RenderNamedFlowThread::addFlowChild(RenderObject* newChild) -{ - // The child list is used to sort the flow thread's children render objects - // based on their corresponding nodes DOM order. The list is needed to avoid searching the whole DOM. - - Node* childNode = newChild->node(); - - // Do not add anonymous objects. - if (!childNode) - return; - - ASSERT(childNode->isElementNode()); - - RenderObject* beforeChild = nextRendererForNode(childNode); - if (beforeChild) - m_flowThreadChildList->insertBefore(beforeChild, newChild); - else - m_flowThreadChildList->add(newChild); -} - -void RenderNamedFlowThread::removeFlowChild(RenderObject* child) -{ - m_flowThreadChildList->remove(child); + auto newStyle = RenderStyle::clone(style()); + newStyle.setWritingMode(firstFragment->style().writingMode()); + setStyle(WTFMove(newStyle)); } bool RenderNamedFlowThread::dependsOn(RenderNamedFlowThread* otherRenderFlowThread) const @@ -177,35 +125,35 @@ bool RenderNamedFlowThread::dependsOn(RenderNamedFlowThread* otherRenderFlowThre // If the first region appears before second region in DOM, // the first region is "less" than the second region. // If the first region is "less" than the second region, the first region receives content before second region. -static bool compareRenderRegions(const RenderRegion* firstRegion, const RenderRegion* secondRegion) +static bool compareRenderNamedFlowFragments(const RenderNamedFlowFragment* firstFragment, const RenderNamedFlowFragment* secondFragment) { - ASSERT(firstRegion); - ASSERT(secondRegion); + ASSERT(firstFragment); + ASSERT(secondFragment); - ASSERT(firstRegion->generatingElement()); - ASSERT(secondRegion->generatingElement()); + ASSERT(firstFragment->generatingElement()); + ASSERT(secondFragment->generatingElement()); // If the regions belong to different nodes, compare their position in the DOM. - if (firstRegion->generatingElement() != secondRegion->generatingElement()) { - unsigned short position = firstRegion->generatingElement()->compareDocumentPosition(secondRegion->generatingElement()); + if (firstFragment->generatingElement() != secondFragment->generatingElement()) { + unsigned short position = firstFragment->generatingElement()->compareDocumentPosition(*secondFragment->generatingElement()); // If the second region is contained in the first one, the first region is "less" if it's :before. if (position & Node::DOCUMENT_POSITION_CONTAINED_BY) { - ASSERT(secondRegion->style().styleType() == NOPSEUDO); - return firstRegion->style().styleType() == BEFORE; + ASSERT(secondFragment->style().styleType() == NOPSEUDO); + return firstFragment->style().styleType() == BEFORE; } // If the second region contains the first region, the first region is "less" if the second is :after. if (position & Node::DOCUMENT_POSITION_CONTAINS) { - ASSERT(firstRegion->style().styleType() == NOPSEUDO); - return secondRegion->style().styleType() == AFTER; + ASSERT(firstFragment->style().styleType() == NOPSEUDO); + return secondFragment->style().styleType() == AFTER; } return (position & Node::DOCUMENT_POSITION_FOLLOWING); } // FIXME: Currently it's not possible for an element to be both a region and have pseudo-children. The case is covered anyway. - switch (firstRegion->style().styleType()) { + switch (firstFragment->style().styleType()) { case BEFORE: // The second region can be the node or the after pseudo-element (before is smaller than any of those). return true; @@ -214,7 +162,7 @@ static bool compareRenderRegions(const RenderRegion* firstRegion, const RenderRe return false; case NOPSEUDO: // The second region can either be the before or the after pseudo-element (the node is only smaller than the after pseudo-element). - return firstRegion->style().styleType() == AFTER; + return firstFragment->style().styleType() == AFTER; default: break; } @@ -224,31 +172,32 @@ static bool compareRenderRegions(const RenderRegion* firstRegion, const RenderRe } // This helper function adds a region to a list preserving the order property of the list. -static void addRegionToList(RenderRegionList& regionList, RenderRegion* renderRegion) +static void addFragmentToList(RenderRegionList& regionList, RenderNamedFlowFragment* renderNamedFlowFragment) { if (regionList.isEmpty()) - regionList.add(renderRegion); + regionList.add(renderNamedFlowFragment); else { - // Find the first region "greater" than renderRegion. + // Find the first region "greater" than renderNamedFlowFragment. auto it = regionList.begin(); - while (it != regionList.end() && !compareRenderRegions(renderRegion, *it)) + while (it != regionList.end() && !compareRenderNamedFlowFragments(renderNamedFlowFragment, downcast<RenderNamedFlowFragment>(*it))) ++it; - regionList.insertBefore(it, renderRegion); + regionList.insertBefore(it, renderNamedFlowFragment); } } -void RenderNamedFlowThread::addRegionToNamedFlowThread(RenderRegion* renderRegion) +void RenderNamedFlowThread::addFragmentToNamedFlowThread(RenderNamedFlowFragment* renderNamedFlowFragment) { - ASSERT(renderRegion); - ASSERT(!renderRegion->isValid()); + ASSERT(renderNamedFlowFragment); + ASSERT(!renderNamedFlowFragment->isValid()); - if (renderRegion->parentNamedFlowThread()) - addDependencyOnFlowThread(renderRegion->parentNamedFlowThread()); + if (renderNamedFlowFragment->parentNamedFlowThread()) + addDependencyOnFlowThread(renderNamedFlowFragment->parentNamedFlowThread()); - renderRegion->setIsValid(true); - addRegionToList(m_regionList, renderRegion); + renderNamedFlowFragment->setIsValid(true); + renderNamedFlowFragment->updateRegionFlags(); + addFragmentToList(m_regionList, renderNamedFlowFragment); - if (m_regionList.first() == renderRegion) + if (m_regionList.first() == renderNamedFlowFragment) updateWritingMode(); } @@ -257,17 +206,18 @@ void RenderNamedFlowThread::addRegionToThread(RenderRegion* renderRegion) ASSERT(renderRegion); ASSERT(!renderRegion->isValid()); + RenderNamedFlowFragment& renderNamedFlowFragment = downcast<RenderNamedFlowFragment>(*renderRegion); resetMarkForDestruction(); - if (renderRegion->parentNamedFlowThread() && renderRegion->parentNamedFlowThread()->dependsOn(this)) { + if (renderNamedFlowFragment.parentNamedFlowThread() && renderNamedFlowFragment.parentNamedFlowThread()->dependsOn(this)) { // The order of invalid regions is irrelevant. - m_invalidRegionList.add(renderRegion); + m_invalidRegionList.add(&renderNamedFlowFragment); // Register ourself to get a notification when the state changes. - renderRegion->parentNamedFlowThread()->m_observerThreadsSet.add(this); + renderNamedFlowFragment.parentNamedFlowThread()->m_observerThreadsSet.add(this); return; } - addRegionToNamedFlowThread(renderRegion); + addFragmentToNamedFlowThread(&renderNamedFlowFragment); invalidateRegions(); } @@ -276,29 +226,27 @@ void RenderNamedFlowThread::removeRegionFromThread(RenderRegion* renderRegion) { ASSERT(renderRegion); - if (renderRegion->parentNamedFlowThread()) { - if (!renderRegion->isValid()) { - ASSERT(m_invalidRegionList.contains(renderRegion)); - m_invalidRegionList.remove(renderRegion); - renderRegion->parentNamedFlowThread()->m_observerThreadsSet.remove(this); + RenderNamedFlowFragment& renderNamedFlowFragment = downcast<RenderNamedFlowFragment>(*renderRegion); + if (renderNamedFlowFragment.parentNamedFlowThread()) { + if (!renderNamedFlowFragment.isValid()) { + ASSERT(m_invalidRegionList.contains(&renderNamedFlowFragment)); + m_invalidRegionList.remove(&renderNamedFlowFragment); + renderNamedFlowFragment.parentNamedFlowThread()->m_observerThreadsSet.remove(this); // No need to invalidate the regions rectangles. The removed region // was not taken into account. Just return here. return; } - removeDependencyOnFlowThread(renderRegion->parentNamedFlowThread()); + removeDependencyOnFlowThread(renderNamedFlowFragment.parentNamedFlowThread()); } - ASSERT(m_regionList.contains(renderRegion)); - bool wasFirst = m_regionList.first() == renderRegion; - m_regionList.remove(renderRegion); + ASSERT(m_regionList.contains(&renderNamedFlowFragment)); + bool wasFirst = m_regionList.first() == &renderNamedFlowFragment; + m_regionList.remove(&renderNamedFlowFragment); if (canBeDestroyed()) setMarkForDestruction(); - // After removing all the regions in the flow the following layout needs to dispatch the regionLayoutUpdate event - if (m_regionList.isEmpty()) - setDispatchRegionLayoutUpdateEvent(true); - else if (wasFirst) + if (!m_regionList.isEmpty() && wasFirst) updateWritingMode(); invalidateRegions(); @@ -310,72 +258,170 @@ void RenderNamedFlowThread::regionChangedWritingMode(RenderRegion* region) updateWritingMode(); } -void RenderNamedFlowThread::computeOversetStateForRegions(LayoutUnit oldClientAfterEdge) -{ - LayoutUnit height = oldClientAfterEdge; - - // FIXME: the visual overflow of middle region (if it is the last one to contain any content in a render flow thread) - // might not be taken into account because the render flow thread height is greater that that regions height + its visual overflow - // because of how computeLogicalHeight is implemented for RenderNamedFlowThread (as a sum of all regions height). - // This means that the middle region will be marked as fit (even if it has visual overflow flowing into the next region) - if (hasRenderOverflow() - && ( (isHorizontalWritingMode() && visualOverflowRect().maxY() > clientBoxRect().maxY()) - || (!isHorizontalWritingMode() && visualOverflowRect().maxX() > clientBoxRect().maxX()))) - height = isHorizontalWritingMode() ? visualOverflowRect().maxY() : visualOverflowRect().maxX(); - - RenderRegion* lastReg = lastRegion(); - for (auto& region : m_regionList) { - LayoutUnit flowMin = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().y() : region->flowThreadPortionRect().x()); - LayoutUnit flowMax = height - (isHorizontalWritingMode() ? region->flowThreadPortionRect().maxY() : region->flowThreadPortionRect().maxX()); - RegionOversetState previousState = region->regionOversetState(); - RegionOversetState state = RegionFit; - if (flowMin <= 0) - state = RegionEmpty; - if (flowMax > 0 && region == lastReg) - state = RegionOverset; - region->setRegionOversetState(state); - // determine whether the NamedFlow object should dispatch a regionLayoutUpdate event - // FIXME: currently it cannot determine whether a region whose regionOverset state remained either "fit" or "overset" has actually - // changed, so it just assumes that the NamedFlow should dispatch the event - if (previousState != state - || state == RegionFit - || state == RegionOverset) - setDispatchRegionLayoutUpdateEvent(true); +LayoutRect RenderNamedFlowThread::decorationsClipRectForBoxInNamedFlowFragment(const RenderBox& box, RenderNamedFlowFragment& fragment) const +{ + LayoutRect visualOverflowRect = fragment.visualOverflowRectForBox(box); + LayoutUnit initialLogicalX = style().isHorizontalWritingMode() ? visualOverflowRect.x() : visualOverflowRect.y(); + + // The visual overflow rect returned by visualOverflowRectForBox is already flipped but the + // RenderRegion::rectFlowPortionForBox method expects it unflipped. + flipForWritingModeLocalCoordinates(visualOverflowRect); + visualOverflowRect = fragment.rectFlowPortionForBox(&box, visualOverflowRect); + + // Now flip it again. + flipForWritingModeLocalCoordinates(visualOverflowRect); + + // Take the scrolled offset of this object's parents into consideration. + ScrollPosition scrollPosition; + RenderBlock* containingBlock = box.containingBlock(); + while (containingBlock && !is<RenderView>(*containingBlock)) { + if (containingBlock->isRenderNamedFlowThread()) { + // We've reached the flow thread, take the scrolled offset of the region into consideration. + ASSERT(containingBlock == this); + scrollPosition += toIntSize(fragment.fragmentContainer().scrollPosition()); + break; + } + + scrollPosition += toIntSize(containingBlock->scrollPosition()); + containingBlock = containingBlock->containingBlock(); + } + + if (!scrollPosition.isZero()) { + if (style().isFlippedBlocksWritingMode()) + scrollPosition = -scrollPosition; + + visualOverflowRect.inflateX(scrollPosition.x()); + visualOverflowRect.inflateY(scrollPosition.y()); + } + + // Layers are in physical coordinates so the origin must be moved to the physical top-left of the flowthread. + if (style().isFlippedBlocksWritingMode()) { + if (style().isHorizontalWritingMode()) + visualOverflowRect.moveBy(LayoutPoint(0, height())); + else + visualOverflowRect.moveBy(LayoutPoint(width(), 0)); + } + + const RenderBox* iterBox = &box; + while (iterBox && iterBox != this) { + RenderBlock* containerBlock = iterBox->containingBlock(); + + // FIXME: This doesn't work properly with flipped writing modes. + // https://bugs.webkit.org/show_bug.cgi?id=125149 + if (iterBox->isPositioned()) { + // For positioned elements, just use the layer's absolute bounding box. + visualOverflowRect.moveBy(iterBox->layer()->absoluteBoundingBox().location()); + break; + } + + LayoutRect currentBoxRect = iterBox->frameRect(); + if (iterBox->style().isFlippedBlocksWritingMode()) { + if (iterBox->style().isHorizontalWritingMode()) + currentBoxRect.setY(currentBoxRect.height() - currentBoxRect.maxY()); + else + currentBoxRect.setX(currentBoxRect.width() - currentBoxRect.maxX()); + } + + if (containerBlock->style().writingMode() != iterBox->style().writingMode()) + iterBox->flipForWritingMode(currentBoxRect); + + visualOverflowRect.moveBy(currentBoxRect.location()); + iterBox = containerBlock; + } + + // Since the purpose of this method is to make sure the borders of a fragmented + // element don't overflow the region in the fragmentation direction, there's no + // point in restricting the clipping rect on the logical X axis. + // This also saves us the trouble of handling percent-based widths and margins + // since the absolute bounding box of a positioned element would not contain + // the correct coordinates relative to the region we're interested in, but rather + // relative to the actual flow thread. + if (style().isHorizontalWritingMode()) { + if (initialLogicalX < visualOverflowRect.x()) + visualOverflowRect.shiftXEdgeTo(initialLogicalX); + if (visualOverflowRect.width() < frameRect().width()) + visualOverflowRect.setWidth(frameRect().width()); + } else { + if (initialLogicalX < visualOverflowRect.y()) + visualOverflowRect.shiftYEdgeTo(initialLogicalX); + if (visualOverflowRect.height() < frameRect().height()) + visualOverflowRect.setHeight(frameRect().height()); + } + + return visualOverflowRect; +} + +RenderBlock* RenderNamedFlowThread::fragmentFromRenderBoxAsRenderBlock(RenderBox* renderBox, const IntPoint& absolutePoint, const RenderBox& flowedBox) +{ + return downcast<RenderNamedFlowThread>(*renderBox).fragmentFromAbsolutePointAndBox(absolutePoint, flowedBox); +} + +RenderNamedFlowFragment* RenderNamedFlowThread::fragmentFromAbsolutePointAndBox(const IntPoint& absolutePoint, const RenderBox& flowedBox) +{ + RenderRegion* startRegion = nullptr; + RenderRegion* endRegion = nullptr; + if (!getRegionRangeForBox(&flowedBox, startRegion, endRegion)) + return nullptr; + + for (auto iter = m_regionList.find(startRegion), end = m_regionList.end(); iter != end; ++iter) { + auto& fragment = downcast<RenderNamedFlowFragment>(**iter); + RenderBlockFlow& fragmentContainer = fragment.fragmentContainer(); + IntRect fragmentAbsoluteRect(roundedIntPoint(fragmentContainer.localToAbsolute()), roundedIntSize(fragmentContainer.paddingBoxRect().size())); + if (fragmentAbsoluteRect.contains(absolutePoint)) + return &fragment; - if (previousState != state) - setDispatchRegionOversetChangeEvent(true); + if (&fragment == endRegion) + break; } + return nullptr; +} + +void RenderNamedFlowThread::computeOverflow(LayoutUnit oldClientAfterEdge, bool recomputeFloats) +{ + RenderFlowThread::computeOverflow(oldClientAfterEdge, recomputeFloats); + + m_flowContentBottom = oldClientAfterEdge; +} + +void RenderNamedFlowThread::layout() +{ + RenderFlowThread::layout(); + // If the number of regions has changed since we last computed the overset property, schedule the regionOversetChange event. if (previousRegionCountChanged()) { setDispatchRegionOversetChangeEvent(true); updatePreviousRegionCount(); } +} + +void RenderNamedFlowThread::dispatchNamedFlowEvents() +{ + ASSERT(inFinalLayoutPhase()); - // With the regions overflow state computed we can also set the overset flag for the named flow. - // If there are no valid regions in the chain, overset is true. - m_overset = lastReg ? lastReg->regionOversetState() == RegionOverset : true; + dispatchRegionOversetChangeEventIfNeeded(); } void RenderNamedFlowThread::checkInvalidRegions() { - Vector<RenderRegion*> newValidRegions; + Vector<RenderNamedFlowFragment*> newValidFragments; for (auto& region : m_invalidRegionList) { + auto& namedFlowFragment = downcast<RenderNamedFlowFragment>(*region); // The only reason a region would be invalid is because it has a parent flow thread. - ASSERT(!region->isValid() && region->parentNamedFlowThread()); - if (region->parentNamedFlowThread()->dependsOn(this)) + ASSERT(!namedFlowFragment.isValid() && namedFlowFragment.parentNamedFlowThread()); + if (namedFlowFragment.parentNamedFlowThread()->dependsOn(this)) continue; - newValidRegions.append(region); + newValidFragments.append(&namedFlowFragment); } - for (auto& region : newValidRegions) { - m_invalidRegionList.remove(region); - region->parentNamedFlowThread()->m_observerThreadsSet.remove(this); - addRegionToNamedFlowThread(region); + for (auto& namedFlowFragment : newValidFragments) { + m_invalidRegionList.remove(namedFlowFragment); + namedFlowFragment->parentNamedFlowThread()->m_observerThreadsSet.remove(this); + addFragmentToNamedFlowThread(namedFlowFragment); } - if (!newValidRegions.isEmpty()) + if (!newValidFragments.isEmpty()) invalidateRegions(); if (m_observerThreadsSet.isEmpty()) @@ -427,37 +473,37 @@ void RenderNamedFlowThread::registerNamedFlowContentElement(Element& contentElem { ASSERT(&contentElement.document() == &document()); - contentElement.setInNamedFlow(); + contentElement.setIsNamedFlowContentElement(); resetMarkForDestruction(); // Find the first content node following the new content node. for (auto& element : m_contentElements) { - unsigned short position = contentElement.compareDocumentPosition(element); + unsigned short position = contentElement.compareDocumentPosition(*element); if (position & Node::DOCUMENT_POSITION_FOLLOWING) { m_contentElements.insertBefore(element, &contentElement); - InspectorInstrumentation::didRegisterNamedFlowContentElement(&document(), m_namedFlow.get(), &contentElement, element); + InspectorInstrumentation::didRegisterNamedFlowContentElement(document(), namedFlow(), contentElement, element); return; } } m_contentElements.add(&contentElement); - InspectorInstrumentation::didRegisterNamedFlowContentElement(&document(), m_namedFlow.get(), &contentElement); + InspectorInstrumentation::didRegisterNamedFlowContentElement(document(), namedFlow(), contentElement); } void RenderNamedFlowThread::unregisterNamedFlowContentElement(Element& contentElement) { ASSERT(m_contentElements.contains(&contentElement)); - ASSERT(contentElement.inNamedFlow()); + ASSERT(contentElement.isNamedFlowContentElement()); ASSERT(&contentElement.document() == &document()); - contentElement.clearInNamedFlow(); + contentElement.clearIsNamedFlowContentElement(); m_contentElements.remove(&contentElement); if (canBeDestroyed()) setMarkForDestruction(); - InspectorInstrumentation::didUnregisterNamedFlowContentElement(&document(), m_namedFlow.get(), &contentElement); + InspectorInstrumentation::didUnregisterNamedFlowContentElement(document(), namedFlow(), contentElement); } bool RenderNamedFlowThread::hasContentElement(Element& contentElement) const @@ -467,7 +513,7 @@ bool RenderNamedFlowThread::hasContentElement(Element& contentElement) const const AtomicString& RenderNamedFlowThread::flowThreadName() const { - return m_namedFlow->name(); + return namedFlow().name(); } bool RenderNamedFlowThread::isChildAllowed(const RenderObject& child, const RenderStyle& style) const @@ -475,69 +521,54 @@ bool RenderNamedFlowThread::isChildAllowed(const RenderObject& child, const Rend if (!child.node()) return true; - ASSERT(child.node()->isElementNode()); + ASSERT(is<Element>(*child.node())); - Node* originalParent = NodeRenderingTraversal::parent(child.node()); - if (!originalParent || !originalParent->isElementNode() || !originalParent->renderer()) + auto* originalParent = composedTreeAncestors(*child.node()).first(); + if (!originalParent || !originalParent->renderer()) return true; - return toElement(originalParent)->renderer()->isChildAllowed(child, style); + return originalParent->renderer()->isChildAllowed(child, style); } -void RenderNamedFlowThread::dispatchRegionLayoutUpdateEvent() +void RenderNamedFlowThread::dispatchRegionOversetChangeEventIfNeeded() { - RenderFlowThread::dispatchRegionLayoutUpdateEvent(); - InspectorInstrumentation::didUpdateRegionLayout(&document(), m_namedFlow.get()); - - if (!m_regionLayoutUpdateEventTimer.isActive() && m_namedFlow->hasEventListeners()) - m_regionLayoutUpdateEventTimer.startOneShot(0); -} + if (!m_dispatchRegionOversetChangeEvent) + return; -void RenderNamedFlowThread::dispatchRegionOversetChangeEvent() -{ - RenderFlowThread::dispatchRegionOversetChangeEvent(); - InspectorInstrumentation::didChangeRegionOverset(&document(), m_namedFlow.get()); + m_dispatchRegionOversetChangeEvent = false; + InspectorInstrumentation::didChangeRegionOverset(document(), namedFlow()); - if (!m_regionOversetChangeEventTimer.isActive() && m_namedFlow->hasEventListeners()) + if (!m_regionOversetChangeEventTimer.isActive() && namedFlow().hasEventListeners()) m_regionOversetChangeEventTimer.startOneShot(0); } -void RenderNamedFlowThread::regionLayoutUpdateEventTimerFired(Timer<RenderNamedFlowThread>&) +void RenderNamedFlowThread::regionOversetChangeEventTimerFired() { - ASSERT(m_namedFlow); - - m_namedFlow->dispatchRegionLayoutUpdateEvent(); -} - -void RenderNamedFlowThread::regionOversetChangeEventTimerFired(Timer<RenderNamedFlowThread>&) -{ - ASSERT(m_namedFlow); - - m_namedFlow->dispatchRegionOversetChangeEvent(); + namedFlow().dispatchRegionOversetChangeEvent(); } void RenderNamedFlowThread::setMarkForDestruction() { - if (m_namedFlow->flowState() == WebKitNamedFlow::FlowStateNull) + if (namedFlow().flowState() == WebKitNamedFlow::FlowStateNull) return; - m_namedFlow->setRenderer(0); + namedFlow().setRenderer(nullptr); // After this call ends, the renderer can be safely destroyed. // 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. } void RenderNamedFlowThread::resetMarkForDestruction() { - if (m_namedFlow->flowState() == WebKitNamedFlow::FlowStateCreated) + if (namedFlow().flowState() == WebKitNamedFlow::FlowStateCreated) return; - m_namedFlow->setRenderer(this); + namedFlow().setRenderer(this); } bool RenderNamedFlowThread::isMarkedForDestruction() const { // Flow threads in the "NULL" state can be destroyed. - return m_namedFlow->flowState() == WebKitNamedFlow::FlowStateNull; + return namedFlow().flowState() == WebKitNamedFlow::FlowStateNull; } static bool isContainedInElements(const Vector<Element*>& others, Element* element) @@ -559,34 +590,32 @@ static bool boxIntersectsRegion(LayoutUnit logicalTopForBox, LayoutUnit logicalB } // Retrieve the next node to be visited while computing the ranges inside a region. -static Node* nextNodeInsideContentElement(const Node* currNode, const Element* contentElement) +static Node* nextNodeInsideContentElement(const Node& currNode, const Element* contentElement) { - ASSERT(currNode); - ASSERT(contentElement && contentElement->inNamedFlow()); + ASSERT(contentElement && contentElement->isNamedFlowContentElement()); -#if ENABLE(SVG) - if (currNode->renderer() && currNode->renderer()->isSVGRoot()) + if (currNode.renderer() && currNode.renderer()->isSVGRoot()) return NodeTraversal::nextSkippingChildren(currNode, contentElement); -#endif + return NodeTraversal::next(currNode, contentElement); } -void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range>>& rangeObjects, const RenderRegion* region) const +void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range>>& rangeObjects, const RenderNamedFlowFragment* namedFlowFragment) const { LayoutUnit logicalTopForRegion; LayoutUnit logicalBottomForRegion; // extend the first region top to contain everything up to its logical height - if (region->isFirstRegion()) + if (namedFlowFragment->isFirstRegion()) logicalTopForRegion = LayoutUnit::min(); else - logicalTopForRegion = region->logicalTopForFlowThreadContent(); + logicalTopForRegion = namedFlowFragment->logicalTopForFlowThreadContent(); // extend the last region to contain everything above its y() - if (region->isLastRegion()) + if (namedFlowFragment->isLastRegion()) logicalBottomForRegion = LayoutUnit::max(); else - logicalBottomForRegion = region->logicalBottomForFlowThreadContent(); + logicalBottomForRegion = namedFlowFragment->logicalBottomForFlowThreadContent(); Vector<Element*> elements; // eliminate the contentElements that are descendants of other contentElements @@ -604,23 +633,27 @@ void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range>>& rangeObjects, const bool startsAboveRegion = true; bool endsBelowRegion = true; bool skipOverOutsideNodes = false; - Node* lastEndNode = 0; + Node* lastEndNode = nullptr; - for (Node* node = contentElement; node; node = nextNodeInsideContentElement(node, contentElement)) { + for (Node* node = contentElement; node; node = nextNodeInsideContentElement(*node, contentElement)) { RenderObject* renderer = node->renderer(); if (!renderer) continue; LayoutRect boundingBox; - if (renderer->isRenderInline()) - boundingBox = toRenderInline(renderer)->linesBoundingBox(); - else if (renderer->isText()) - boundingBox = toRenderText(renderer)->linesBoundingBox(); - else { - boundingBox = toRenderBox(renderer)->frameRect(); - if (toRenderBox(renderer)->isRelPositioned()) - boundingBox.move(toRenderBox(renderer)->relativePositionLogicalOffset()); - } + if (is<RenderInline>(*renderer)) + boundingBox = downcast<RenderInline>(*renderer).linesBoundingBox(); + else if (is<RenderText>(*renderer)) + boundingBox = downcast<RenderText>(*renderer).linesBoundingBox(); + else if (is<RenderLineBreak>(*renderer)) + boundingBox = downcast<RenderLineBreak>(*renderer).linesBoundingBox(); + else if (is<RenderBox>(*renderer)) { + auto& renderBox = downcast<RenderBox>(*renderer); + boundingBox = renderBox.frameRect(); + if (renderBox.isRelPositioned()) + boundingBox.move(renderBox.relativePositionLogicalOffset()); + } else + continue; LayoutUnit offsetTop = renderer->containingBlock()->offsetFromLogicalTopOfFirstPage(); const LayoutPoint logicalOffsetFromTop(isHorizontalWritingMode() ? LayoutUnit() : offsetTop, @@ -628,8 +661,8 @@ void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range>>& rangeObjects, const boundingBox.moveBy(logicalOffsetFromTop); - LayoutUnit logicalTopForRenderer = region->logicalTopOfFlowThreadContentRect(boundingBox); - LayoutUnit logicalBottomForRenderer = region->logicalBottomOfFlowThreadContentRect(boundingBox); + LayoutUnit logicalTopForRenderer = namedFlowFragment->logicalTopOfFlowThreadContentRect(boundingBox); + LayoutUnit logicalBottomForRenderer = namedFlowFragment->logicalBottomOfFlowThreadContentRect(boundingBox); // if the bounding box of the current element doesn't intersect the region box // close the current range only if the start element began inside the region, @@ -637,43 +670,47 @@ void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range>>& rangeObjects, const if (!boxIntersectsRegion(logicalTopForRenderer, logicalBottomForRenderer, logicalTopForRegion, logicalBottomForRegion)) { if (foundStartPosition) { if (!startsAboveRegion) { - if (range->intersectsNode(node, IGNORE_EXCEPTION)) - range->setEndBefore(node, IGNORE_EXCEPTION); - rangeObjects.append(range->cloneRange(IGNORE_EXCEPTION)); + auto intersectsResult = range->intersectsNode(*node); + if (!intersectsResult.hasException() && intersectsResult.releaseReturnValue()) + range->setEndBefore(*node); + rangeObjects.append(range->cloneRange()); range = Range::create(contentElement->document()); startsAboveRegion = true; } else skipOverOutsideNodes = true; } if (skipOverOutsideNodes) - range->setStartAfter(node, IGNORE_EXCEPTION); + range->setStartAfter(*node); foundStartPosition = false; continue; } // start position if (logicalTopForRenderer < logicalTopForRegion && startsAboveRegion) { - if (renderer->isText()) { // Text crosses region top + if (is<RenderText>(*renderer)) { + // Text crosses region top // for Text elements, just find the last textbox that is contained inside the region and use its start() offset as start position - RenderText* textRenderer = toRenderText(renderer); - for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + RenderText& textRenderer = downcast<RenderText>(*renderer); + for (InlineTextBox* box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) { if (offsetTop + box->logicalBottom() < logicalTopForRegion) continue; - range->setStart(Position(toText(node), box->start())); + range->setStart(Position(downcast<Text>(node), box->start())); startsAboveRegion = false; break; } - } else { // node crosses region top + } else { + // node crosses region top // for all elements, except Text, just set the start position to be before their children startsAboveRegion = true; range->setStart(Position(node, Position::PositionIsBeforeChildren)); } - } else { // node starts inside region + } else { + // node starts inside region // 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 // the range is closed. if (startsAboveRegion) { startsAboveRegion = false; - range->setStartBefore(node, IGNORE_EXCEPTION); + range->setStartBefore(*node); } } skipOverOutsideNodes = false; @@ -682,32 +719,35 @@ void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range>>& rangeObjects, const // end position if (logicalBottomForRegion < logicalBottomForRenderer && (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode)))) { // 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 - if (renderer->isText()) { // Text crosses region bottom - RenderText* textRenderer = toRenderText(renderer); - InlineTextBox* lastBox = 0; - for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + if (is<RenderText>(*renderer)) { + // Text crosses region bottom + RenderText& textRenderer = downcast<RenderText>(*renderer); + InlineTextBox* lastBox = nullptr; + for (InlineTextBox* box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) { if ((offsetTop + box->logicalTop()) < logicalBottomForRegion) { lastBox = box; continue; } ASSERT(lastBox); if (lastBox) - range->setEnd(Position(toText(node), lastBox->start() + lastBox->len())); + range->setEnd(Position(downcast<Text>(node), lastBox->start() + lastBox->len())); break; } endsBelowRegion = false; lastEndNode = node; - } else { // node crosses region bottom + } else { + // node crosses region bottom // for all elements, except Text, just set the start position to be after their children range->setEnd(Position(node, Position::PositionIsAfterChildren)); endsBelowRegion = true; lastEndNode = node; } - } else { // node ends inside region + } else { + // node ends inside region // for elements that ends inside the region, set the end position to be after them // allow this end position to be changed only by other elements that are not descendants of the current end node if (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode))) { - range->setEndAfter(node, IGNORE_EXCEPTION); + range->setEndAfter(*node); endsBelowRegion = false; lastEndNode = node; } @@ -718,7 +758,13 @@ void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range>>& rangeObjects, const } } -#if USE(ACCELERATED_COMPOSITING) +void RenderNamedFlowThread::applyBreakAfterContent(LayoutUnit clientHeight) +{ + // Simulate a region break at height. If it points inside an auto logical height region, + // then it may determine the region computed autoheight. + addForcedRegionBreak(this, clientHeight, this, false); +} + bool RenderNamedFlowThread::collectsGraphicsLayersUnderRegions() const { // We only need to map layers to regions for named flow threads. @@ -727,15 +773,13 @@ bool RenderNamedFlowThread::collectsGraphicsLayersUnderRegions() const return true; } -#endif // Check if the content is flown into at least a region with region styling rules. void RenderNamedFlowThread::checkRegionsWithStyling() { bool hasRegionsWithStyling = false; for (const auto& region : m_regionList) { - const RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(region); - if (namedFlowFragment->hasCustomRegionStyle()) { + if (downcast<RenderNamedFlowFragment>(*region).hasCustomRegionStyle()) { hasRegionsWithStyling = true; break; } @@ -743,20 +787,38 @@ void RenderNamedFlowThread::checkRegionsWithStyling() m_hasRegionsWithStyling = hasRegionsWithStyling; } -void RenderNamedFlowThread::clearRenderObjectCustomStyle(const RenderObject* object) +void RenderNamedFlowThread::clearRenderObjectCustomStyle(const RenderElement& object) { // Clear the styles for the object in the regions. // 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. - for (auto& region : m_regionList) { - RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(region); - namedFlowFragment->clearObjectStyleInRegion(object); - } + for (auto& region : m_regionList) + downcast<RenderNamedFlowFragment>(*region).clearObjectStyleInRegion(object); } -void RenderNamedFlowThread::removeFlowChildInfo(RenderObject* child) +void RenderNamedFlowThread::removeFlowChildInfo(RenderElement& child) { RenderFlowThread::removeFlowChildInfo(child); clearRenderObjectCustomStyle(child); } +bool RenderNamedFlowThread::absoluteQuadsForBox(Vector<FloatQuad>& quads, bool* wasFixed, const RenderBox* renderer, float localTop, float localBottom) const +{ + RenderRegion* startRegion = nullptr; + RenderRegion* endRegion = nullptr; + // If the box doesn't have a range, we don't know how it is fragmented so fallback to the default behaviour. + if (!computedRegionRangeForBox(renderer, startRegion, endRegion)) + return false; + + for (auto iter = m_regionList.find(startRegion), end = m_regionList.end(); iter != end; ++iter) { + RenderRegion* region = *iter; + + region->absoluteQuadsForBoxInRegion(quads, wasFixed, renderer, localTop, localBottom); + + if (region == endRegion) + break; + } + + return true; +} + } |