#include "config.h"
#include "RenderRuby.h"
#include "RenderIterator.h"
#include "RenderRubyRun.h"
#include "RenderStyle.h"
#include "StyleInheritedData.h"
#include <wtf/IsoMallocInlines.h>
#include <wtf/RefPtr.h>
namespace WebCore {
WTF_MAKE_ISO_ALLOCATED_IMPL(RenderRubyAsInline);
WTF_MAKE_ISO_ALLOCATED_IMPL(RenderRubyAsBlock);
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() == INLINE_BLOCK));
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() == BEFORE;
}
static inline bool isRubyAfterBlock(const RenderObject* object)
{
return isAnonymousRubyInlineBlock(object)
&& !object->nextSibling()
&& downcast<RenderBlock>(*object).firstChild()
&& downcast<RenderBlock>(*object).firstChild()->style().styleType() == 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(), INLINE_BLOCK));
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);
}
static inline RenderRubyRun& findRubyRunParent(RenderObject& child)
{
return *lineageOfType<RenderRubyRun>(child).first();
}
RenderRubyAsInline::RenderRubyAsInline(Element& element, RenderStyle&& style)
: RenderInline(element, WTFMove(style))
{
}
RenderRubyAsInline::~RenderRubyAsInline() = default;
void RenderRubyAsInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderInline::styleDidChange(diff, oldStyle);
propagateStyleToAnonymousChildren(PropagateToAllChildren);
}
void RenderRubyAsInline::addChild(RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
if (child->isBeforeContent()) {
if (child->isInline()) {
RenderInline::addChild(WTFMove(child), firstChild());
} else {
RenderBlock* beforeBlock = rubyBeforeBlock(this);
if (!beforeBlock) {
auto newBlock = createAnonymousRubyInlineBlock(*this);
beforeBlock = newBlock.get();
RenderInline::addChild(WTFMove(newBlock), firstChild());
}
beforeBlock->addChild(WTFMove(child));
}
return;
}
if (child->isAfterContent()) {
if (child->isInline()) {
RenderInline::addChild(WTFMove(child));
} else {
RenderBlock* afterBlock = rubyAfterBlock(this);
if (!afterBlock) {
auto newBlock = createAnonymousRubyInlineBlock(*this);
afterBlock = newBlock.get();
RenderInline::addChild(WTFMove(newBlock));
}
afterBlock->addChild(WTFMove(child));
}
return;
}
if (child->isRubyRun()) {
RenderInline::addChild(WTFMove(child), beforeChild);
return;
}
if (beforeChild && !isAfterContent(beforeChild)) {
ASSERT(!beforeChild->isRubyRun());
RenderElement* run = beforeChild->parent();
while (run && !run->isRubyRun())
run = run->parent();
if (run) {
run->addChild(WTFMove(child), beforeChild);
return;
}
ASSERT_NOT_REACHED(); }
RenderRubyRun* lastRun = lastRubyRun(this);
if (!lastRun || lastRun->hasRubyText()) {
auto newRun = RenderRubyRun::staticCreateRubyRun(this);
lastRun = newRun.get();
RenderInline::addChild(WTFMove(newRun), beforeChild);
}
lastRun->addChild(WTFMove(child));
}
RenderPtr<RenderObject> RenderRubyAsInline::takeChild(RenderObject& child)
{
if (child.parent() == this) {
#ifndef ASSERT_DISABLED
ASSERT(isRubyChildForNormalRemoval(child));
#endif
return RenderInline::takeChild(child);
}
if (isAnonymousRubyInlineBlock(child.parent())) {
ASSERT(child.isBeforeContent() || child.isAfterContent());
auto& parent = *child.parent();
auto takenChild = parent.takeChild(child);
parent.removeFromParentAndDestroy();
return takenChild;
}
return findRubyRunParent(child).takeChild(child);
}
RenderRubyAsBlock::RenderRubyAsBlock(Element& element, RenderStyle&& style)
: RenderBlockFlow(element, WTFMove(style))
{
}
RenderRubyAsBlock::~RenderRubyAsBlock() = default;
void RenderRubyAsBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderBlockFlow::styleDidChange(diff, oldStyle);
propagateStyleToAnonymousChildren(PropagateToAllChildren);
}
void RenderRubyAsBlock::addChild(RenderPtr<RenderObject> child, RenderObject* beforeChild)
{
if (child->isBeforeContent()) {
if (child->isInline()) {
RenderBlockFlow::addChild(WTFMove(child), firstChild());
} else {
RenderBlock* beforeBlock = rubyBeforeBlock(this);
if (!beforeBlock) {
auto newBlock = createAnonymousRubyInlineBlock(*this);
beforeBlock = newBlock.get();
RenderBlockFlow::addChild(WTFMove(newBlock), firstChild());
}
beforeBlock->addChild(WTFMove(child));
}
return;
}
if (child->isAfterContent()) {
if (child->isInline()) {
RenderBlockFlow::addChild(WTFMove(child));
} else {
RenderBlock* afterBlock = rubyAfterBlock(this);
if (!afterBlock) {
auto newBlock = createAnonymousRubyInlineBlock(*this);
afterBlock = newBlock.get();
RenderBlockFlow::addChild(WTFMove(newBlock));
}
afterBlock->addChild(WTFMove(child));
}
return;
}
if (child->isRubyRun()) {
RenderBlockFlow::addChild(WTFMove(child), beforeChild);
return;
}
if (beforeChild && !isAfterContent(beforeChild)) {
ASSERT(!beforeChild->isRubyRun());
RenderElement* run = beforeChild->parent();
while (run && !run->isRubyRun())
run = run->parent();
if (run) {
run->addChild(WTFMove(child), beforeChild);
return;
}
ASSERT_NOT_REACHED(); }
RenderRubyRun* lastRun = lastRubyRun(this);
if (!lastRun || lastRun->hasRubyText()) {
auto newRun = RenderRubyRun::staticCreateRubyRun(this);
lastRun = newRun.get();
RenderBlockFlow::addChild(WTFMove(newRun), beforeChild);
}
lastRun->addChild(WTFMove(child));
}
RenderPtr<RenderObject> RenderRubyAsBlock::takeChild(RenderObject& child)
{
if (child.parent() == this) {
#ifndef ASSERT_DISABLED
ASSERT(isRubyChildForNormalRemoval(child));
#endif
return RenderBlockFlow::takeChild(child);
}
if (isAnonymousRubyInlineBlock(child.parent())) {
ASSERT(child.isBeforeContent() || child.isAfterContent());
auto& parent = *child.parent();
auto takenChild = parent.takeChild(child);
parent.removeFromParentAndDestroy();
return takenChild;
}
return findRubyRunParent(child).takeChild(child);
}
}