RenderTreeBuilderInline.cpp [plain text]
#include "config.h"
#include "RenderTreeBuilderInline.h"
#include "RenderChildIterator.h"
#include "RenderFullScreen.h"
#include "RenderInline.h"
#include "RenderTable.h"
#include "RenderTreeBuilderMultiColumn.h"
#include "RenderTreeBuilderTable.h"
namespace WebCore {
static bool canUseAsParentForContinuation(const RenderObject* renderer)
{
if (!renderer)
return false;
if (!is<RenderBlock>(renderer) && renderer->isAnonymous())
return false;
if (is<RenderTable>(renderer))
return false;
return true;
}
static RenderBoxModelObject* nextContinuation(RenderObject* renderer)
{
if (is<RenderInline>(*renderer) && !renderer->isReplaced())
return downcast<RenderInline>(*renderer).continuation();
return downcast<RenderBlock>(*renderer).inlineContinuation();
}
static RenderBoxModelObject* continuationBefore(RenderInline& parent, RenderObject* beforeChild)
{
if (beforeChild && beforeChild->parent() == &parent)
return &parent;
RenderBoxModelObject* curr = nextContinuation(&parent);
RenderBoxModelObject* nextToLast = &parent;
RenderBoxModelObject* last = &parent;
while (curr) {
if (beforeChild && beforeChild->parent() == curr) {
if (curr->firstChild() == beforeChild)
return last;
return curr;
}
nextToLast = last;
last = curr;
curr = nextContinuation(curr);
}
if (!beforeChild && !last->firstChild())
return nextToLast;
return last;
}
static RenderPtr<RenderInline> cloneAsContinuation(RenderInline& renderer)
{
RenderPtr<RenderInline> cloneInline = createRenderer<RenderInline>(*renderer.element(), RenderStyle::clone(renderer.style()));
cloneInline->initializeStyle();
cloneInline->setFragmentedFlowState(renderer.fragmentedFlowState());
cloneInline->setHasOutlineAutoAncestor(renderer.hasOutlineAutoAncestor());
cloneInline->setIsContinuation();
return cloneInline;
}
static RenderElement* inFlowPositionedInlineAncestor(RenderElement& renderer)
{
auto* ancestor = &renderer;
while (ancestor && ancestor->isRenderInline()) {
if (ancestor->isInFlowPositioned())
return ancestor;
ancestor = ancestor->parent();
}
return nullptr;
}
RenderTreeBuilder::Inline::Inline(RenderTreeBuilder& builder)
: m_builder(builder)
{
}
void RenderTreeBuilder::Inline::attach(RenderInline& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
auto* beforeChildOrPlaceholder = beforeChild;
if (auto* fragmentedFlow = parent.enclosingFragmentedFlow())
beforeChildOrPlaceholder = m_builder.multiColumnBuilder().resolveMovedChild(*fragmentedFlow, beforeChild);
if (parent.continuation()) {
insertChildToContinuation(parent, WTFMove(child), beforeChildOrPlaceholder);
return;
}
attachIgnoringContinuation(parent, WTFMove(child), beforeChildOrPlaceholder);
}
void RenderTreeBuilder::Inline::insertChildToContinuation(RenderInline& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
auto* flow = continuationBefore(parent, beforeChild);
RenderBoxModelObject* beforeChildAncestor = nullptr;
if (!beforeChild) {
auto* continuation = nextContinuation(flow);
beforeChildAncestor = continuation ? continuation : flow;
} else if (canUseAsParentForContinuation(beforeChild->parent()))
beforeChildAncestor = downcast<RenderBoxModelObject>(beforeChild->parent());
else if (beforeChild->parent()) {
auto* parent = beforeChild->parent();
while (parent && parent->parent() && parent->parent()->isAnonymous()) {
if (parent->isContinuation())
break;
parent = parent->parent();
}
ASSERT(parent && parent->parent());
beforeChildAncestor = downcast<RenderBoxModelObject>(parent->parent());
} else
ASSERT_NOT_REACHED();
if (child->isFloatingOrOutOfFlowPositioned())
return m_builder.attachIgnoringContinuation(*beforeChildAncestor, WTFMove(child), beforeChild);
if (flow == beforeChildAncestor)
return m_builder.attachIgnoringContinuation(*flow, WTFMove(child), beforeChild);
bool childInline = newChildIsInline(parent, *child);
if (childInline == beforeChildAncestor->isInline())
return m_builder.attachIgnoringContinuation(*beforeChildAncestor, WTFMove(child), beforeChild);
if (flow->isInline() == childInline)
return m_builder.attachIgnoringContinuation(*flow, WTFMove(child)); return m_builder.attachIgnoringContinuation(*beforeChildAncestor, WTFMove(child), beforeChild);
}
void RenderTreeBuilder::Inline::attachIgnoringContinuation(RenderInline& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
if (!beforeChild && parent.isAfterContent(parent.lastChild()))
beforeChild = parent.lastChild();
bool childInline = newChildIsInline(parent, *child);
if (!childInline && !child->isFloatingOrOutOfFlowPositioned()) {
auto newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent.style(), DisplayType::Block);
if (auto positionedAncestor = inFlowPositionedInlineAncestor(parent))
newStyle.setPosition(positionedAncestor->style().position());
auto newBox = createRenderer<RenderBlockFlow>(parent.document(), WTFMove(newStyle));
newBox->initializeStyle();
newBox->setIsContinuation();
RenderBoxModelObject* oldContinuation = parent.continuation();
if (oldContinuation)
oldContinuation->removeFromContinuationChain();
newBox->insertIntoContinuationChainAfter(parent);
splitFlow(parent, beforeChild, WTFMove(newBox), WTFMove(child), oldContinuation);
return;
}
auto& childToAdd = *child;
m_builder.attachToRenderElement(parent, WTFMove(child), beforeChild);
childToAdd.setNeedsLayoutAndPrefWidthsRecalc();
}
void RenderTreeBuilder::Inline::splitFlow(RenderInline& parent, RenderObject* beforeChild, RenderPtr<RenderBlock> newBlockBox, RenderPtr<RenderObject> child, RenderBoxModelObject* oldCont)
{
auto& addedBlockBox = *newBlockBox;
RenderBlock* pre = nullptr;
RenderBlock* block = parent.containingBlock();
block->deleteLines();
RenderPtr<RenderBlock> createdPre;
bool madeNewBeforeBlock = false;
if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) {
pre = block;
pre->removePositionedObjects(nullptr);
if (is<RenderBlockFlow>(*pre))
downcast<RenderBlockFlow>(*pre).removeFloatingObjects();
block = block->containingBlock();
} else {
createdPre = block->createAnonymousBlock();
pre = createdPre.get();
madeNewBeforeBlock = true;
}
auto createdPost = pre->createAnonymousBoxWithSameTypeAs(*block);
auto& post = downcast<RenderBlock>(*createdPost);
RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
if (createdPre)
m_builder.attachToRenderElementInternal(*block, WTFMove(createdPre), boxFirst);
m_builder.attachToRenderElementInternal(*block, WTFMove(newBlockBox), boxFirst);
m_builder.attachToRenderElementInternal(*block, WTFMove(createdPost), boxFirst);
block->setChildrenInline(false);
if (madeNewBeforeBlock) {
RenderObject* o = boxFirst;
while (o) {
RenderObject* no = o;
o = no->nextSibling();
auto childToMove = m_builder.detachFromRenderElement(*block, *no);
m_builder.attachToRenderElementInternal(*pre, WTFMove(childToMove));
no->setNeedsLayoutAndPrefWidthsRecalc();
}
}
splitInlines(parent, pre, &post, &addedBlockBox, beforeChild, oldCont);
addedBlockBox.setChildrenInline(false);
m_builder.attach(addedBlockBox, WTFMove(child));
pre->setNeedsLayoutAndPrefWidthsRecalc();
block->setNeedsLayoutAndPrefWidthsRecalc();
post.setNeedsLayoutAndPrefWidthsRecalc();
}
void RenderTreeBuilder::Inline::splitInlines(RenderInline& parent, RenderBlock* fromBlock, RenderBlock* toBlock, RenderBlock* middleBlock, RenderObject* beforeChild, RenderBoxModelObject* oldCont)
{
RenderPtr<RenderInline> cloneInline = cloneAsContinuation(parent);
#if ENABLE(FULLSCREEN_API)
const Element* fullScreenElement = parent.document().webkitCurrentFullScreenElement();
if (fullScreenElement && beforeChild && beforeChild->node() == fullScreenElement)
beforeChild = parent.document().fullScreenRenderer();
#endif
for (RenderObject* rendererToMove = beforeChild; rendererToMove;) {
RenderObject* nextSibling = rendererToMove->nextSibling();
if (rendererToMove->parent() != &parent) {
auto* anonymousParent = rendererToMove->parent();
while (anonymousParent && anonymousParent->parent() != &parent) {
ASSERT(anonymousParent->isAnonymous());
anonymousParent = anonymousParent->parent();
}
if (!anonymousParent) {
ASSERT_NOT_REACHED();
break;
}
if (!rendererToMove->previousSibling()) {
rendererToMove = anonymousParent;
nextSibling = anonymousParent->nextSibling();
} else if (!rendererToMove->nextSibling()) {
nextSibling = anonymousParent->nextSibling();
}
}
auto childToMove = m_builder.detachFromRenderElement(*rendererToMove->parent(), *rendererToMove);
m_builder.attachIgnoringContinuation(*cloneInline, WTFMove(childToMove));
rendererToMove->setNeedsLayoutAndPrefWidthsRecalc();
rendererToMove = nextSibling;
}
cloneInline->insertIntoContinuationChainAfter(*middleBlock);
if (oldCont)
oldCont->insertIntoContinuationChainAfter(*cloneInline);
RenderBoxModelObject* current = downcast<RenderBoxModelObject>(parent.parent());
RenderBoxModelObject* currentChild = &parent;
unsigned splitDepth = 1;
const unsigned cMaxSplitDepth = 200;
while (current && current != fromBlock) {
if (splitDepth < cMaxSplitDepth) {
RenderPtr<RenderInline> cloneChild = WTFMove(cloneInline);
cloneInline = cloneAsContinuation(downcast<RenderInline>(*current));
m_builder.attachIgnoringContinuation(*cloneInline, WTFMove(cloneChild));
cloneInline->insertIntoContinuationChainAfter(*current);
for (auto* sibling = currentChild->nextSibling(); sibling;) {
auto* next = sibling->nextSibling();
auto childToMove = m_builder.detachFromRenderElement(*current, *sibling);
m_builder.attachIgnoringContinuation(*cloneInline, WTFMove(childToMove));
sibling->setNeedsLayoutAndPrefWidthsRecalc();
sibling = next;
}
}
currentChild = current;
current = downcast<RenderBoxModelObject>(current->parent());
++splitDepth;
}
for (auto& cloneBlockChild : childrenOfType<RenderBlock>(*cloneInline))
cloneBlockChild.resetEnclosingFragmentedFlowAndChildInfoIncludingDescendants();
m_builder.attachToRenderElementInternal(*toBlock, WTFMove(cloneInline));
for (auto* current = currentChild->nextSibling(); current;) {
auto* next = current->nextSibling();
auto childToMove = m_builder.detachFromRenderElement(*fromBlock, *current);
m_builder.attachToRenderElementInternal(*toBlock, WTFMove(childToMove));
current = next;
}
}
bool RenderTreeBuilder::Inline::newChildIsInline(const RenderInline& parent, const RenderObject& child)
{
return child.isInline() || (m_builder.tableBuilder().childRequiresTable(parent, child) && parent.style().display() == DisplayType::Inline);
}
void RenderTreeBuilder::Inline::childBecameNonInline(RenderInline& parent, RenderElement& child)
{
auto newBox = parent.containingBlock()->createAnonymousBlock();
newBox->setIsContinuation();
auto* oldContinuation = parent.continuation();
if (oldContinuation)
oldContinuation->removeFromContinuationChain();
newBox->insertIntoContinuationChainAfter(parent);
auto* beforeChild = child.nextSibling();
auto removedChild = m_builder.detachFromRenderElement(parent, child);
splitFlow(parent, beforeChild, WTFMove(newBox), WTFMove(removedChild), oldContinuation);
}
}