#include "config.h"
#include "RenderRuby.h"
#include "RenderIterator.h"
#include "RenderRubyRun.h"
#include "RenderStyle.h"
#include "StyleInheritedData.h"
#include <wtf/RefPtr.h>
namespace WebCore {
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.isRenderMultiColumnFlowThread()
|| 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 RenderBlock* createAnonymousRubyInlineBlock(RenderObject& ruby)
{
RenderBlock* newBlock = new 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()
{
}
void RenderRubyAsInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderInline::styleDidChange(diff, oldStyle);
propagateStyleToAnonymousChildren(PropagateToAllChildren);
}
void RenderRubyAsInline::addChild(RenderObject* child, RenderObject* beforeChild)
{
if (child->isBeforeContent()) {
if (child->isInline()) {
RenderInline::addChild(child, firstChild());
} else {
RenderBlock* beforeBlock = rubyBeforeBlock(this);
if (!beforeBlock) {
beforeBlock = createAnonymousRubyInlineBlock(*this);
RenderInline::addChild(beforeBlock, firstChild());
}
beforeBlock->addChild(child);
}
return;
}
if (child->isAfterContent()) {
if (child->isInline()) {
RenderInline::addChild(child);
} else {
RenderBlock* afterBlock = rubyAfterBlock(this);
if (!afterBlock) {
afterBlock = createAnonymousRubyInlineBlock(*this);
RenderInline::addChild(afterBlock);
}
afterBlock->addChild(child);
}
return;
}
if (child->isRubyRun()) {
RenderInline::addChild(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(child, beforeChild);
return;
}
ASSERT_NOT_REACHED(); }
RenderRubyRun* lastRun = lastRubyRun(this);
if (!lastRun || lastRun->hasRubyText()) {
lastRun = RenderRubyRun::staticCreateRubyRun(this);
RenderInline::addChild(lastRun, beforeChild);
}
lastRun->addChild(child);
}
void RenderRubyAsInline::removeChild(RenderObject& child)
{
if (child.parent() == this) {
#ifndef ASSERT_DISABLED
ASSERT(isRubyChildForNormalRemoval(child));
#endif
RenderInline::removeChild(child);
return;
}
if (isAnonymousRubyInlineBlock(child.parent())) {
ASSERT(child.isBeforeContent() || child.isAfterContent());
child.parent()->removeChild(child);
removeChild(*child.parent());
return;
}
findRubyRunParent(child).removeChild(child);
}
RenderRubyAsBlock::RenderRubyAsBlock(Element& element, RenderStyle&& style)
: RenderBlockFlow(element, WTFMove(style))
{
}
RenderRubyAsBlock::~RenderRubyAsBlock()
{
}
void RenderRubyAsBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderBlockFlow::styleDidChange(diff, oldStyle);
propagateStyleToAnonymousChildren(PropagateToAllChildren);
}
void RenderRubyAsBlock::addChild(RenderObject* child, RenderObject* beforeChild)
{
if (child->isBeforeContent()) {
if (child->isInline()) {
RenderBlockFlow::addChild(child, firstChild());
} else {
RenderBlock* beforeBlock = rubyBeforeBlock(this);
if (!beforeBlock) {
beforeBlock = createAnonymousRubyInlineBlock(*this);
RenderBlockFlow::addChild(beforeBlock, firstChild());
}
beforeBlock->addChild(child);
}
return;
}
if (child->isAfterContent()) {
if (child->isInline()) {
RenderBlockFlow::addChild(child);
} else {
RenderBlock* afterBlock = rubyAfterBlock(this);
if (!afterBlock) {
afterBlock = createAnonymousRubyInlineBlock(*this);
RenderBlockFlow::addChild(afterBlock);
}
afterBlock->addChild(child);
}
return;
}
if (child->isRubyRun()) {
RenderBlockFlow::addChild(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(child, beforeChild);
return;
}
ASSERT_NOT_REACHED(); }
RenderRubyRun* lastRun = lastRubyRun(this);
if (!lastRun || lastRun->hasRubyText()) {
lastRun = RenderRubyRun::staticCreateRubyRun(this);
RenderBlockFlow::addChild(lastRun, beforeChild);
}
lastRun->addChild(child);
}
void RenderRubyAsBlock::removeChild(RenderObject& child)
{
if (child.parent() == this) {
#ifndef ASSERT_DISABLED
ASSERT(isRubyChildForNormalRemoval(child));
#endif
RenderBlockFlow::removeChild(child);
return;
}
if (isAnonymousRubyInlineBlock(child.parent())) {
ASSERT(child.isBeforeContent() || child.isAfterContent());
child.parent()->removeChild(child);
removeChild(*child.parent());
return;
}
findRubyRunParent(child).removeChild(child);
}
}