summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/RenderNamedFlowThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/RenderNamedFlowThread.cpp')
-rw-r--r--Source/WebCore/rendering/RenderNamedFlowThread.cpp580
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;
+}
+
}