RenderMathMLScripts.cpp [plain text]
#include "config.h"
#if ENABLE(MATHML)
#include "RenderMathMLScripts.h"
#include "MathMLElement.h"
namespace WebCore {
using namespace MathMLNames;
static bool isPrescript(const RenderObject& renderObject)
{
return renderObject.node() && renderObject.node()->hasTagName(MathMLNames::mprescriptsTag);
}
RenderMathMLScripts::RenderMathMLScripts(Element& element, Ref<RenderStyle>&& style)
: RenderMathMLBlock(element, WTF::move(style))
, m_baseWrapper(0)
{
if (element.hasTagName(MathMLNames::msubTag))
m_kind = Sub;
else if (element.hasTagName(MathMLNames::msupTag))
m_kind = Super;
else if (element.hasTagName(MathMLNames::msubsupTag))
m_kind = SubSup;
else {
ASSERT(element.hasTagName(MathMLNames::mmultiscriptsTag));
m_kind = Multiscripts;
}
}
RenderBoxModelObject* RenderMathMLScripts::base() const
{
if (!m_baseWrapper)
return nullptr;
RenderObject* base = m_baseWrapper->firstChild();
if (!is<RenderBoxModelObject>(base))
return nullptr;
return downcast<RenderBoxModelObject>(base);
}
void RenderMathMLScripts::fixAnonymousStyleForSubSupPair(RenderObject* subSupPair, bool isPostScript)
{
ASSERT(subSupPair && subSupPair->style().refCount() == 1);
RenderStyle& scriptsStyle = subSupPair->style();
scriptsStyle.setFlexDirection(FlowColumnReverse);
if (m_kind == Sub)
scriptsStyle.setJustifyContentPosition(ContentPositionFlexStart);
else if (m_kind == Super)
scriptsStyle.setJustifyContentPosition(ContentPositionFlexEnd);
else
scriptsStyle.setJustifyContentDistribution(ContentDistributionSpaceBetween);
scriptsStyle.setAlignItemsPosition(isPostScript ? ItemPositionFlexStart : ItemPositionFlexEnd);
scriptsStyle.setOrder(isPostScript ? 0 : -1);
LayoutUnit scriptSize = static_cast<int>(0.75 * style().fontSize());
scriptsStyle.setFontSize(scriptSize);
}
void RenderMathMLScripts::fixAnonymousStyles()
{
ASSERT(m_baseWrapper && m_baseWrapper->style().hasOneRef());
m_baseWrapper->style().setAlignSelfPosition(ItemPositionFlexStart);
RenderObject* subSupPair = m_baseWrapper;
for (subSupPair = subSupPair->nextSibling(); subSupPair && !isPrescript(*subSupPair); subSupPair = subSupPair->nextSibling())
fixAnonymousStyleForSubSupPair(subSupPair, true);
if (subSupPair && m_kind == Multiscripts) {
for (subSupPair = subSupPair->nextSibling(); subSupPair && !isPrescript(*subSupPair); subSupPair = subSupPair->nextSibling())
fixAnonymousStyleForSubSupPair(subSupPair, false);
}
for (; subSupPair; subSupPair = subSupPair->nextSibling()) {
if (!isPrescript(*subSupPair)) {
ASSERT(subSupPair && subSupPair->style().refCount() == 1);
RenderStyle& scriptsStyle = subSupPair->style();
scriptsStyle.setFlexDirection(FlowRow);
scriptsStyle.setJustifyContentPosition(ContentPositionFlexStart);
scriptsStyle.setAlignItemsPosition(ItemPositionCenter);
scriptsStyle.setOrder(0);
scriptsStyle.setFontSize(style().fontSize());
}
}
}
void RenderMathMLScripts::addChildInternal(bool doNotRestructure, RenderObject* child, RenderObject* beforeChild)
{
if (doNotRestructure) {
RenderMathMLBlock::addChild(child, beforeChild);
return;
}
if (beforeChild) {
RenderObject* parent = beforeChild->parent();
if (parent != this) {
RenderMathMLBlock& parentBlock = downcast<RenderMathMLBlock>(*parent);
if (is<RenderMathMLScriptsWrapper>(parentBlock)) {
RenderMathMLScriptsWrapper& wrapper = downcast<RenderMathMLScriptsWrapper>(parentBlock);
wrapper.addChildInternal(false, child, beforeChild);
return;
}
}
}
if (beforeChild == m_baseWrapper) {
m_baseWrapper->addChildInternal(false, child, m_baseWrapper->firstChild());
return;
}
if (isPrescript(*child)) {
RenderMathMLBlock::addChild(child, beforeChild);
return;
}
if (!beforeChild || isPrescript(*beforeChild)) {
RenderMathMLBlock* previousSibling = downcast<RenderMathMLBlock>(beforeChild ? beforeChild->previousSibling() : lastChild());
if (is<RenderMathMLScriptsWrapper>(previousSibling)) {
RenderMathMLScriptsWrapper& wrapper = downcast<RenderMathMLScriptsWrapper>(*previousSibling);
if ((wrapper.m_kind == RenderMathMLScriptsWrapper::Base && wrapper.isEmpty()) || (wrapper.m_kind == RenderMathMLScriptsWrapper::SubSupPair && !wrapper.firstChild()->nextSibling())) {
wrapper.addChildInternal(true, child);
return;
}
}
RenderMathMLScriptsWrapper* subSupPair = RenderMathMLScriptsWrapper::createAnonymousWrapper(this, RenderMathMLScriptsWrapper::SubSupPair);
subSupPair->addChildInternal(true, child);
RenderMathMLBlock::addChild(subSupPair, beforeChild);
return;
}
RenderMathMLScriptsWrapper& wrapper = downcast<RenderMathMLScriptsWrapper>(*beforeChild);
ASSERT(wrapper.m_kind == RenderMathMLScriptsWrapper::SubSupPair);
ASSERT(!(m_baseWrapper->isEmpty() && m_baseWrapper->nextSibling() == beforeChild));
wrapper.addChildInternal(false, child, wrapper.firstChild());
}
void RenderMathMLScripts::removeChildInternal(bool doNotRestructure, RenderObject& child)
{
if (doNotRestructure) {
RenderMathMLBlock::removeChild(child);
return;
}
ASSERT(isPrescript(child));
RenderObject* previousSibling = child.previousSibling();
RenderObject* nextSibling = child.nextSibling();
ASSERT(previousSibling);
if (nextSibling && !isPrescript(*previousSibling) && !isPrescript(*nextSibling)) {
RenderMathMLScriptsWrapper& previousWrapper = downcast<RenderMathMLScriptsWrapper>(*previousSibling);
RenderMathMLScriptsWrapper& nextWrapper = downcast<RenderMathMLScriptsWrapper>(*nextSibling);
ASSERT(nextWrapper.m_kind == RenderMathMLScriptsWrapper::SubSupPair && !nextWrapper.isEmpty());
if ((previousWrapper.m_kind == RenderMathMLScriptsWrapper::Base && previousWrapper.isEmpty()) || (previousWrapper.m_kind == RenderMathMLScriptsWrapper::SubSupPair && !previousWrapper.firstChild()->nextSibling())) {
RenderObject* script = nextWrapper.firstChild();
nextWrapper.removeChildInternal(false, *script);
previousWrapper.addChildInternal(true, script);
}
}
RenderMathMLBlock::removeChild(child);
}
void RenderMathMLScripts::addChild(RenderObject* child, RenderObject* beforeChild)
{
if (isEmpty()) {
m_baseWrapper = RenderMathMLScriptsWrapper::createAnonymousWrapper(this, RenderMathMLScriptsWrapper::Base);
RenderMathMLBlock::addChild(m_baseWrapper);
}
addChildInternal(false, child, beforeChild);
fixAnonymousStyles();
}
void RenderMathMLScripts::removeChild(RenderObject& child)
{
if (beingDestroyed() || documentBeingDestroyed()) {
RenderMathMLBlock::removeChild(child);
return;
}
removeChildInternal(false, child);
fixAnonymousStyles();
}
void RenderMathMLScripts::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderMathMLBlock::styleDidChange(diff, oldStyle);
if (!isEmpty())
fixAnonymousStyles();
}
RenderMathMLOperator* RenderMathMLScripts::unembellishedOperator()
{
RenderBoxModelObject* base = this->base();
if (!is<RenderMathMLBlock>(base))
return nullptr;
return downcast<RenderMathMLBlock>(*base).unembellishedOperator();
}
void RenderMathMLScripts::layout()
{
RenderMathMLBlock::layout();
if (!m_baseWrapper)
return;
RenderBox* base = m_baseWrapper->firstChildBox();
if (!base)
return;
LayoutUnit baseHeight = base->logicalHeight();
LayoutUnit baseBaseline = base->firstLineBaseline().valueOr(baseHeight);
LayoutUnit axis = style().fontMetrics().xHeight() / 2;
int fontSize = style().fontSize();
ASSERT(m_baseWrapper->style().hasOneRef());
bool needsSecondLayout = false;
LayoutUnit topPadding = 0;
LayoutUnit bottomPadding = 0;
Element* scriptElement = element();
LayoutUnit superscriptShiftValue = 0;
LayoutUnit subscriptShiftValue = 0;
if (m_kind == Sub || m_kind == SubSup || m_kind == Multiscripts)
parseMathMLLength(scriptElement->fastGetAttribute(MathMLNames::subscriptshiftAttr), subscriptShiftValue, &style(), false);
if (m_kind == Super || m_kind == SubSup || m_kind == Multiscripts)
parseMathMLLength(scriptElement->fastGetAttribute(MathMLNames::superscriptshiftAttr), superscriptShiftValue, &style(), false);
bool isPostScript = true;
RenderMathMLBlock* subSupPair = downcast<RenderMathMLBlock>(m_baseWrapper->nextSibling());
for (; subSupPair; subSupPair = downcast<RenderMathMLBlock>(subSupPair->nextSibling())) {
if (isPrescript(*subSupPair)) {
if (!isPostScript)
break;
isPostScript = false;
continue;
}
if (RenderBox* superscript = m_kind == Sub ? 0 : subSupPair->lastChildBox()) {
LayoutUnit superscriptHeight = superscript->logicalHeight();
LayoutUnit superscriptBaseline = superscript->firstLineBaseline().valueOr(superscriptHeight);
LayoutUnit minBaseline = std::max<LayoutUnit>(fontSize / 3 + 1 + superscriptBaseline, superscriptHeight + axis + superscriptShiftValue);
topPadding = std::max<LayoutUnit>(topPadding, minBaseline - baseBaseline);
}
if (RenderBox* subscript = m_kind == Super ? 0 : subSupPair->firstChildBox()) {
LayoutUnit subscriptHeight = subscript->logicalHeight();
LayoutUnit subscriptBaseline = subscript->firstLineBaseline().valueOr(subscriptHeight);
LayoutUnit baseExtendUnderBaseline = baseHeight - baseBaseline;
LayoutUnit subscriptUnderItsBaseline = subscriptHeight - subscriptBaseline;
LayoutUnit minExtendUnderBaseline = std::max<LayoutUnit>(fontSize / 5 + 1 + subscriptUnderItsBaseline, subscriptHeight + subscriptShiftValue - axis);
bottomPadding = std::max<LayoutUnit>(bottomPadding, minExtendUnderBaseline - baseExtendUnderBaseline);
}
}
Length newPadding(topPadding, Fixed);
if (newPadding != m_baseWrapper->style().paddingTop()) {
m_baseWrapper->style().setPaddingTop(newPadding);
needsSecondLayout = true;
}
newPadding = Length(bottomPadding, Fixed);
if (newPadding != m_baseWrapper->style().paddingBottom()) {
m_baseWrapper->style().setPaddingBottom(newPadding);
needsSecondLayout = true;
}
if (!needsSecondLayout)
return;
setNeedsLayout(MarkOnlyThis);
m_baseWrapper->setChildNeedsLayout(MarkOnlyThis);
RenderMathMLBlock::layout();
}
Optional<int> RenderMathMLScripts::firstLineBaseline() const
{
if (m_baseWrapper) {
if (Optional<int> baseline = m_baseWrapper->firstLineBaseline())
return baseline;
}
return RenderMathMLBlock::firstLineBaseline();
}
RenderMathMLScriptsWrapper* RenderMathMLScriptsWrapper::createAnonymousWrapper(RenderMathMLScripts* renderObject, WrapperType type)
{
RenderMathMLScriptsWrapper* newBlock = new RenderMathMLScriptsWrapper(renderObject->document(), RenderStyle::createAnonymousStyleWithDisplay(&renderObject->style(), FLEX), type);
newBlock->initializeStyle();
return newBlock;
}
void RenderMathMLScriptsWrapper::addChildInternal(bool doNotRestructure, RenderObject* child, RenderObject* beforeChild)
{
if (doNotRestructure) {
RenderMathMLBlock::addChild(child, beforeChild);
return;
}
RenderMathMLScripts* parentNode = downcast<RenderMathMLScripts>(parent());
if (m_kind == Base) {
RenderObject* sibling = nextSibling();
if (!isEmpty() && !beforeChild) {
parentNode->addChildInternal(false, sibling);
return;
}
RenderObject* oldBase = firstChild();
if (oldBase)
RenderMathMLBlock::removeChild(*oldBase);
if (isPrescript(*child))
parentNode->addChildInternal(true, child, sibling);
else
RenderMathMLBlock::addChild(child);
if (oldBase)
parentNode->addChildInternal(false, oldBase, sibling);
return;
}
if (isPrescript(*child)) {
if (!beforeChild)
parentNode->addChildInternal(true, child, nextSibling());
else if (beforeChild == firstChild())
parentNode->addChildInternal(true, child, this);
else {
RenderObject* sibling = nextSibling();
parentNode->removeChildInternal(true, *this);
parentNode->addChildInternal(true, child, sibling);
RenderObject* script = firstChild();
RenderMathMLBlock::removeChild(*script);
parentNode->addChildInternal(false, script, child);
script = beforeChild;
RenderMathMLBlock::removeChild(*script);
parentNode->addChildInternal(false, script, sibling);
destroy();
}
return;
}
RenderMathMLScriptsWrapper* subSupPair = this;
while (subSupPair->nextSibling() && !isPrescript(*subSupPair->nextSibling()))
subSupPair = downcast<RenderMathMLScriptsWrapper>(subSupPair->nextSibling());
if (subSupPair->firstChild()->nextSibling()) {
RenderMathMLScriptsWrapper* newPair = createAnonymousWrapper(parentNode, RenderMathMLScriptsWrapper::SubSupPair);
parentNode->addChildInternal(true, newPair, subSupPair->nextSibling());
subSupPair = newPair;
}
for (RenderObject* previousSibling = subSupPair->previousSibling(); subSupPair != this; previousSibling = previousSibling->previousSibling()) {
RenderMathMLScriptsWrapper& previousSubSupPair = downcast<RenderMathMLScriptsWrapper>(*previousSibling);
RenderObject* script = previousSubSupPair.lastChild();
previousSubSupPair.removeChildInternal(true, *script);
subSupPair->addChildInternal(true, script, subSupPair->firstChild());
subSupPair = downcast<RenderMathMLScriptsWrapper>(previousSibling);
}
RenderMathMLBlock::addChild(child, firstChild() == beforeChild ? beforeChild : nullptr);
}
void RenderMathMLScriptsWrapper::addChild(RenderObject* child, RenderObject* beforeChild)
{
RenderMathMLScripts* parentNode = downcast<RenderMathMLScripts>(parent());
addChildInternal(false, child, beforeChild);
parentNode->fixAnonymousStyles();
}
void RenderMathMLScriptsWrapper::removeChildInternal(bool doNotRestructure, RenderObject& child)
{
if (doNotRestructure) {
RenderMathMLBlock::removeChild(child);
return;
}
RenderMathMLScripts* parentNode = downcast<RenderMathMLScripts>(parent());
if (m_kind == Base) {
RenderObject* sibling = nextSibling();
RenderMathMLBlock::removeChild(child);
if (sibling && !isPrescript(*sibling)) {
RenderMathMLScriptsWrapper& wrapper = downcast<RenderMathMLScriptsWrapper>(*sibling);
RenderObject* script = wrapper.firstChild();
wrapper.removeChildInternal(false, *script);
RenderMathMLBlock::addChild(script);
}
return;
}
RenderMathMLBlock::removeChild(child);
RenderMathMLScriptsWrapper* subSupPair = this;
for (RenderObject* nextSibling = subSupPair->nextSibling(); nextSibling && !isPrescript(*nextSibling); nextSibling = nextSibling->nextSibling()) {
RenderMathMLScriptsWrapper& nextSubSupPair = downcast<RenderMathMLScriptsWrapper>(*nextSibling);
RenderObject* script = nextSubSupPair.firstChild();
nextSubSupPair.removeChildInternal(true, *script);
subSupPair->addChildInternal(true, script);
subSupPair = downcast<RenderMathMLScriptsWrapper>(nextSibling);
}
if (subSupPair->isEmpty()) {
parentNode->removeChildInternal(true, *subSupPair);
subSupPair->destroy();
}
}
void RenderMathMLScriptsWrapper::removeChild(RenderObject& child)
{
if (beingDestroyed() || documentBeingDestroyed()) {
RenderMathMLBlock::removeChild(child);
return;
}
RenderMathMLScripts* parentNode = downcast<RenderMathMLScripts>(parent());
removeChildInternal(false, child);
parentNode->fixAnonymousStyles();
}
}
#endif // ENABLE(MATHML)