RenderNamedFlowThread.cpp [plain text]
#include "config.h"
#include "RenderNamedFlowThread.h"
#include "ComposedTreeAncestorIterator.h"
#include "FlowThreadController.h"
#include "InlineTextBox.h"
#include "InspectorInstrumentation.h"
#include "NodeTraversal.h"
#include "Position.h"
#include "Range.h"
#include "RenderInline.h"
#include "RenderLayer.h"
#include "RenderLineBreak.h"
#include "RenderNamedFlowFragment.h"
#include "RenderText.h"
#include "RenderView.h"
#include "ShadowRoot.h"
#include "Text.h"
#include "WebKitNamedFlow.h"
namespace WebCore {
RenderNamedFlowThread::RenderNamedFlowThread(Document& document, RenderStyle&& style, Ref<WebKitNamedFlow>&& namedFlow)
: RenderFlowThread(document, WTFMove(style))
, m_hasRegionsWithStyling(false)
, m_dispatchRegionOversetChangeEvent(false)
, m_namedFlow(WTFMove(namedFlow))
, m_regionOversetChangeEventTimer(*this, &RenderNamedFlowThread::regionOversetChangeEventTimerFired)
{
}
RenderNamedFlowThread::~RenderNamedFlowThread()
{
clearContentElements();
setMarkForDestruction();
}
const char* RenderNamedFlowThread::renderName() const
{
return "RenderNamedFlowThread";
}
void RenderNamedFlowThread::clearContentElements()
{
for (auto& contentElement : m_contentElements) {
ASSERT(contentElement);
ASSERT(contentElement->isNamedFlowContentElement());
ASSERT(&contentElement->document() == &document());
contentElement->clearIsNamedFlowContentElement();
}
m_contentElements.clear();
}
void RenderNamedFlowThread::updateWritingMode()
{
auto* firstFragment = downcast<RenderNamedFlowFragment>(m_regionList.first());
if (!firstFragment)
return;
if (style().writingMode() == firstFragment->style().writingMode())
return;
auto newStyle = RenderStyle::clone(style());
newStyle.setWritingMode(firstFragment->style().writingMode());
setStyle(WTFMove(newStyle));
}
bool RenderNamedFlowThread::dependsOn(RenderNamedFlowThread* otherRenderFlowThread) const
{
if (m_layoutBeforeThreadsSet.contains(otherRenderFlowThread))
return true;
for (const auto& beforeFlowThreadPair : m_layoutBeforeThreadsSet) {
const auto& beforeFlowThread = beforeFlowThreadPair.key;
if (beforeFlowThread->dependsOn(otherRenderFlowThread))
return true;
}
return false;
}
static bool compareRenderNamedFlowFragments(const RenderNamedFlowFragment* firstFragment, const RenderNamedFlowFragment* secondFragment)
{
ASSERT(firstFragment);
ASSERT(secondFragment);
ASSERT(firstFragment->generatingElement());
ASSERT(secondFragment->generatingElement());
if (firstFragment->generatingElement() != secondFragment->generatingElement()) {
unsigned short position = firstFragment->generatingElement()->compareDocumentPosition(*secondFragment->generatingElement());
if (position & Node::DOCUMENT_POSITION_CONTAINED_BY) {
ASSERT(secondFragment->style().styleType() == NOPSEUDO);
return firstFragment->style().styleType() == BEFORE;
}
if (position & Node::DOCUMENT_POSITION_CONTAINS) {
ASSERT(firstFragment->style().styleType() == NOPSEUDO);
return secondFragment->style().styleType() == AFTER;
}
return (position & Node::DOCUMENT_POSITION_FOLLOWING);
}
switch (firstFragment->style().styleType()) {
case BEFORE:
return true;
case AFTER:
return false;
case NOPSEUDO:
return firstFragment->style().styleType() == AFTER;
default:
break;
}
ASSERT_NOT_REACHED();
return true;
}
static void addFragmentToList(RenderRegionList& regionList, RenderNamedFlowFragment* renderNamedFlowFragment)
{
if (regionList.isEmpty())
regionList.add(renderNamedFlowFragment);
else {
auto it = regionList.begin();
while (it != regionList.end() && !compareRenderNamedFlowFragments(renderNamedFlowFragment, downcast<RenderNamedFlowFragment>(*it)))
++it;
regionList.insertBefore(it, renderNamedFlowFragment);
}
}
void RenderNamedFlowThread::addFragmentToNamedFlowThread(RenderNamedFlowFragment* renderNamedFlowFragment)
{
ASSERT(renderNamedFlowFragment);
ASSERT(!renderNamedFlowFragment->isValid());
if (renderNamedFlowFragment->parentNamedFlowThread())
addDependencyOnFlowThread(renderNamedFlowFragment->parentNamedFlowThread());
renderNamedFlowFragment->setIsValid(true);
renderNamedFlowFragment->updateRegionFlags();
addFragmentToList(m_regionList, renderNamedFlowFragment);
if (m_regionList.first() == renderNamedFlowFragment)
updateWritingMode();
}
void RenderNamedFlowThread::addRegionToThread(RenderRegion* renderRegion)
{
ASSERT(renderRegion);
ASSERT(!renderRegion->isValid());
RenderNamedFlowFragment& renderNamedFlowFragment = downcast<RenderNamedFlowFragment>(*renderRegion);
resetMarkForDestruction();
if (renderNamedFlowFragment.parentNamedFlowThread() && renderNamedFlowFragment.parentNamedFlowThread()->dependsOn(this)) {
m_invalidRegionList.add(&renderNamedFlowFragment);
renderNamedFlowFragment.parentNamedFlowThread()->m_observerThreadsSet.add(this);
return;
}
addFragmentToNamedFlowThread(&renderNamedFlowFragment);
invalidateRegions();
}
void RenderNamedFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
{
ASSERT(renderRegion);
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);
return;
}
removeDependencyOnFlowThread(renderNamedFlowFragment.parentNamedFlowThread());
}
ASSERT(m_regionList.contains(&renderNamedFlowFragment));
bool wasFirst = m_regionList.first() == &renderNamedFlowFragment;
m_regionList.remove(&renderNamedFlowFragment);
if (canBeDestroyed())
setMarkForDestruction();
if (!m_regionList.isEmpty() && wasFirst)
updateWritingMode();
invalidateRegions();
}
void RenderNamedFlowThread::regionChangedWritingMode(RenderRegion* region)
{
if (m_regionList.first() == region)
updateWritingMode();
}
LayoutRect RenderNamedFlowThread::decorationsClipRectForBoxInNamedFlowFragment(const RenderBox& box, RenderNamedFlowFragment& fragment) const
{
LayoutRect visualOverflowRect = fragment.visualOverflowRectForBox(box);
LayoutUnit initialLogicalX = style().isHorizontalWritingMode() ? visualOverflowRect.x() : visualOverflowRect.y();
flipForWritingModeLocalCoordinates(visualOverflowRect);
visualOverflowRect = fragment.rectFlowPortionForBox(&box, visualOverflowRect);
flipForWritingModeLocalCoordinates(visualOverflowRect);
ScrollPosition scrollPosition;
RenderBlock* containingBlock = box.containingBlock();
while (containingBlock && !is<RenderView>(*containingBlock)) {
if (containingBlock->isRenderNamedFlowThread()) {
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());
}
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();
if (iterBox->isPositioned()) {
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;
}
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 (&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 (previousRegionCountChanged()) {
setDispatchRegionOversetChangeEvent(true);
updatePreviousRegionCount();
}
}
void RenderNamedFlowThread::dispatchNamedFlowEvents()
{
ASSERT(inFinalLayoutPhase());
dispatchRegionOversetChangeEventIfNeeded();
}
void RenderNamedFlowThread::checkInvalidRegions()
{
Vector<RenderNamedFlowFragment*> newValidFragments;
for (auto& region : m_invalidRegionList) {
auto& namedFlowFragment = downcast<RenderNamedFlowFragment>(*region);
ASSERT(!namedFlowFragment.isValid() && namedFlowFragment.parentNamedFlowThread());
if (namedFlowFragment.parentNamedFlowThread()->dependsOn(this))
continue;
newValidFragments.append(&namedFlowFragment);
}
for (auto& namedFlowFragment : newValidFragments) {
m_invalidRegionList.remove(namedFlowFragment);
namedFlowFragment->parentNamedFlowThread()->m_observerThreadsSet.remove(this);
addFragmentToNamedFlowThread(namedFlowFragment);
}
if (!newValidFragments.isEmpty())
invalidateRegions();
if (m_observerThreadsSet.isEmpty())
return;
Vector<RenderNamedFlowThread*> observers;
copyToVector(m_observerThreadsSet, observers);
for (auto& flowThread : observers)
flowThread->checkInvalidRegions();
}
void RenderNamedFlowThread::addDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread)
{
RenderNamedFlowThreadCountedSet::AddResult result = m_layoutBeforeThreadsSet.add(otherFlowThread);
if (result.isNewEntry) {
view().flowThreadController().setIsRenderNamedFlowThreadOrderDirty(true);
}
}
void RenderNamedFlowThread::removeDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread)
{
bool removed = m_layoutBeforeThreadsSet.remove(otherFlowThread);
if (removed) {
checkInvalidRegions();
view().flowThreadController().setIsRenderNamedFlowThreadOrderDirty(true);
}
}
void RenderNamedFlowThread::pushDependencies(RenderNamedFlowThreadList& list)
{
for (auto& flowThreadPair : m_layoutBeforeThreadsSet) {
auto& flowThread = flowThreadPair.key;
if (list.contains(flowThread))
continue;
flowThread->pushDependencies(list);
list.add(flowThread);
}
}
void RenderNamedFlowThread::registerNamedFlowContentElement(Element& contentElement)
{
ASSERT(&contentElement.document() == &document());
contentElement.setIsNamedFlowContentElement();
resetMarkForDestruction();
for (auto& element : m_contentElements) {
unsigned short position = contentElement.compareDocumentPosition(*element);
if (position & Node::DOCUMENT_POSITION_FOLLOWING) {
m_contentElements.insertBefore(element, &contentElement);
InspectorInstrumentation::didRegisterNamedFlowContentElement(document(), namedFlow(), contentElement, element);
return;
}
}
m_contentElements.add(&contentElement);
InspectorInstrumentation::didRegisterNamedFlowContentElement(document(), namedFlow(), contentElement);
}
void RenderNamedFlowThread::unregisterNamedFlowContentElement(Element& contentElement)
{
ASSERT(m_contentElements.contains(&contentElement));
ASSERT(contentElement.isNamedFlowContentElement());
ASSERT(&contentElement.document() == &document());
contentElement.clearIsNamedFlowContentElement();
m_contentElements.remove(&contentElement);
if (canBeDestroyed())
setMarkForDestruction();
InspectorInstrumentation::didUnregisterNamedFlowContentElement(document(), namedFlow(), contentElement);
}
bool RenderNamedFlowThread::hasContentElement(Element& contentElement) const
{
return m_contentElements.contains(&contentElement);
}
const AtomicString& RenderNamedFlowThread::flowThreadName() const
{
return namedFlow().name();
}
bool RenderNamedFlowThread::isChildAllowed(const RenderObject& child, const RenderStyle& style) const
{
if (!child.node())
return true;
ASSERT(is<Element>(*child.node()));
auto* originalParent = composedTreeAncestors(*child.node()).first();
if (!originalParent || !originalParent->renderer())
return true;
return originalParent->renderer()->isChildAllowed(child, style);
}
void RenderNamedFlowThread::dispatchRegionOversetChangeEventIfNeeded()
{
if (!m_dispatchRegionOversetChangeEvent)
return;
m_dispatchRegionOversetChangeEvent = false;
InspectorInstrumentation::didChangeRegionOverset(document(), namedFlow());
if (!m_regionOversetChangeEventTimer.isActive() && namedFlow().hasEventListeners())
m_regionOversetChangeEventTimer.startOneShot(0);
}
void RenderNamedFlowThread::regionOversetChangeEventTimerFired()
{
namedFlow().dispatchRegionOversetChangeEvent();
}
void RenderNamedFlowThread::setMarkForDestruction()
{
if (namedFlow().flowState() == WebKitNamedFlow::FlowStateNull)
return;
namedFlow().setRenderer(nullptr);
}
void RenderNamedFlowThread::resetMarkForDestruction()
{
if (namedFlow().flowState() == WebKitNamedFlow::FlowStateCreated)
return;
namedFlow().setRenderer(this);
}
bool RenderNamedFlowThread::isMarkedForDestruction() const
{
return namedFlow().flowState() == WebKitNamedFlow::FlowStateNull;
}
static bool isContainedInElements(const Vector<Element*>& others, Element* element)
{
for (auto& other : others) {
if (other->contains(element))
return true;
}
return false;
}
static bool boxIntersectsRegion(LayoutUnit logicalTopForBox, LayoutUnit logicalBottomForBox, LayoutUnit logicalTopForRegion, LayoutUnit logicalBottomForRegion)
{
bool regionIsEmpty = logicalBottomForRegion != LayoutUnit::max() && logicalTopForRegion != LayoutUnit::min()
&& (logicalBottomForRegion - logicalTopForRegion) <= 0;
return (logicalBottomForBox - logicalTopForBox) > 0
&& !regionIsEmpty
&& logicalTopForBox < logicalBottomForRegion && logicalTopForRegion < logicalBottomForBox;
}
static Node* nextNodeInsideContentElement(const Node& currNode, const Element* contentElement)
{
ASSERT(contentElement && contentElement->isNamedFlowContentElement());
if (currNode.renderer() && currNode.renderer()->isSVGRoot())
return NodeTraversal::nextSkippingChildren(currNode, contentElement);
return NodeTraversal::next(currNode, contentElement);
}
void RenderNamedFlowThread::getRanges(Vector<RefPtr<Range>>& rangeObjects, const RenderNamedFlowFragment* namedFlowFragment) const
{
LayoutUnit logicalTopForRegion;
LayoutUnit logicalBottomForRegion;
if (namedFlowFragment->isFirstRegion())
logicalTopForRegion = LayoutUnit::min();
else
logicalTopForRegion = namedFlowFragment->logicalTopForFlowThreadContent();
if (namedFlowFragment->isLastRegion())
logicalBottomForRegion = LayoutUnit::max();
else
logicalBottomForRegion = namedFlowFragment->logicalBottomForFlowThreadContent();
Vector<Element*> elements;
for (auto& element : contentElements()) {
if (!isContainedInElements(elements, element))
elements.append(element);
}
for (auto& contentElement : elements) {
if (!contentElement->renderer())
continue;
RefPtr<Range> range = Range::create(contentElement->document());
bool foundStartPosition = false;
bool startsAboveRegion = true;
bool endsBelowRegion = true;
bool skipOverOutsideNodes = false;
Node* lastEndNode = nullptr;
for (Node* node = contentElement; node; node = nextNodeInsideContentElement(*node, contentElement)) {
RenderObject* renderer = node->renderer();
if (!renderer)
continue;
LayoutRect boundingBox;
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,
isHorizontalWritingMode() ? offsetTop : LayoutUnit());
boundingBox.moveBy(logicalOffsetFromTop);
LayoutUnit logicalTopForRenderer = namedFlowFragment->logicalTopOfFlowThreadContentRect(boundingBox);
LayoutUnit logicalBottomForRenderer = namedFlowFragment->logicalBottomOfFlowThreadContentRect(boundingBox);
if (!boxIntersectsRegion(logicalTopForRenderer, logicalBottomForRenderer, logicalTopForRegion, logicalBottomForRegion)) {
if (foundStartPosition) {
if (!startsAboveRegion) {
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);
foundStartPosition = false;
continue;
}
if (logicalTopForRenderer < logicalTopForRegion && startsAboveRegion) {
if (is<RenderText>(*renderer)) {
RenderText& textRenderer = downcast<RenderText>(*renderer);
for (InlineTextBox* box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) {
if (offsetTop + box->logicalBottom() < logicalTopForRegion)
continue;
range->setStart(Position(downcast<Text>(node), box->start()));
startsAboveRegion = false;
break;
}
} else {
startsAboveRegion = true;
range->setStart(Position(node, Position::PositionIsBeforeChildren));
}
} else {
if (startsAboveRegion) {
startsAboveRegion = false;
range->setStartBefore(*node);
}
}
skipOverOutsideNodes = false;
foundStartPosition = true;
if (logicalBottomForRegion < logicalBottomForRenderer && (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode)))) {
if (is<RenderText>(*renderer)) {
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(downcast<Text>(node), lastBox->start() + lastBox->len()));
break;
}
endsBelowRegion = false;
lastEndNode = node;
} else {
range->setEnd(Position(node, Position::PositionIsAfterChildren));
endsBelowRegion = true;
lastEndNode = node;
}
} else {
if (endsBelowRegion || (!endsBelowRegion && !node->isDescendantOf(lastEndNode))) {
range->setEndAfter(*node);
endsBelowRegion = false;
lastEndNode = node;
}
}
}
if (foundStartPosition || skipOverOutsideNodes)
rangeObjects.append(range);
}
}
void RenderNamedFlowThread::applyBreakAfterContent(LayoutUnit clientHeight)
{
addForcedRegionBreak(this, clientHeight, this, false);
}
bool RenderNamedFlowThread::collectsGraphicsLayersUnderRegions() const
{
return true;
}
void RenderNamedFlowThread::checkRegionsWithStyling()
{
bool hasRegionsWithStyling = false;
for (const auto& region : m_regionList) {
if (downcast<RenderNamedFlowFragment>(*region).hasCustomRegionStyle()) {
hasRegionsWithStyling = true;
break;
}
}
m_hasRegionsWithStyling = hasRegionsWithStyling;
}
void RenderNamedFlowThread::clearRenderObjectCustomStyle(const RenderElement& object)
{
for (auto& region : m_regionList)
downcast<RenderNamedFlowFragment>(*region).clearObjectStyleInRegion(object);
}
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 (!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;
}
}