RenderTreeBuilder.cpp [plain text]
#include "config.h"
#include "RenderTreeBuilder.h"
#include "AXObjectCache.h"
#include "Frame.h"
#include "FrameSelection.h"
#include "RenderButton.h"
#include "RenderCounter.h"
#include "RenderElement.h"
#include "RenderEmbeddedObject.h"
#include "RenderFullScreen.h"
#include "RenderGrid.h"
#include "RenderHTMLCanvas.h"
#include "RenderLineBreak.h"
#include "RenderMathMLFenced.h"
#include "RenderMenuList.h"
#include "RenderMultiColumnFlow.h"
#include "RenderMultiColumnSpannerPlaceholder.h"
#include "RenderReplaced.h"
#include "RenderRuby.h"
#include "RenderRubyBase.h"
#include "RenderRubyRun.h"
#include "RenderSVGContainer.h"
#include "RenderSVGInline.h"
#include "RenderSVGRoot.h"
#include "RenderSVGText.h"
#include "RenderTable.h"
#include "RenderTableRow.h"
#include "RenderTableSection.h"
#include "RenderText.h"
#include "RenderTextFragment.h"
#include "RenderTreeBuilderBlock.h"
#include "RenderTreeBuilderBlockFlow.h"
#include "RenderTreeBuilderContinuation.h"
#include "RenderTreeBuilderFirstLetter.h"
#include "RenderTreeBuilderFormControls.h"
#include "RenderTreeBuilderFullScreen.h"
#include "RenderTreeBuilderInline.h"
#include "RenderTreeBuilderList.h"
#include "RenderTreeBuilderMathML.h"
#include "RenderTreeBuilderMultiColumn.h"
#include "RenderTreeBuilderRuby.h"
#include "RenderTreeBuilderSVG.h"
#include "RenderTreeBuilderTable.h"
#include "RenderTreeMutationDisallowedScope.h"
#include "RenderView.h"
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "FrameView.h"
#include "FrameViewLayoutContext.h"
#include "RuntimeEnabledFeatures.h"
#endif
namespace WebCore {
RenderTreeBuilder* RenderTreeBuilder::s_current;
static void markBoxForRelayoutAfterSplit(RenderBox& box)
{
if (is<RenderTable>(box)) {
downcast<RenderTable>(box).forceSectionsRecalc();
} else if (is<RenderTableSection>(box))
downcast<RenderTableSection>(box).setNeedsCellRecalc();
box.setNeedsLayoutAndPrefWidthsRecalc();
}
static void getInlineRun(RenderObject* start, RenderObject* boundary, RenderObject*& inlineRunStart, RenderObject*& inlineRunEnd)
{
auto* curr = start;
bool sawInline;
do {
while (curr && !(curr->isInline() || curr->isFloatingOrOutOfFlowPositioned()))
curr = curr->nextSibling();
inlineRunStart = inlineRunEnd = curr;
if (!curr)
return;
sawInline = curr->isInline();
curr = curr->nextSibling();
while (curr && (curr->isInline() || curr->isFloatingOrOutOfFlowPositioned()) && (curr != boundary)) {
inlineRunEnd = curr;
if (curr->isInline())
sawInline = true;
curr = curr->nextSibling();
}
} while (!sawInline);
}
RenderTreeBuilder::RenderTreeBuilder(RenderView& view)
: m_view(view)
, m_firstLetterBuilder(makeUnique<FirstLetter>(*this))
, m_listBuilder(makeUnique<List>(*this))
, m_multiColumnBuilder(makeUnique<MultiColumn>(*this))
, m_tableBuilder(makeUnique<Table>(*this))
, m_rubyBuilder(makeUnique<Ruby>(*this))
, m_formControlsBuilder(makeUnique<FormControls>(*this))
, m_blockBuilder(makeUnique<Block>(*this))
, m_blockFlowBuilder(makeUnique<BlockFlow>(*this))
, m_inlineBuilder(makeUnique<Inline>(*this))
, m_svgBuilder(makeUnique<SVG>(*this))
#if ENABLE(MATHML)
, m_mathMLBuilder(makeUnique<MathML>(*this))
#endif
, m_continuationBuilder(makeUnique<Continuation>(*this))
#if ENABLE(FULLSCREEN_API)
, m_fullScreenBuilder(makeUnique<FullScreen>(*this))
#endif
{
RELEASE_ASSERT(!s_current || &m_view != &s_current->m_view);
m_previous = s_current;
s_current = this;
}
RenderTreeBuilder::~RenderTreeBuilder()
{
s_current = m_previous;
}
void RenderTreeBuilder::destroy(RenderObject& renderer)
{
RELEASE_ASSERT(RenderTreeMutationDisallowedScope::isMutationAllowed());
ASSERT(renderer.parent());
auto toDestroy = detach(*renderer.parent(), renderer);
#if ENABLE(FULLSCREEN_API)
if (is<RenderFullScreen>(renderer))
fullScreenBuilder().cleanupOnDestroy(downcast<RenderFullScreen>(renderer));
#endif
if (is<RenderTextFragment>(renderer))
firstLetterBuilder().cleanupOnDestroy(downcast<RenderTextFragment>(renderer));
if (is<RenderBoxModelObject>(renderer))
continuationBuilder().cleanupOnDestroy(downcast<RenderBoxModelObject>(renderer));
if (!is<RenderElement>(toDestroy.get()))
return;
auto& childToDestroy = downcast<RenderElement>(*toDestroy.get());
while (childToDestroy.firstChild()) {
auto& firstChild = *childToDestroy.firstChild();
if (auto* node = firstChild.node())
node->setRenderer(nullptr);
destroy(firstChild);
}
}
void RenderTreeBuilder::attach(RenderElement& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
reportVisuallyNonEmptyContent(parent, *child);
attachInternal(parent, WTFMove(child), beforeChild);
}
void RenderTreeBuilder::attachInternal(RenderElement& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
auto insertRecursiveIfNeeded = [&](RenderElement& parentCandidate) {
if (&parent == &parentCandidate) {
if (is<RenderBlockFlow>(parent) && downcast<RenderBlockFlow>(parent).multiColumnFlow()) {
blockFlowBuilder().attach(downcast<RenderBlockFlow>(parent), WTFMove(child), beforeChild);
return;
}
attachToRenderElement(parent, WTFMove(child), beforeChild);
return;
}
attachInternal(parentCandidate, WTFMove(child), beforeChild);
};
ASSERT(&parent.view() == &m_view);
if (is<RenderText>(beforeChild)) {
if (auto* wrapperInline = downcast<RenderText>(*beforeChild).inlineWrapperForDisplayContents())
beforeChild = wrapperInline;
} else if (is<RenderBox>(beforeChild)) {
auto& beforeChildBox = downcast<RenderBox>(*beforeChild);
if (auto* enclosingFragmentedFlow = parent.enclosingFragmentedFlow()) {
auto columnSpannerPlaceholderForBeforeChild = [&]() -> RenderMultiColumnSpannerPlaceholder* {
if (!is<RenderMultiColumnFlow>(enclosingFragmentedFlow))
return nullptr;
auto& multiColumnFlow = downcast<RenderMultiColumnFlow>(*enclosingFragmentedFlow);
return multiColumnFlow.findColumnSpannerPlaceholder(&beforeChildBox);
};
if (auto* spannerPlaceholder = columnSpannerPlaceholderForBeforeChild())
beforeChild = spannerPlaceholder;
}
}
if (is<RenderTableRow>(parent)) {
auto& parentCandidate = tableBuilder().findOrCreateParentForChild(downcast<RenderTableRow>(parent), *child, beforeChild);
if (&parentCandidate == &parent) {
tableBuilder().attach(downcast<RenderTableRow>(parentCandidate), WTFMove(child), beforeChild);
return;
}
insertRecursiveIfNeeded(parentCandidate);
return;
}
if (is<RenderTableSection>(parent)) {
auto& parentCandidate = tableBuilder().findOrCreateParentForChild(downcast<RenderTableSection>(parent), *child, beforeChild);
if (&parent == &parentCandidate) {
tableBuilder().attach(downcast<RenderTableSection>(parent), WTFMove(child), beforeChild);
return;
}
insertRecursiveIfNeeded(parentCandidate);
return;
}
if (is<RenderTable>(parent)) {
auto& parentCandidate = tableBuilder().findOrCreateParentForChild(downcast<RenderTable>(parent), *child, beforeChild);
if (&parentCandidate == &parent) {
tableBuilder().attach(downcast<RenderTable>(parentCandidate), WTFMove(child), beforeChild);
return;
}
insertRecursiveIfNeeded(parentCandidate);
return;
}
if (is<RenderRubyAsBlock>(parent)) {
insertRecursiveIfNeeded(rubyBuilder().findOrCreateParentForChild(downcast<RenderRubyAsBlock>(parent), *child, beforeChild));
return;
}
if (is<RenderRubyAsInline>(parent)) {
insertRecursiveIfNeeded(rubyBuilder().findOrCreateParentForChild(downcast<RenderRubyAsInline>(parent), *child, beforeChild));
return;
}
if (is<RenderRubyRun>(parent)) {
rubyBuilder().attach(downcast<RenderRubyRun>(parent), WTFMove(child), beforeChild);
return;
}
if (is<RenderButton>(parent)) {
formControlsBuilder().attach(downcast<RenderButton>(parent), WTFMove(child), beforeChild);
return;
}
if (is<RenderMenuList>(parent)) {
formControlsBuilder().attach(downcast<RenderMenuList>(parent), WTFMove(child), beforeChild);
return;
}
if (is<RenderSVGContainer>(parent)) {
svgBuilder().attach(downcast<RenderSVGContainer>(parent), WTFMove(child), beforeChild);
return;
}
if (is<RenderSVGInline>(parent)) {
svgBuilder().attach(downcast<RenderSVGInline>(parent), WTFMove(child), beforeChild);
return;
}
if (is<RenderSVGRoot>(parent)) {
svgBuilder().attach(downcast<RenderSVGRoot>(parent), WTFMove(child), beforeChild);
return;
}
if (is<RenderSVGText>(parent)) {
svgBuilder().attach(downcast<RenderSVGText>(parent), WTFMove(child), beforeChild);
return;
}
#if ENABLE(MATHML)
if (is<RenderMathMLFenced>(parent)) {
mathMLBuilder().attach(downcast<RenderMathMLFenced>(parent), WTFMove(child), beforeChild);
return;
}
#endif
if (is<RenderGrid>(parent)) {
attachToRenderGrid(downcast<RenderGrid>(parent), WTFMove(child), beforeChild);
return;
}
if (is<RenderBlockFlow>(parent)) {
blockFlowBuilder().attach(downcast<RenderBlockFlow>(parent), WTFMove(child), beforeChild);
return;
}
if (is<RenderBlock>(parent)) {
blockBuilder().attach(downcast<RenderBlock>(parent), WTFMove(child), beforeChild);
return;
}
if (is<RenderInline>(parent)) {
inlineBuilder().attach(downcast<RenderInline>(parent), WTFMove(child), beforeChild);
return;
}
attachToRenderElement(parent, WTFMove(child), beforeChild);
}
void RenderTreeBuilder::attachIgnoringContinuation(RenderElement& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
if (is<RenderInline>(parent)) {
inlineBuilder().attachIgnoringContinuation(downcast<RenderInline>(parent), WTFMove(child), beforeChild);
return;
}
if (is<RenderBlock>(parent)) {
blockBuilder().attachIgnoringContinuation(downcast<RenderBlock>(parent), WTFMove(child), beforeChild);
return;
}
attachInternal(parent, WTFMove(child), beforeChild);
}
RenderPtr<RenderObject> RenderTreeBuilder::detach(RenderElement& parent, RenderObject& child, CanCollapseAnonymousBlock canCollapseAnonymousBlock)
{
if (is<RenderRubyAsInline>(parent))
return rubyBuilder().detach(downcast<RenderRubyAsInline>(parent), child);
if (is<RenderRubyAsBlock>(parent))
return rubyBuilder().detach(downcast<RenderRubyAsBlock>(parent), child);
if (is<RenderRubyRun>(parent))
return rubyBuilder().detach(downcast<RenderRubyRun>(parent), child);
if (is<RenderMenuList>(parent))
return formControlsBuilder().detach(downcast<RenderMenuList>(parent), child);
if (is<RenderButton>(parent))
return formControlsBuilder().detach(downcast<RenderButton>(parent), child);
if (is<RenderGrid>(parent))
return detachFromRenderGrid(downcast<RenderGrid>(parent), child);
if (is<RenderSVGText>(parent))
return svgBuilder().detach(downcast<RenderSVGText>(parent), child);
if (is<RenderSVGInline>(parent))
return svgBuilder().detach(downcast<RenderSVGInline>(parent), child);
if (is<RenderSVGContainer>(parent))
return svgBuilder().detach(downcast<RenderSVGContainer>(parent), child);
if (is<RenderSVGRoot>(parent))
return svgBuilder().detach(downcast<RenderSVGRoot>(parent), child);
if (is<RenderBlockFlow>(parent))
return blockBuilder().detach(downcast<RenderBlockFlow>(parent), child, canCollapseAnonymousBlock);
if (is<RenderBlock>(parent))
return blockBuilder().detach(downcast<RenderBlock>(parent), child, canCollapseAnonymousBlock);
return detachFromRenderElement(parent, child);
}
#if ENABLE(FULLSCREEN_API)
void RenderTreeBuilder::createPlaceholderForFullScreen(RenderFullScreen& renderer, std::unique_ptr<RenderStyle> style, const LayoutRect& frameRect)
{
fullScreenBuilder().createPlaceholder(renderer, WTFMove(style), frameRect);
}
#endif
void RenderTreeBuilder::attachToRenderElement(RenderElement& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
if (tableBuilder().childRequiresTable(parent, *child)) {
RenderTable* table;
RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : parent.lastChild();
if (afterChild && afterChild->isAnonymous() && is<RenderTable>(*afterChild) && !afterChild->isBeforeContent())
table = downcast<RenderTable>(afterChild);
else {
auto newTable = RenderTable::createAnonymousWithParentRenderer(parent);
table = newTable.get();
attach(parent, WTFMove(newTable), beforeChild);
}
attach(*table, WTFMove(child));
return;
}
auto& newChild = *child.get();
attachToRenderElementInternal(parent, WTFMove(child), beforeChild);
parent.didAttachChild(newChild, beforeChild);
}
void RenderTreeBuilder::attachToRenderElementInternal(RenderElement& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
RELEASE_ASSERT_WITH_MESSAGE(!parent.view().frameView().layoutContext().layoutState(), "Layout must not mutate render tree");
ASSERT(parent.canHaveChildren() || parent.canHaveGeneratedChildren());
ASSERT(!child->parent());
ASSERT(!parent.isRenderBlockFlow() || (!child->isTableSection() && !child->isTableRow() && !child->isTableCell()));
while (beforeChild && beforeChild->parent() && beforeChild->parent() != &parent)
beforeChild = beforeChild->parent();
ASSERT(!beforeChild || beforeChild->parent() == &parent);
ASSERT(!is<RenderText>(beforeChild) || !downcast<RenderText>(*beforeChild).inlineWrapperForDisplayContents());
auto* newChild = parent.attachRendererInternal(WTFMove(child), beforeChild);
newChild->initializeFragmentedFlowStateOnInsertion();
if (!parent.renderTreeBeingDestroyed()) {
newChild->insertedIntoTree();
auto* fragmentedFlow = newChild->enclosingFragmentedFlow();
if (is<RenderMultiColumnFlow>(fragmentedFlow))
multiColumnBuilder().multiColumnDescendantInserted(downcast<RenderMultiColumnFlow>(*fragmentedFlow), *newChild);
if (is<RenderElement>(*newChild))
RenderCounter::rendererSubtreeAttached(downcast<RenderElement>(*newChild));
}
newChild->setNeedsLayoutAndPrefWidthsRecalc();
parent.setPreferredLogicalWidthsDirty(true);
if (!parent.normalChildNeedsLayout())
parent.setChildNeedsLayout();
if (AXObjectCache* cache = parent.document().axObjectCache())
cache->childrenChanged(&parent, newChild);
if (is<RenderBlockFlow>(parent))
downcast<RenderBlockFlow>(parent).invalidateLineLayoutPath();
if (parent.hasOutlineAutoAncestor() || parent.outlineStyleForRepaint().outlineStyleIsAuto() == OutlineIsAuto::On)
newChild->setHasOutlineAutoAncestor();
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
if (RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextEnabled()) {
if (parent.document().view())
parent.document().view()->layoutContext().invalidateLayoutTreeContent();
}
#endif
}
void RenderTreeBuilder::move(RenderBoxModelObject& from, RenderBoxModelObject& to, RenderObject& child, RenderObject* beforeChild, NormalizeAfterInsertion normalizeAfterInsertion)
{
ASSERT(normalizeAfterInsertion == NormalizeAfterInsertion::No || !is<RenderBlock>(from) || !downcast<RenderBlock>(from).hasPositionedObjects());
ASSERT(&from == child.parent());
ASSERT(!beforeChild || &to == beforeChild->parent());
if (normalizeAfterInsertion == NormalizeAfterInsertion::Yes && (to.isRenderBlock() || to.isRenderInline())) {
auto childToMove = detachFromRenderElement(from, child);
attach(to, WTFMove(childToMove), beforeChild);
} else {
auto childToMove = detachFromRenderElement(from, child);
attachToRenderElementInternal(to, WTFMove(childToMove), beforeChild);
}
}
void RenderTreeBuilder::move(RenderBoxModelObject& from, RenderBoxModelObject& to, RenderObject& child, NormalizeAfterInsertion normalizeAfterInsertion)
{
move(from, to, child, nullptr, normalizeAfterInsertion);
}
void RenderTreeBuilder::moveAllChildren(RenderBoxModelObject& from, RenderBoxModelObject& to, NormalizeAfterInsertion normalizeAfterInsertion)
{
moveAllChildren(from, to, nullptr, normalizeAfterInsertion);
}
void RenderTreeBuilder::moveAllChildren(RenderBoxModelObject& from, RenderBoxModelObject& to, RenderObject* beforeChild, NormalizeAfterInsertion normalizeAfterInsertion)
{
moveChildren(from, to, from.firstChild(), nullptr, beforeChild, normalizeAfterInsertion);
}
void RenderTreeBuilder::moveChildren(RenderBoxModelObject& from, RenderBoxModelObject& to, RenderObject* startChild, RenderObject* endChild, NormalizeAfterInsertion normalizeAfterInsertion)
{
moveChildren(from, to, startChild, endChild, nullptr, normalizeAfterInsertion);
}
void RenderTreeBuilder::moveChildren(RenderBoxModelObject& from, RenderBoxModelObject& to, RenderObject* startChild, RenderObject* endChild, RenderObject* beforeChild, NormalizeAfterInsertion normalizeAfterInsertion)
{
if (normalizeAfterInsertion == NormalizeAfterInsertion::Yes && is<RenderBlock>(from)) {
downcast<RenderBlock>(from).removePositionedObjects(nullptr);
if (is<RenderBlockFlow>(from))
downcast<RenderBlockFlow>(from).removeFloatingObjects();
}
ASSERT(!beforeChild || &to == beforeChild->parent());
for (RenderObject* child = startChild; child && child != endChild; ) {
RenderObject* nextSibling = child->nextSibling();
if (is<RenderTextFragment>(*child) && is<RenderText>(nextSibling)) {
RenderObject* firstLetterObj = nullptr;
if (RenderBlock* block = downcast<RenderTextFragment>(*child).blockForAccompanyingFirstLetter()) {
RenderElement* firstLetterContainer = nullptr;
block->getFirstLetter(firstLetterObj, firstLetterContainer, child);
}
if (firstLetterObj == nextSibling)
nextSibling = nextSibling->nextSibling();
}
move(from, to, *child, beforeChild, normalizeAfterInsertion);
child = nextSibling;
}
}
void RenderTreeBuilder::moveAllChildrenIncludingFloats(RenderBlock& from, RenderBlock& to, RenderTreeBuilder::NormalizeAfterInsertion normalizeAfterInsertion)
{
if (is<RenderBlockFlow>(from)) {
blockFlowBuilder().moveAllChildrenIncludingFloats(downcast<RenderBlockFlow>(from), to, normalizeAfterInsertion);
return;
}
moveAllChildren(from, to, normalizeAfterInsertion);
}
void RenderTreeBuilder::normalizeTreeAfterStyleChange(RenderElement& renderer, RenderStyle& oldStyle)
{
if (!renderer.parent())
return;
auto& parent = *renderer.parent();
bool wasFloating = oldStyle.isFloating();
bool wasOutOfFlowPositioned = oldStyle.hasOutOfFlowPosition();
bool isFloating = renderer.style().isFloating();
bool isOutOfFlowPositioned = renderer.style().hasOutOfFlowPosition();
bool startsAffectingParent = false;
bool noLongerAffectsParent = false;
if (is<RenderBlock>(parent))
noLongerAffectsParent = (!wasFloating && isFloating) || (!wasOutOfFlowPositioned && isOutOfFlowPositioned);
if (is<RenderBlockFlow>(parent) || is<RenderInline>(parent)) {
startsAffectingParent = (wasFloating || wasOutOfFlowPositioned) && !isFloating && !isOutOfFlowPositioned;
ASSERT(!startsAffectingParent || !noLongerAffectsParent);
}
if (startsAffectingParent) {
if (!is<RenderSVGInline>(renderer))
renderer.setInline(renderer.style().isDisplayInlineType());
if (renderer.isInline() != renderer.parent()->childrenInline())
childFlowStateChangesAndAffectsParentBlock(renderer);
return;
}
if (noLongerAffectsParent) {
childFlowStateChangesAndNoLongerAffectsParentBlock(renderer);
if (is<RenderBlockFlow>(renderer)) {
if (isFloating && renderer.previousSibling() && renderer.previousSibling()->isAnonymousBlock())
move(downcast<RenderBoxModelObject>(parent), downcast<RenderBoxModelObject>(*renderer.previousSibling()), renderer, RenderTreeBuilder::NormalizeAfterInsertion::No);
}
}
if (auto* enclosingFragmentedFlow = parent.enclosingFragmentedFlow(); is<RenderMultiColumnFlow>(enclosingFragmentedFlow) && wasOutOfFlowPositioned && !isOutOfFlowPositioned) {
multiColumnBuilder().multiColumnDescendantInserted(downcast<RenderMultiColumnFlow>(*enclosingFragmentedFlow), renderer);
renderer.initializeFragmentedFlowStateOnInsertion();
}
}
void RenderTreeBuilder::makeChildrenNonInline(RenderBlock& parent, RenderObject* insertionPoint)
{
ASSERT(parent.isInlineBlockOrInlineTable() || !parent.isInline());
ASSERT(!insertionPoint || insertionPoint->parent() == &parent);
parent.setChildrenInline(false);
auto* child = parent.firstChild();
if (!child)
return;
parent.deleteLines();
while (child) {
RenderObject* inlineRunStart = nullptr;
RenderObject* inlineRunEnd = nullptr;
getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd);
if (!inlineRunStart)
break;
child = inlineRunEnd->nextSibling();
auto newBlock = parent.createAnonymousBlock();
auto& block = *newBlock;
attachToRenderElementInternal(parent, WTFMove(newBlock), inlineRunStart);
moveChildren(parent, block, inlineRunStart, child, RenderTreeBuilder::NormalizeAfterInsertion::No);
}
#ifndef NDEBUG
for (RenderObject* c = parent.firstChild(); c; c = c->nextSibling())
ASSERT(!c->isInline());
#endif
parent.repaint();
}
RenderObject* RenderTreeBuilder::splitAnonymousBoxesAroundChild(RenderBox& parent, RenderObject& originalBeforeChild)
{
auto* beforeChild = RenderTreeBuilder::MultiColumn::adjustBeforeChildForMultiColumnSpannerIfNeeded(originalBeforeChild);
bool didSplitParentAnonymousBoxes = false;
while (beforeChild->parent() != &parent) {
auto& boxToSplit = downcast<RenderBox>(*beforeChild->parent());
if (boxToSplit.firstChild() != beforeChild && boxToSplit.isAnonymous()) {
didSplitParentAnonymousBoxes = true;
auto newPostBox = boxToSplit.createAnonymousBoxWithSameTypeAs(parent);
auto& postBox = *newPostBox;
postBox.setChildrenInline(boxToSplit.childrenInline());
RenderBox* parentBox = downcast<RenderBox>(boxToSplit.parent());
markBoxForRelayoutAfterSplit(*parentBox);
attachToRenderElementInternal(*parentBox, WTFMove(newPostBox), boxToSplit.nextSibling());
moveChildren(boxToSplit, postBox, beforeChild, nullptr, RenderTreeBuilder::NormalizeAfterInsertion::Yes);
markBoxForRelayoutAfterSplit(boxToSplit);
markBoxForRelayoutAfterSplit(postBox);
beforeChild = &postBox;
} else
beforeChild = &boxToSplit;
}
if (didSplitParentAnonymousBoxes)
markBoxForRelayoutAfterSplit(parent);
ASSERT(beforeChild->parent() == &parent);
return beforeChild;
}
void RenderTreeBuilder::childFlowStateChangesAndAffectsParentBlock(RenderElement& child)
{
if (!child.isInline()) {
auto* currentEnclosingFragment = child.enclosingFragmentedFlow();
auto parent = makeWeakPtr(child.parent());
if (is<RenderBlock>(*parent))
blockBuilder().childBecameNonInline(downcast<RenderBlock>(*parent), child);
else if (is<RenderInline>(*parent))
inlineBuilder().childBecameNonInline(downcast<RenderInline>(*parent), child);
if (auto* newParent = child.parent()) {
if (newParent != parent && is<RenderGrid>(*newParent)) {
downcast<RenderGrid>(*newParent).dirtyGrid();
}
if (auto* newEnclosingFragmentedFlow = newParent->enclosingFragmentedFlow(); is<RenderMultiColumnFlow>(newEnclosingFragmentedFlow) && currentEnclosingFragment != newEnclosingFragmentedFlow) {
multiColumnBuilder().multiColumnDescendantInserted(downcast<RenderMultiColumnFlow>(*newEnclosingFragmentedFlow), child);
}
}
} else {
auto* parent = child.parent();
auto newBlock = downcast<RenderBlock>(*parent).createAnonymousBlock();
auto& block = *newBlock;
attachToRenderElementInternal(*parent, WTFMove(newBlock), &child);
auto thisToMove = detachFromRenderElement(*parent, child);
attachToRenderElementInternal(block, WTFMove(thisToMove));
}
}
void RenderTreeBuilder::removeAnonymousWrappersForInlineChildrenIfNeeded(RenderElement& parent)
{
if (!is<RenderBlock>(parent))
return;
auto& blockParent = downcast<RenderBlock>(parent);
if (!blockParent.canDropAnonymousBlockChild())
return;
Optional<bool> shouldAllChildrenBeInline;
for (auto* current = blockParent.firstChild(); current; current = current->nextSibling()) {
if (current->style().isFloating() || current->style().hasOutOfFlowPosition())
continue;
if (!current->isAnonymousBlock() || downcast<RenderBlock>(*current).isContinuation())
return;
auto* firstChild = current->firstChildSlow();
if (!firstChild)
continue;
auto isInlineLevelBox = firstChild->isInline();
if (!shouldAllChildrenBeInline.hasValue()) {
shouldAllChildrenBeInline = isInlineLevelBox;
continue;
}
if (*shouldAllChildrenBeInline != isInlineLevelBox)
return;
}
RenderObject* next = nullptr;
for (auto* current = blockParent.firstChild(); current; current = next) {
next = current->nextSibling();
if (current->isAnonymousBlock())
blockBuilder().dropAnonymousBoxChild(blockParent, downcast<RenderBlock>(*current));
}
}
void RenderTreeBuilder::childFlowStateChangesAndNoLongerAffectsParentBlock(RenderElement& child)
{
ASSERT(child.parent());
removeAnonymousWrappersForInlineChildrenIfNeeded(*child.parent());
}
void RenderTreeBuilder::destroyAndCleanUpAnonymousWrappers(RenderObject& rendererToDestroy)
{
if (rendererToDestroy.renderTreeBeingDestroyed()) {
destroy(rendererToDestroy);
return;
}
if (is<RenderBox>(rendererToDestroy) && rendererToDestroy.isFloatingOrOutOfFlowPositioned())
downcast<RenderBox>(rendererToDestroy).removeFloatingOrPositionedChildFromBlockLists();
auto isAnonymousAndSafeToDelete = [] (const auto& renderer) {
return renderer.isAnonymous() && !renderer.isRenderView() && !renderer.isRenderFragmentedFlow();
};
auto destroyRootIncludingAnonymous = [&] () -> RenderObject& {
auto* destroyRoot = &rendererToDestroy;
while (!is<RenderView>(*destroyRoot)) {
auto& destroyRootParent = *destroyRoot->parent();
if (!isAnonymousAndSafeToDelete(destroyRootParent))
break;
bool destroyingOnlyChild = destroyRootParent.firstChild() == destroyRoot && destroyRootParent.lastChild() == destroyRoot;
if (!destroyingOnlyChild)
break;
destroyRoot = &destroyRootParent;
}
return *destroyRoot;
};
auto& destroyRoot = destroyRootIncludingAnonymous();
if (is<RenderTableRow>(destroyRoot))
tableBuilder().collapseAndDestroyAnonymousSiblingRows(downcast<RenderTableRow>(destroyRoot));
auto destroyRootParent = makeWeakPtr(*destroyRoot.parent());
if (&rendererToDestroy != &destroyRoot) {
destroy(rendererToDestroy);
}
destroy(destroyRoot);
if (!destroyRootParent)
return;
removeAnonymousWrappersForInlineChildrenIfNeeded(*destroyRootParent);
if (isAnonymousAndSafeToDelete(*destroyRootParent) && !destroyRootParent->firstChild())
destroyAndCleanUpAnonymousWrappers(*destroyRootParent);
}
void RenderTreeBuilder::updateAfterDescendants(RenderElement& renderer)
{
if (is<RenderBlock>(renderer))
firstLetterBuilder().updateAfterDescendants(downcast<RenderBlock>(renderer));
if (is<RenderListItem>(renderer))
listBuilder().updateItemMarker(downcast<RenderListItem>(renderer));
if (is<RenderBlockFlow>(renderer))
multiColumnBuilder().updateAfterDescendants(downcast<RenderBlockFlow>(renderer));
}
RenderPtr<RenderObject> RenderTreeBuilder::detachFromRenderGrid(RenderGrid& parent, RenderObject& child)
{
auto takenChild = blockBuilder().detach(parent, child);
if (child.isOutOfFlowPositioned())
return takenChild;
parent.dirtyGrid();
return takenChild;
}
RenderPtr<RenderObject> RenderTreeBuilder::detachFromRenderElement(RenderElement& parent, RenderObject& child, WillBeDestroyed willBeDestroyed)
{
RELEASE_ASSERT_WITH_MESSAGE(!parent.view().frameView().layoutContext().layoutState(), "Layout must not mutate render tree");
ASSERT(parent.canHaveChildren() || parent.canHaveGeneratedChildren());
ASSERT(child.parent() == &parent);
if (child.isFloatingOrOutOfFlowPositioned())
downcast<RenderBox>(child).removeFloatingOrPositionedChildFromBlockLists();
if (!parent.renderTreeBeingDestroyed() && child.everHadLayout()) {
child.setNeedsLayoutAndPrefWidthsRecalc();
if (child.isBody())
parent.view().repaintRootContents();
else
child.repaint();
}
if (is<RenderBox>(child))
downcast<RenderBox>(child).deleteLineBoxWrapper();
else if (is<RenderLineBreak>(child))
downcast<RenderLineBreak>(child).deleteInlineBoxWrapper();
if (!parent.renderTreeBeingDestroyed() && is<RenderFlexibleBox>(parent) && !child.isFloatingOrOutOfFlowPositioned() && child.isBox())
downcast<RenderFlexibleBox>(parent).clearCachedChildIntrinsicContentLogicalHeight(downcast<RenderBox>(child));
if (!parent.renderTreeBeingDestroyed() && willBeDestroyed == WillBeDestroyed::Yes && child.isSelectionBorder())
parent.frame().selection().setNeedsSelectionUpdate();
child.resetFragmentedFlowStateOnRemoval();
if (!parent.renderTreeBeingDestroyed())
child.willBeRemovedFromTree();
auto childToTake = parent.detachRendererInternal(child);
if (!parent.renderTreeBeingDestroyed() && is<RenderElement>(*childToTake))
RenderCounter::rendererRemovedFromTree(downcast<RenderElement>(*childToTake));
if (!parent.renderTreeBeingDestroyed()) {
if (AXObjectCache* cache = parent.document().existingAXObjectCache())
cache->childrenChanged(&parent);
}
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
if (RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextEnabled()) {
if (parent.document().view())
parent.document().view()->layoutContext().invalidateLayoutTreeContent();
}
#endif
return childToTake;
}
void RenderTreeBuilder::attachToRenderGrid(RenderGrid& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
auto& newChild = *child;
blockBuilder().attach(parent, WTFMove(child), beforeChild);
if (newChild.isOutOfFlowPositioned())
return;
parent.dirtyGrid();
}
void RenderTreeBuilder::reportVisuallyNonEmptyContent(const RenderElement& parent, const RenderObject& child)
{
if (is<RenderText>(child)) {
auto& style = parent.style();
if (style.visibility() == Visibility::Visible && !style.fontCascade().isLoadingCustomFonts()) {
auto& textRenderer = downcast<RenderText>(child);
m_view.frameView().incrementVisuallyNonEmptyCharacterCount(textRenderer.text());
}
return;
}
if (is<RenderHTMLCanvas>(child) || is<RenderEmbeddedObject>(child)) {
auto& replacedRenderer = downcast<RenderReplaced>(child);
m_view.frameView().incrementVisuallyNonEmptyPixelCount(roundedIntSize(replacedRenderer.intrinsicSize()));
return;
}
if (is<RenderSVGRoot>(child)) {
auto fixedSize = [] (const auto& renderer) -> Optional<IntSize> {
auto& style = renderer.style();
if (!style.width().isFixed() || !style.height().isFixed())
return { };
return makeOptional(IntSize { style.width().intValue(), style.height().intValue() });
};
auto candidateSize = IntSize { };
if (auto size = fixedSize(child))
candidateSize = *size;
else if (auto size = fixedSize(parent))
candidateSize = *size;
if (!candidateSize.isEmpty())
m_view.frameView().incrementVisuallyNonEmptyPixelCount(candidateSize);
return;
}
}
}