RenderTreeBuilderBlock.cpp [plain text]
#include "config.h"
#include "RenderTreeBuilderBlock.h"
#include "RenderButton.h"
#include "RenderChildIterator.h"
#include "RenderFullScreen.h"
#include "RenderMultiColumnFlow.h"
#include "RenderRuby.h"
#include "RenderRubyRun.h"
#include "RenderTextControl.h"
#include "RenderTreeBuilderMultiColumn.h"
namespace WebCore {
static void moveAllChildrenToInternal(RenderBoxModelObject& from, RenderElement& newParent)
{
while (from.firstChild())
newParent.attachRendererInternal(from.detachRendererInternal(*from.firstChild()), &from);
}
static bool canDropAnonymousBlock(const RenderBlock& anonymousBlock)
{
if (anonymousBlock.beingDestroyed() || anonymousBlock.continuation())
return false;
if (anonymousBlock.isRubyRun() || anonymousBlock.isRubyBase())
return false;
return true;
}
static bool canMergeContiguousAnonymousBlocks(RenderObject& oldChild, RenderObject* previous, RenderObject* next)
{
ASSERT(!oldChild.renderTreeBeingDestroyed());
if (oldChild.isInline())
return false;
if (is<RenderBoxModelObject>(oldChild) && downcast<RenderBoxModelObject>(oldChild).continuation())
return false;
if (previous) {
if (!previous->isAnonymousBlock())
return false;
RenderBlock& previousAnonymousBlock = downcast<RenderBlock>(*previous);
if (!canDropAnonymousBlock(previousAnonymousBlock))
return false;
}
if (next) {
if (!next->isAnonymousBlock())
return false;
RenderBlock& nextAnonymousBlock = downcast<RenderBlock>(*next);
if (!canDropAnonymousBlock(nextAnonymousBlock))
return false;
}
return true;
}
static RenderBlock* continuationBefore(RenderBlock& parent, RenderObject* beforeChild)
{
if (beforeChild && beforeChild->parent() == &parent)
return &parent;
RenderBlock* nextToLast = &parent;
RenderBlock* last = &parent;
for (auto* current = downcast<RenderBlock>(parent.continuation()); current; current = downcast<RenderBlock>(current->continuation())) {
if (beforeChild && beforeChild->parent() == current) {
if (current->firstChild() == beforeChild)
return last;
return current;
}
nextToLast = last;
last = current;
}
if (!beforeChild && !last->firstChild())
return nextToLast;
return last;
}
RenderTreeBuilder::Block::Block(RenderTreeBuilder& builder)
: m_builder(builder)
{
}
void RenderTreeBuilder::Block::attach(RenderBlock& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
if (parent.continuation() && !parent.isAnonymousBlock())
insertChildToContinuation(parent, WTFMove(child), beforeChild);
else
attachIgnoringContinuation(parent, WTFMove(child), beforeChild);
}
void RenderTreeBuilder::Block::insertChildToContinuation(RenderBlock& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
RenderBlock* flow = continuationBefore(parent, beforeChild);
ASSERT(!beforeChild || is<RenderBlock>(*beforeChild->parent()));
RenderBoxModelObject* beforeChildParent = nullptr;
if (beforeChild)
beforeChildParent = downcast<RenderBoxModelObject>(beforeChild->parent());
else {
RenderBoxModelObject* continuation = flow->continuation();
if (continuation)
beforeChildParent = continuation;
else
beforeChildParent = flow;
}
if (child->isFloatingOrOutOfFlowPositioned()) {
m_builder.attachIgnoringContinuation(*beforeChildParent, WTFMove(child), beforeChild);
return;
}
bool childIsNormal = child->isInline() || child->style().columnSpan() == ColumnSpan::None;
bool bcpIsNormal = beforeChildParent->isInline() || beforeChildParent->style().columnSpan() == ColumnSpan::None;
bool flowIsNormal = flow->isInline() || flow->style().columnSpan() == ColumnSpan::None;
if (flow == beforeChildParent) {
m_builder.attachIgnoringContinuation(*flow, WTFMove(child), beforeChild);
return;
}
if (childIsNormal == bcpIsNormal) {
m_builder.attachIgnoringContinuation(*beforeChildParent, WTFMove(child), beforeChild);
return;
}
if (flowIsNormal == childIsNormal) {
m_builder.attachIgnoringContinuation(*flow, WTFMove(child)); return;
}
m_builder.attachIgnoringContinuation(*beforeChildParent, WTFMove(child), beforeChild);
}
void RenderTreeBuilder::Block::attachIgnoringContinuation(RenderBlock& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
if (beforeChild && beforeChild->parent() != &parent) {
RenderElement* beforeChildContainer = beforeChild->parent();
while (beforeChildContainer->parent() != &parent)
beforeChildContainer = beforeChildContainer->parent();
ASSERT(beforeChildContainer);
if (beforeChildContainer->isAnonymous()) {
RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!beforeChildContainer->isInline());
RenderElement* beforeChildAnonymousContainer = beforeChildContainer;
if (beforeChildAnonymousContainer->isAnonymousBlock()
#if ENABLE(FULLSCREEN_API)
|| beforeChildAnonymousContainer->isRenderFullScreen()
|| beforeChildAnonymousContainer->isRenderFullScreenPlaceholder()
#endif
) {
if (child->isInline() || beforeChild->parent()->firstChild() != beforeChild)
m_builder.attach(*beforeChild->parent(), WTFMove(child), beforeChild);
else
m_builder.attach(parent, WTFMove(child), beforeChild->parent());
return;
}
ASSERT(beforeChildAnonymousContainer->isTable());
if (child->isTablePart()) {
m_builder.attach(*beforeChildAnonymousContainer, WTFMove(child), beforeChild);
return;
}
beforeChild = m_builder.splitAnonymousBoxesAroundChild(parent, *beforeChild);
RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(beforeChild->parent() == &parent);
}
}
bool madeBoxesNonInline = false;
if (parent.childrenInline() && !child->isInline() && !child->isFloatingOrOutOfFlowPositioned()) {
m_builder.makeChildrenNonInline(parent, beforeChild);
madeBoxesNonInline = true;
if (beforeChild && beforeChild->parent() != &parent) {
beforeChild = beforeChild->parent();
ASSERT(beforeChild->isAnonymousBlock());
ASSERT(beforeChild->parent() == &parent);
}
} else if (!parent.childrenInline() && (child->isFloatingOrOutOfFlowPositioned() || child->isInline())) {
RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : parent.lastChild();
if (afterChild && afterChild->isAnonymousBlock()) {
m_builder.attach(downcast<RenderBlock>(*afterChild), WTFMove(child));
return;
}
if (child->isInline()) {
auto newBox = parent.createAnonymousBlock();
auto& box = *newBox;
m_builder.attachToRenderElement(parent, WTFMove(newBox), beforeChild);
m_builder.attach(box, WTFMove(child));
return;
}
}
parent.invalidateLineLayoutPath();
m_builder.attachToRenderElement(parent, WTFMove(child), beforeChild);
if (madeBoxesNonInline && is<RenderBlock>(parent.parent()) && parent.isAnonymousBlock())
removeLeftoverAnonymousBlock(parent);
}
void RenderTreeBuilder::Block::childBecameNonInline(RenderBlock& parent, RenderElement&)
{
m_builder.makeChildrenNonInline(parent);
if (parent.isAnonymousBlock() && is<RenderBlock>(parent.parent()))
removeLeftoverAnonymousBlock(parent);
}
void RenderTreeBuilder::Block::removeLeftoverAnonymousBlock(RenderBlock& anonymousBlock)
{
ASSERT(anonymousBlock.isAnonymousBlock());
ASSERT(!anonymousBlock.childrenInline());
ASSERT(anonymousBlock.parent());
if (anonymousBlock.continuation())
return;
auto* parent = anonymousBlock.parent();
if (is<RenderButton>(*parent) || is<RenderTextControl>(*parent) || is<RenderRubyAsBlock>(*parent) || is<RenderRubyRun>(*parent))
return;
moveAllChildrenToInternal(anonymousBlock, *parent);
auto toBeDestroyed = m_builder.detachFromRenderElement(*parent, anonymousBlock);
}
RenderPtr<RenderObject> RenderTreeBuilder::Block::detach(RenderBlock& parent, RenderObject& oldChild, CanCollapseAnonymousBlock canCollapseAnonymousBlock)
{
if (parent.renderTreeBeingDestroyed())
return m_builder.detachFromRenderElement(parent, oldChild);
auto prev = makeWeakPtr(oldChild.previousSibling());
auto next = makeWeakPtr(oldChild.nextSibling());
bool canMergeAnonymousBlocks = canMergeContiguousAnonymousBlocks(oldChild, prev.get(), next.get());
parent.invalidateLineLayoutPath();
auto takenChild = m_builder.detachFromRenderElement(parent, oldChild);
if (canMergeAnonymousBlocks && prev && next) {
prev->setNeedsLayoutAndPrefWidthsRecalc();
RenderBlock& nextBlock = downcast<RenderBlock>(*next);
RenderBlock& prevBlock = downcast<RenderBlock>(*prev);
if (prev->childrenInline() != next->childrenInline()) {
RenderBlock& inlineChildrenBlock = prev->childrenInline() ? prevBlock : nextBlock;
RenderBlock& blockChildrenBlock = prev->childrenInline() ? nextBlock : prevBlock;
ASSERT(!inlineChildrenBlock.continuation());
inlineChildrenBlock.setStyle(RenderStyle::createAnonymousStyleWithDisplay(parent.style(), DisplayType::Block));
auto blockToMove = m_builder.detachFromRenderElement(parent, inlineChildrenBlock);
RenderObject* beforeChild = prev == &inlineChildrenBlock ? blockChildrenBlock.firstChild() : nullptr;
m_builder.attachToRenderElementInternal(blockChildrenBlock, WTFMove(blockToMove), beforeChild);
next->setNeedsLayoutAndPrefWidthsRecalc();
if (&inlineChildrenBlock == &prevBlock)
prev = nullptr;
else
next = nullptr;
} else {
m_builder.moveAllChildrenIncludingFloats(nextBlock, prevBlock, RenderTreeBuilder::NormalizeAfterInsertion::No);
nextBlock.deleteLines();
m_builder.destroy(nextBlock);
}
}
if (canCollapseAnonymousBlock == CanCollapseAnonymousBlock::Yes && parent.canDropAnonymousBlockChild()) {
RenderObject* child = prev ? prev.get() : next.get();
if (canMergeAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling()) {
dropAnonymousBoxChild(parent, downcast<RenderBlock>(*child));
} else if ((prev && prev->isAnonymousBlock()) || (next && next->isAnonymousBlock())) {
RenderBlock& anonBlock = downcast<RenderBlock>((prev && prev->isAnonymousBlock()) ? *prev : *next);
if (canDropAnonymousBlock(anonBlock)) {
bool dropAnonymousBlock = true;
for (auto& sibling : childrenOfType<RenderObject>(parent)) {
if (&sibling == &anonBlock)
continue;
if (!sibling.isFloating()) {
dropAnonymousBlock = false;
break;
}
}
if (dropAnonymousBlock)
dropAnonymousBoxChild(parent, anonBlock);
}
}
}
if (!parent.firstChild()) {
if (parent.childrenInline())
parent.deleteLines();
}
return takenChild;
}
void RenderTreeBuilder::Block::dropAnonymousBoxChild(RenderBlock& parent, RenderBlock& child)
{
parent.setNeedsLayoutAndPrefWidthsRecalc();
parent.setChildrenInline(child.childrenInline());
auto* nextSibling = child.nextSibling();
auto toBeDeleted = m_builder.detachFromRenderElement(parent, child);
m_builder.moveAllChildren(child, parent, nextSibling, RenderTreeBuilder::NormalizeAfterInsertion::No);
child.deleteLines();
}
RenderPtr<RenderObject> RenderTreeBuilder::Block::detach(RenderBlockFlow& parent, RenderObject& child, CanCollapseAnonymousBlock canCollapseAnonymousBlock)
{
if (!parent.renderTreeBeingDestroyed()) {
auto* fragmentedFlow = parent.multiColumnFlow();
if (fragmentedFlow && fragmentedFlow != &child)
m_builder.multiColumnBuilder().multiColumnRelativeWillBeRemoved(*fragmentedFlow, child);
}
return detach(static_cast<RenderBlock&>(parent), child, canCollapseAnonymousBlock);
}
}