RenderTreeBuilderRuby.cpp [plain text]
#include "config.h"
#include "RenderTreeBuilderRuby.h"
#include "RenderAncestorIterator.h"
#include "RenderRuby.h"
#include "RenderRubyBase.h"
#include "RenderRubyRun.h"
#include "RenderTreeBuilder.h"
#include "RenderTreeBuilderBlock.h"
#include "RenderTreeBuilderBlockFlow.h"
#include "RenderTreeBuilderInline.h"
namespace WebCore {
static inline RenderRubyRun& findRubyRunParent(RenderObject& child)
{
return *lineageOfType<RenderRubyRun>(child).first();
}
static inline bool isAnonymousRubyInlineBlock(const RenderObject* object)
{
ASSERT(!object
|| !isRuby(object->parent())
|| is<RenderRubyRun>(*object)
|| (object->isInline() && (object->isBeforeContent() || object->isAfterContent()))
|| (object->isAnonymous() && is<RenderBlock>(*object) && object->style().display() == DisplayType::InlineBlock));
return object
&& isRuby(object->parent())
&& is<RenderBlock>(*object)
&& !is<RenderRubyRun>(*object);
}
static inline bool isRubyBeforeBlock(const RenderObject* object)
{
return isAnonymousRubyInlineBlock(object)
&& !object->previousSibling()
&& downcast<RenderBlock>(*object).firstChild()
&& downcast<RenderBlock>(*object).firstChild()->style().styleType() == PseudoId::Before;
}
static inline bool isRubyAfterBlock(const RenderObject* object)
{
return isAnonymousRubyInlineBlock(object)
&& !object->nextSibling()
&& downcast<RenderBlock>(*object).firstChild()
&& downcast<RenderBlock>(*object).firstChild()->style().styleType() == PseudoId::After;
}
#ifndef ASSERT_DISABLED
static inline bool isRubyChildForNormalRemoval(const RenderObject& object)
{
return object.isRubyRun()
|| object.isBeforeContent()
|| object.isAfterContent()
|| object.isRenderMultiColumnFlow()
|| object.isRenderMultiColumnSet()
|| isAnonymousRubyInlineBlock(&object);
}
#endif
static inline RenderBlock* rubyBeforeBlock(const RenderElement* ruby)
{
RenderObject* child = ruby->firstChild();
return isRubyBeforeBlock(child) ? downcast<RenderBlock>(child) : nullptr;
}
static inline RenderBlock* rubyAfterBlock(const RenderElement* ruby)
{
RenderObject* child = ruby->lastChild();
return isRubyAfterBlock(child) ? downcast<RenderBlock>(child) : nullptr;
}
static auto createAnonymousRubyInlineBlock(RenderObject& ruby)
{
auto newBlock = createRenderer<RenderBlockFlow>(ruby.document(), RenderStyle::createAnonymousStyleWithDisplay(ruby.style(), DisplayType::InlineBlock));
newBlock->initializeStyle();
return newBlock;
}
static RenderRubyRun* lastRubyRun(const RenderElement* ruby)
{
RenderObject* child = ruby->lastChild();
if (child && !is<RenderRubyRun>(*child))
child = child->previousSibling();
if (!is<RenderRubyRun>(child)) {
ASSERT(!child || child->isBeforeContent() || child == rubyBeforeBlock(ruby));
return nullptr;
}
return downcast<RenderRubyRun>(child);
}
RenderTreeBuilder::Ruby::Ruby(RenderTreeBuilder& builder)
: m_builder(builder)
{
}
void RenderTreeBuilder::Ruby::moveInlineChildren(RenderRubyBase& from, RenderRubyBase& to, RenderObject* beforeChild)
{
ASSERT(from.childrenInline());
if (!from.firstChild())
return;
RenderBlock* toBlock = nullptr;
if (to.childrenInline()) {
toBlock = &to;
} else {
auto* lastChild = to.lastChild();
if (lastChild && lastChild->isAnonymousBlock() && lastChild->childrenInline())
toBlock = downcast<RenderBlock>(lastChild);
else {
auto newToBlock = to.createAnonymousBlock();
toBlock = newToBlock.get();
m_builder.attachToRenderElementInternal(to, WTFMove(newToBlock));
}
}
ASSERT(toBlock);
m_builder.moveChildren(from, *toBlock, from.firstChild(), beforeChild, RenderTreeBuilder::NormalizeAfterInsertion::No);
}
void RenderTreeBuilder::Ruby::moveBlockChildren(RenderRubyBase& from, RenderRubyBase& to, RenderObject* beforeChild)
{
ASSERT(!from.childrenInline());
if (!from.firstChild())
return;
if (to.childrenInline())
m_builder.makeChildrenNonInline(to);
auto* firstChildHere = from.firstChild();
auto* lastChildThere = to.lastChild();
if (firstChildHere->isAnonymousBlock() && firstChildHere->childrenInline()
&& lastChildThere && lastChildThere->isAnonymousBlock() && lastChildThere->childrenInline()) {
auto* anonBlockHere = downcast<RenderBlock>(firstChildHere);
auto* anonBlockThere = downcast<RenderBlock>(lastChildThere);
m_builder.moveAllChildren(*anonBlockHere, *anonBlockThere, RenderTreeBuilder::NormalizeAfterInsertion::Yes);
anonBlockHere->deleteLines();
m_builder.destroy(*anonBlockHere);
}
m_builder.moveChildren(from, to, from.firstChild(), beforeChild, RenderTreeBuilder::NormalizeAfterInsertion::No);
}
void RenderTreeBuilder::Ruby::moveChildren(RenderRubyBase& from, RenderRubyBase& to)
{
moveChildrenInternal(from, to);
from.addFloatsToNewParent(to);
}
void RenderTreeBuilder::Ruby::moveChildrenInternal(RenderRubyBase& from, RenderRubyBase& to, RenderObject* beforeChild)
{
if (beforeChild && beforeChild->parent() != &from)
beforeChild = m_builder.splitAnonymousBoxesAroundChild(from, *beforeChild);
if (from.childrenInline())
moveInlineChildren(from, to, beforeChild);
else
moveBlockChildren(from, to, beforeChild);
from.setNeedsLayoutAndPrefWidthsRecalc();
to.setNeedsLayoutAndPrefWidthsRecalc();
}
void RenderTreeBuilder::Ruby::attach(RenderRubyRun& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
if (child->isRubyText()) {
if (!beforeChild) {
ASSERT(!parent.hasRubyText());
m_builder.blockFlowBuilder().attach(parent, WTFMove(child), parent.firstChild());
return;
}
if (beforeChild->isRubyText()) {
ASSERT(beforeChild->parent() == &parent);
RenderElement* ruby = parent.parent();
ASSERT(isRuby(ruby));
auto newRun = RenderRubyRun::staticCreateRubyRun(ruby);
m_builder.attach(*ruby, WTFMove(newRun), parent.nextSibling());
m_builder.blockFlowBuilder().attach(parent, WTFMove(child), beforeChild);
auto takenBeforeChild = m_builder.blockBuilder().detach(parent, *beforeChild);
m_builder.attach(*newRun, WTFMove(takenBeforeChild));
return;
}
if (parent.hasRubyBase()) {
RenderElement* ruby = parent.parent();
auto newRun = RenderRubyRun::staticCreateRubyRun(ruby);
auto& run = *newRun;
m_builder.attach(*ruby, WTFMove(newRun), &parent);
m_builder.attach(run, WTFMove(child));
moveChildrenInternal(rubyBaseSafe(parent), rubyBaseSafe(run), beforeChild);
}
return;
}
if (beforeChild && beforeChild->isRubyText())
beforeChild = nullptr;
m_builder.attach(rubyBaseSafe(parent), WTFMove(child), beforeChild);
}
RenderElement& RenderTreeBuilder::Ruby::findOrCreateParentForChild(RenderRubyAsBlock& parent, const RenderObject& child, RenderObject*& beforeChild)
{
if (child.isBeforeContent()) {
if (child.isInline())
return parent;
auto* beforeBlock = rubyBeforeBlock(&parent);
if (!beforeBlock) {
auto newBlock = createAnonymousRubyInlineBlock(parent);
beforeBlock = newBlock.get();
m_builder.blockFlowBuilder().attach(parent, WTFMove(newBlock), parent.firstChild());
}
beforeChild = nullptr;
return *beforeBlock;
}
if (child.isAfterContent()) {
if (child.isInline())
return parent;
auto* afterBlock = rubyAfterBlock(&parent);
if (!afterBlock) {
auto newBlock = createAnonymousRubyInlineBlock(parent);
afterBlock = newBlock.get();
m_builder.blockFlowBuilder().attach(parent, WTFMove(newBlock), nullptr);
}
beforeChild = nullptr;
return *afterBlock;
}
if (child.isRubyRun())
return parent;
if (beforeChild && !parent.isAfterContent(beforeChild)) {
ASSERT(!beforeChild->isRubyRun());
auto* run = beforeChild->parent();
while (run && !run->isRubyRun())
run = run->parent();
if (run)
return *run;
ASSERT_NOT_REACHED(); }
auto* lastRun = lastRubyRun(&parent);
if (!lastRun || lastRun->hasRubyText()) {
auto newRun = RenderRubyRun::staticCreateRubyRun(&parent);
lastRun = newRun.get();
m_builder.blockFlowBuilder().attach(parent, WTFMove(newRun), beforeChild);
}
beforeChild = nullptr;
return *lastRun;
}
RenderElement& RenderTreeBuilder::Ruby::findOrCreateParentForChild(RenderRubyAsInline& parent, const RenderObject& child, RenderObject*& beforeChild)
{
if (child.isBeforeContent()) {
if (child.isInline())
return parent;
auto* beforeBlock = rubyBeforeBlock(&parent);
if (!beforeBlock) {
auto newBlock = createAnonymousRubyInlineBlock(parent);
beforeBlock = newBlock.get();
m_builder.inlineBuilder().attach(parent, WTFMove(newBlock), parent.firstChild());
}
beforeChild = nullptr;
return *beforeBlock;
}
if (child.isAfterContent()) {
if (child.isInline())
return parent;
auto* afterBlock = rubyAfterBlock(&parent);
if (!afterBlock) {
auto newBlock = createAnonymousRubyInlineBlock(parent);
afterBlock = newBlock.get();
m_builder.inlineBuilder().attach(parent, WTFMove(newBlock), nullptr);
}
beforeChild = nullptr;
return *afterBlock;
}
if (child.isRubyRun())
return parent;
if (beforeChild && !parent.isAfterContent(beforeChild)) {
ASSERT(!beforeChild->isRubyRun());
auto* run = beforeChild->parent();
while (run && !run->isRubyRun())
run = run->parent();
if (run)
return *run;
ASSERT_NOT_REACHED(); }
auto* lastRun = lastRubyRun(&parent);
if (!lastRun || lastRun->hasRubyText()) {
auto newRun = RenderRubyRun::staticCreateRubyRun(&parent);
lastRun = newRun.get();
m_builder.inlineBuilder().attach(parent, WTFMove(newRun), beforeChild);
}
beforeChild = nullptr;
return *lastRun;
}
RenderRubyBase& RenderTreeBuilder::Ruby::rubyBaseSafe(RenderRubyRun& rubyRun)
{
auto* base = rubyRun.rubyBase();
if (!base) {
auto newBase = rubyRun.createRubyBase();
base = newBase.get();
m_builder.blockFlowBuilder().attach(rubyRun, WTFMove(newBase), nullptr);
}
return *base;
}
RenderPtr<RenderObject> RenderTreeBuilder::Ruby::detach(RenderRubyAsInline& parent, RenderObject& child)
{
if (child.parent() == &parent) {
#ifndef ASSERT_DISABLED
ASSERT(isRubyChildForNormalRemoval(child));
#endif
return m_builder.detachFromRenderElement(parent, child);
}
if (isAnonymousRubyInlineBlock(child.parent())) {
ASSERT(child.isBeforeContent() || child.isAfterContent());
auto& parent = *child.parent();
auto takenChild = m_builder.detach(parent, child);
m_builder.destroy(parent);
return takenChild;
}
return m_builder.detach(findRubyRunParent(child), child);
}
RenderPtr<RenderObject> RenderTreeBuilder::Ruby::detach(RenderRubyAsBlock& parent, RenderObject& child)
{
if (child.parent() == &parent) {
#ifndef ASSERT_DISABLED
ASSERT(isRubyChildForNormalRemoval(child));
#endif
return m_builder.blockBuilder().detach(parent, child);
}
if (isAnonymousRubyInlineBlock(child.parent())) {
ASSERT(child.isBeforeContent() || child.isAfterContent());
auto& parent = *child.parent();
auto takenChild = m_builder.detach(parent, child);
m_builder.destroy(parent);
return takenChild;
}
return m_builder.detach(findRubyRunParent(child), child);
}
RenderPtr<RenderObject> RenderTreeBuilder::Ruby::detach(RenderRubyRun& parent, RenderObject& child)
{
if (!parent.beingDestroyed() && !parent.renderTreeBeingDestroyed() && child.isRubyText()) {
RenderRubyBase* base = parent.rubyBase();
RenderObject* rightNeighbour = parent.nextSibling();
if (base && is<RenderRubyRun>(rightNeighbour)) {
RenderRubyRun& rightRun = downcast<RenderRubyRun>(*rightNeighbour);
if (rightRun.hasRubyBase()) {
RenderRubyBase* rightBase = rightRun.rubyBase();
moveChildren(*rightBase, *base);
m_builder.move(parent, rightRun, *base, RenderTreeBuilder::NormalizeAfterInsertion::No);
m_builder.move(rightRun, parent, *rightBase, RenderTreeBuilder::NormalizeAfterInsertion::No);
ASSERT(!parent.rubyBase()->firstChild());
}
}
}
auto takenChild = m_builder.blockBuilder().detach(parent, child);
if (!parent.beingDestroyed() && !parent.renderTreeBeingDestroyed()) {
RenderBlock* base = parent.rubyBase();
if (base && !base->firstChild()) {
auto takenBase = m_builder.blockBuilder().detach(parent, *base);
base->deleteLines();
}
}
return takenChild;
}
}