#include "config.h"
#include "RenderRubyRun.h"
#include "RenderRubyBase.h"
#include "RenderRubyText.h"
#include "RenderText.h"
#include "RenderView.h"
#include "StyleInheritedData.h"
#include <wtf/StackStats.h>
namespace WebCore {
RenderRubyRun::RenderRubyRun(Document& document, PassRef<RenderStyle> style)
: RenderBlockFlow(document, WTF::move(style))
{
setReplaced(true);
setInline(true);
}
RenderRubyRun::~RenderRubyRun()
{
}
bool RenderRubyRun::hasRubyText() const
{
return firstChild() && firstChild()->isRubyText();
}
bool RenderRubyRun::hasRubyBase() const
{
return lastChild() && lastChild()->isRubyBase();
}
bool RenderRubyRun::isEmpty() const
{
return !hasRubyText() && !hasRubyBase();
}
RenderRubyText* RenderRubyRun::rubyText() const
{
RenderObject* child = firstChild();
ASSERT(!child || !child->isRubyText() || !child->isFloatingOrOutOfFlowPositioned());
return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0;
}
RenderRubyBase* RenderRubyRun::rubyBase() const
{
RenderObject* child = lastChild();
return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0;
}
RenderRubyBase* RenderRubyRun::rubyBaseSafe()
{
RenderRubyBase* base = rubyBase();
if (!base) {
base = createRubyBase();
RenderBlockFlow::addChild(base);
}
return base;
}
RenderBlock* RenderRubyRun::firstLineBlock() const
{
return 0;
}
void RenderRubyRun::updateFirstLetter()
{
}
bool RenderRubyRun::isChildAllowed(const RenderObject& child, const RenderStyle&) const
{
return child.isInline() || child.isRubyText();
}
void RenderRubyRun::addChild(RenderObject* child, RenderObject* beforeChild)
{
ASSERT(child);
if (child->isRubyText()) {
if (!beforeChild) {
ASSERT(!hasRubyText());
RenderBlockFlow::addChild(child, firstChild());
} else if (beforeChild->isRubyText()) {
ASSERT(beforeChild->parent() == this);
RenderElement* ruby = parent();
ASSERT(ruby->isRuby());
RenderBlock* newRun = staticCreateRubyRun(ruby);
ruby->addChild(newRun, nextSibling());
RenderBlockFlow::addChild(child, beforeChild);
RenderBlockFlow::removeChild(*beforeChild);
newRun->addChild(beforeChild);
} else if (hasRubyBase()) {
RenderElement* ruby = parent();
RenderRubyRun* newRun = staticCreateRubyRun(ruby);
ruby->addChild(newRun, this);
newRun->addChild(child);
rubyBaseSafe()->moveChildren(newRun->rubyBaseSafe(), beforeChild);
}
} else {
if (beforeChild && beforeChild->isRubyText())
beforeChild = 0;
rubyBaseSafe()->addChild(child, beforeChild);
}
}
RenderObject* RenderRubyRun::removeChild(RenderObject& child)
{
if (!beingDestroyed() && !documentBeingDestroyed() && child.isRubyText()) {
RenderRubyBase* base = rubyBase();
RenderObject* rightNeighbour = nextSibling();
if (base && rightNeighbour && rightNeighbour->isRubyRun()) {
RenderRubyRun* rightRun = toRenderRubyRun(rightNeighbour);
if (rightRun->hasRubyBase()) {
RenderRubyBase* rightBase = rightRun->rubyBaseSafe();
rightBase->mergeChildrenWithBase(base);
moveChildTo(rightRun, base);
rightRun->moveChildTo(this, rightBase);
ASSERT(!rubyBase()->firstChild());
}
}
}
RenderObject* next = RenderBlockFlow::removeChild(child);
if (!beingDestroyed() && !documentBeingDestroyed()) {
RenderBlock* base = rubyBase();
if (base && !base->firstChild()) {
next = RenderBlockFlow::removeChild(*base);
base->deleteLines();
base->destroy();
}
if (isEmpty()) {
parent()->removeChild(*this);
deleteLines();
destroy();
next = nullptr;
}
}
return next;
}
RenderRubyBase* RenderRubyRun::createRubyBase() const
{
auto newStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), BLOCK);
newStyle.get().setTextAlign(CENTER); auto renderer = new RenderRubyBase(document(), WTF::move(newStyle));
renderer->initializeStyle();
return renderer;
}
RenderRubyRun* RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby)
{
ASSERT(parentRuby && parentRuby->isRuby());
auto renderer = new RenderRubyRun(parentRuby->document(), RenderStyle::createAnonymousStyleWithDisplay(&parentRuby->style(), INLINE_BLOCK));
renderer->initializeStyle();
return renderer;
}
RenderObject* RenderRubyRun::layoutSpecialExcludedChild(bool relayoutChildren)
{
StackStats::LayoutCheckPoint layoutCheckPoint;
RenderRubyText* rt = rubyText();
if (!rt)
return 0;
if (relayoutChildren)
rt->setChildNeedsLayout(MarkOnlyThis);
rt->layoutIfNeeded();
return rt;
}
void RenderRubyRun::layout()
{
RenderBlockFlow::layout();
RenderRubyText* rt = rubyText();
if (!rt)
return;
rt->setLogicalLeft(0);
LayoutUnit lastLineRubyTextBottom = rt->logicalHeight();
LayoutUnit firstLineRubyTextTop = 0;
RootInlineBox* rootBox = rt->lastRootBox();
if (rootBox) {
firstLineRubyTextTop = rt->firstRootBox()->logicalTopLayoutOverflow();
lastLineRubyTextBottom = rootBox->logicalBottomLayoutOverflow();
}
if (style().isFlippedLinesWritingMode() == (style().rubyPosition() == RubyPositionAfter)) {
LayoutUnit firstLineTop = 0;
if (RenderRubyBase* rb = rubyBase()) {
RootInlineBox* rootBox = rb->firstRootBox();
if (rootBox)
firstLineTop = rootBox->logicalTopLayoutOverflow();
firstLineTop += rb->logicalTop();
}
rt->setLogicalTop(-lastLineRubyTextBottom + firstLineTop);
} else {
LayoutUnit lastLineBottom = logicalHeight();
if (RenderRubyBase* rb = rubyBase()) {
RootInlineBox* rootBox = rb->lastRootBox();
if (rootBox)
lastLineBottom = rootBox->logicalBottomLayoutOverflow();
lastLineBottom += rb->logicalTop();
}
rt->setLogicalTop(-firstLineRubyTextTop + lastLineBottom);
}
computeOverflow(clientLogicalBottom());
}
static bool shouldOverhang(bool firstLine, const RenderObject* renderer, const RenderRubyBase& rubyBase)
{
if (!renderer || !renderer->isText())
return false;
const RenderStyle& rubyBaseStyle = firstLine ? rubyBase.firstLineStyle() : rubyBase.style();
const RenderStyle& style = firstLine ? renderer->firstLineStyle() : renderer->style();
return style.fontSize() <= rubyBaseStyle.fontSize();
}
void RenderRubyRun::getOverhang(bool firstLine, RenderObject* startRenderer, RenderObject* endRenderer, int& startOverhang, int& endOverhang) const
{
ASSERT(!needsLayout());
startOverhang = 0;
endOverhang = 0;
RenderRubyBase* rubyBase = this->rubyBase();
RenderRubyText* rubyText = this->rubyText();
if (!rubyBase || !rubyText)
return;
if (!rubyBase->firstRootBox())
return;
int logicalWidth = this->logicalWidth();
int logicalLeftOverhang = std::numeric_limits<int>::max();
int logicalRightOverhang = std::numeric_limits<int>::max();
for (RootInlineBox* rootInlineBox = rubyBase->firstRootBox(); rootInlineBox; rootInlineBox = rootInlineBox->nextRootBox()) {
logicalLeftOverhang = std::min<int>(logicalLeftOverhang, rootInlineBox->logicalLeft());
logicalRightOverhang = std::min<int>(logicalRightOverhang, logicalWidth - rootInlineBox->logicalRight());
}
startOverhang = style().isLeftToRightDirection() ? logicalLeftOverhang : logicalRightOverhang;
endOverhang = style().isLeftToRightDirection() ? logicalRightOverhang : logicalLeftOverhang;
if (!shouldOverhang(firstLine, startRenderer, *rubyBase))
startOverhang = 0;
if (!shouldOverhang(firstLine, endRenderer, *rubyBase))
endOverhang = 0;
const RenderStyle& rubyTextStyle = firstLine ? rubyText->firstLineStyle() : rubyText->style();
int halfWidthOfFontSize = rubyTextStyle.fontSize() / 2;
if (startOverhang)
startOverhang = std::min<int>(startOverhang, std::min<int>(toRenderText(startRenderer)->minLogicalWidth(), halfWidthOfFontSize));
if (endOverhang)
endOverhang = std::min<int>(endOverhang, std::min<int>(toRenderText(endRenderer)->minLogicalWidth(), halfWidthOfFontSize));
}
}