1/*
2 * Copyright (C) 2010 Alex Milowski (alex@milowski.com). All rights reserved.
3 * Copyright (C) 2013 The MathJax Consortium.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28
29#if ENABLE(MATHML)
30
31#include "RenderMathMLScripts.h"
32
33#include "MathMLElement.h"
34
35namespace WebCore {
36
37using namespace MathMLNames;
38
39// RenderMathMLScripts implements various MathML elements drawing scripts attached to a base. For valid MathML elements, the structure of the render tree should be:
40//
41// - msub, msup, msubsup: BaseWrapper SubSupPairWrapper
42// - mmultiscripts: BaseWrapper SubSupPairWrapper* (mprescripts SubSupPairWrapper*)*
43//
44// where BaseWrapper and SubSupPairWrapper do not contain any <mprescripts/> children. In addition, BaseWrapper must have one child and SubSupPairWrapper must have either one child (msub, msup) or two children (msubsup, mmultiscripts).
45//
46// In order to accept invalid markup and to handle the script elements consistently and uniformly, we will use a more general structure that encompasses both valid and invalid elements:
47//
48// BaseWrapper SubSupPairWrapper* (mprescripts SubSupPairWrapper*)*
49//
50// where BaseWrapper can now be empty and SubSupPairWrapper can now have one or two elements.
51//
52
53static bool isPrescript(RenderObject* renderObject)
54{
55    ASSERT(renderObject);
56    return renderObject->node() && renderObject->node()->hasTagName(MathMLNames::mprescriptsTag);
57}
58
59RenderMathMLScripts::RenderMathMLScripts(Element& element, PassRef<RenderStyle> style)
60    : RenderMathMLBlock(element, WTF::move(style))
61    , m_baseWrapper(0)
62{
63    // Determine what kind of sub/sup expression we have by element name
64    if (element.hasTagName(MathMLNames::msubTag))
65        m_kind = Sub;
66    else if (element.hasTagName(MathMLNames::msupTag))
67        m_kind = Super;
68    else if (element.hasTagName(MathMLNames::msubsupTag))
69        m_kind = SubSup;
70    else {
71        ASSERT(element.hasTagName(MathMLNames::mmultiscriptsTag));
72        m_kind = Multiscripts;
73    }
74}
75
76RenderBoxModelObject* RenderMathMLScripts::base() const
77{
78    if (!m_baseWrapper)
79        return 0;
80    RenderObject* base = m_baseWrapper->firstChild();
81    if (!base || !base->isBoxModelObject())
82        return 0;
83    return toRenderBoxModelObject(base);
84}
85
86void RenderMathMLScripts::fixAnonymousStyleForSubSupPair(RenderObject* subSupPair, bool isPostScript)
87{
88    ASSERT(subSupPair && subSupPair->style().refCount() == 1);
89    RenderStyle& scriptsStyle = subSupPair->style();
90
91    // subSup pairs are drawn in column from bottom (subscript) to top (superscript).
92    scriptsStyle.setFlexDirection(FlowColumnReverse);
93
94    // The MathML specification does not specify horizontal alignment of
95    // scripts. We align the bottom (respectively top) edge of the subscript
96    // (respectively superscript) with the bottom (respectively top) edge of
97    // the flex container. Note that for valid <msub> and <msup> elements, the
98    // subSupPair should actually have only one script.
99    scriptsStyle.setJustifyContent(m_kind == Sub ? JustifyFlexStart : m_kind == Super ? JustifyFlexEnd : JustifySpaceBetween);
100
101    // The MathML specification does not specify vertical alignment of scripts.
102    // Let's right align prescripts and left align postscripts.
103    // See http://lists.w3.org/Archives/Public/www-math/2012Aug/0006.html
104    scriptsStyle.setAlignItems(isPostScript ? AlignFlexStart : AlignFlexEnd);
105
106    // We set the order property so that the prescripts are drawn before the base.
107    scriptsStyle.setOrder(isPostScript ? 0 : -1);
108
109    // We set this wrapper's font-size for its line-height.
110    LayoutUnit scriptSize = static_cast<int>(0.75 * style().fontSize());
111    scriptsStyle.setFontSize(scriptSize);
112}
113
114void RenderMathMLScripts::fixAnonymousStyles()
115{
116    // We set the base wrapper's style so that baseHeight in layout() will be an unstretched height.
117    ASSERT(m_baseWrapper && m_baseWrapper->style().hasOneRef());
118    m_baseWrapper->style().setAlignSelf(AlignFlexStart);
119
120    // This sets the style for postscript pairs.
121    RenderObject* subSupPair = m_baseWrapper;
122    for (subSupPair = subSupPair->nextSibling(); subSupPair && !isPrescript(subSupPair); subSupPair = subSupPair->nextSibling())
123        fixAnonymousStyleForSubSupPair(subSupPair, true);
124
125    if (subSupPair && m_kind == Multiscripts) {
126        // This sets the style for prescript pairs.
127        for (subSupPair = subSupPair->nextSibling(); subSupPair && !isPrescript(subSupPair); subSupPair = subSupPair->nextSibling())
128            fixAnonymousStyleForSubSupPair(subSupPair, false);
129    }
130
131    // This resets style for extra subSup pairs.
132    for (; subSupPair; subSupPair = subSupPair->nextSibling()) {
133        if (!isPrescript(subSupPair)) {
134            ASSERT(subSupPair && subSupPair->style().refCount() == 1);
135            RenderStyle& scriptsStyle = subSupPair->style();
136            scriptsStyle.setFlexDirection(FlowRow);
137            scriptsStyle.setJustifyContent(JustifyFlexStart);
138            scriptsStyle.setAlignItems(AlignCenter);
139            scriptsStyle.setOrder(0);
140            scriptsStyle.setFontSize(style().fontSize());
141        }
142    }
143}
144
145void RenderMathMLScripts::addChildInternal(bool doNotRestructure, RenderObject* child, RenderObject* beforeChild)
146{
147    if (doNotRestructure) {
148        RenderMathMLBlock::addChild(child, beforeChild);
149        return;
150    }
151
152    if (beforeChild) {
153        // beforeChild may be a grandchild, so we call the addChild function of the corresponding wrapper instead.
154        RenderObject* parent = beforeChild->parent();
155        if (parent != this) {
156            RenderMathMLBlock* parentBlock = toRenderMathMLBlock(parent);
157            if (parentBlock->isRenderMathMLScriptsWrapper()) {
158                RenderMathMLScriptsWrapper* wrapper = toRenderMathMLScriptsWrapper(parentBlock);
159                wrapper->addChildInternal(false, child, beforeChild);
160                return;
161            }
162        }
163    }
164
165    if (beforeChild == m_baseWrapper) {
166        // This is like inserting the child at the beginning of the base wrapper.
167        m_baseWrapper->addChildInternal(false, child, m_baseWrapper->firstChild());
168        return;
169    }
170
171    if (isPrescript(child)) {
172        // The new child becomes an <mprescripts/> separator.
173        RenderMathMLBlock::addChild(child, beforeChild);
174        return;
175    }
176
177    if (!beforeChild || isPrescript(beforeChild)) {
178        // We are at the end of a sequence of subSup pairs.
179        RenderMathMLBlock* previousSibling = toRenderMathMLBlock(beforeChild ? beforeChild->previousSibling() : lastChild());
180        if (previousSibling && previousSibling->isRenderMathMLScriptsWrapper()) {
181            RenderMathMLScriptsWrapper* wrapper = toRenderMathMLScriptsWrapper(previousSibling);
182            if ((wrapper->m_kind == RenderMathMLScriptsWrapper::Base && wrapper->isEmpty()) || (wrapper->m_kind == RenderMathMLScriptsWrapper::SubSupPair && !wrapper->firstChild()->nextSibling())) {
183                // The previous sibling is either an empty base or a SubSup pair with a single child so we can insert the new child into that wrapper.
184                wrapper->addChildInternal(true, child);
185                return;
186            }
187        }
188        // Otherwise we create a new subSupPair to store the new child.
189        RenderMathMLScriptsWrapper* subSupPair = RenderMathMLScriptsWrapper::createAnonymousWrapper(this, RenderMathMLScriptsWrapper::SubSupPair);
190        subSupPair->addChildInternal(true, child);
191        RenderMathMLBlock::addChild(subSupPair, beforeChild);
192        return;
193    }
194
195    // beforeChild is a subSup pair. This is like inserting the new child at the beginning of the subSup wrapper.
196    RenderMathMLScriptsWrapper* wrapper = toRenderMathMLScriptsWrapper(beforeChild);
197    ASSERT(wrapper->m_kind == RenderMathMLScriptsWrapper::SubSupPair);
198    ASSERT(!(m_baseWrapper->isEmpty() && m_baseWrapper->nextSibling() == beforeChild));
199    wrapper->addChildInternal(false, child, wrapper->firstChild());
200}
201
202RenderObject* RenderMathMLScripts::removeChildInternal(bool doNotRestructure, RenderObject& child)
203{
204    if (doNotRestructure)
205        return RenderMathMLBlock::removeChild(child);
206
207    ASSERT(isPrescript(&child));
208
209    RenderObject* previousSibling = child.previousSibling();
210    RenderObject* nextSibling = child.nextSibling();
211    ASSERT(previousSibling);
212
213    if (nextSibling && !isPrescript(previousSibling) && !isPrescript(nextSibling)) {
214        RenderMathMLScriptsWrapper* previousWrapper = toRenderMathMLScriptsWrapper(previousSibling);
215        RenderMathMLScriptsWrapper* nextWrapper = toRenderMathMLScriptsWrapper(nextSibling);
216        ASSERT(nextWrapper->m_kind == RenderMathMLScriptsWrapper::SubSupPair && !nextWrapper->isEmpty());
217        if ((previousWrapper->m_kind == RenderMathMLScriptsWrapper::Base && previousWrapper->isEmpty()) || (previousWrapper->m_kind == RenderMathMLScriptsWrapper::SubSupPair && !previousWrapper->firstChild()->nextSibling())) {
218            RenderObject* script = nextWrapper->firstChild();
219            nextWrapper->removeChildInternal(false, *script);
220            previousWrapper->addChildInternal(true, script);
221        }
222    }
223
224    return RenderMathMLBlock::removeChild(child);
225}
226
227void RenderMathMLScripts::addChild(RenderObject* child, RenderObject* beforeChild)
228{
229    if (isEmpty()) {
230        m_baseWrapper = RenderMathMLScriptsWrapper::createAnonymousWrapper(this, RenderMathMLScriptsWrapper::Base);
231        RenderMathMLBlock::addChild(m_baseWrapper);
232    }
233
234    addChildInternal(false, child, beforeChild);
235
236    fixAnonymousStyles();
237}
238
239RenderObject* RenderMathMLScripts::removeChild(RenderObject& child)
240{
241    if (beingDestroyed() || documentBeingDestroyed()) {
242        // The renderer is being destroyed so we remove the child normally.
243        return RenderMathMLBlock::removeChild(child);
244    }
245
246    RenderObject* next = removeChildInternal(false, child);
247
248    fixAnonymousStyles();
249
250    return next;
251}
252
253void RenderMathMLScripts::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
254{
255    RenderMathMLBlock::styleDidChange(diff, oldStyle);
256
257    if (!isEmpty())
258        fixAnonymousStyles();
259}
260
261RenderMathMLOperator* RenderMathMLScripts::unembellishedOperator()
262{
263    RenderBoxModelObject* base = this->base();
264    if (!base || !base->isRenderMathMLBlock())
265        return 0;
266    return toRenderMathMLBlock(base)->unembellishedOperator();
267}
268
269void RenderMathMLScripts::layout()
270{
271    RenderMathMLBlock::layout();
272
273    if (!m_baseWrapper)
274        return;
275    RenderBox* base = m_baseWrapper->firstChildBox();
276    if (!base)
277        return;
278
279    // Our layout rules include: Don't let the superscript go below the "axis" (half x-height above the
280    // baseline), or the subscript above the axis. Also, don't let the superscript's top edge be
281    // below the base's top edge, or the subscript's bottom edge above the base's bottom edge.
282
283    LayoutUnit baseHeight = base->logicalHeight();
284    LayoutUnit baseBaseline = base->firstLineBaseline();
285    if (baseBaseline == -1)
286        baseBaseline = baseHeight;
287    LayoutUnit axis = style().fontMetrics().xHeight() / 2;
288    int fontSize = style().fontSize();
289
290    ASSERT(m_baseWrapper->style().hasOneRef());
291    bool needsSecondLayout = false;
292
293    LayoutUnit topPadding = 0;
294    LayoutUnit bottomPadding = 0;
295
296    Element* scriptElement = element();
297    LayoutUnit superscriptShiftValue = 0;
298    LayoutUnit subscriptShiftValue = 0;
299    if (m_kind == Sub || m_kind == SubSup || m_kind == Multiscripts)
300        parseMathMLLength(scriptElement->fastGetAttribute(MathMLNames::subscriptshiftAttr), subscriptShiftValue, &style(), false);
301    if (m_kind == Super || m_kind == SubSup || m_kind == Multiscripts)
302        parseMathMLLength(scriptElement->fastGetAttribute(MathMLNames::superscriptshiftAttr), superscriptShiftValue, &style(), false);
303
304    bool isPostScript = true;
305    RenderMathMLBlock* subSupPair = toRenderMathMLBlock(m_baseWrapper->nextSibling());
306    for (; subSupPair; subSupPair = toRenderMathMLBlock(subSupPair->nextSibling())) {
307
308        // We skip the base and <mprescripts/> elements.
309        if (isPrescript(subSupPair)) {
310            if (!isPostScript)
311                break;
312            isPostScript = false;
313            continue;
314        }
315
316        if (RenderBox* superscript = m_kind == Sub ? 0 : subSupPair->lastChildBox()) {
317            LayoutUnit superscriptHeight = superscript->logicalHeight();
318            LayoutUnit superscriptBaseline = superscript->firstLineBaseline();
319            if (superscriptBaseline == -1)
320                superscriptBaseline = superscriptHeight;
321            LayoutUnit minBaseline = std::max<LayoutUnit>(fontSize / 3 + 1 + superscriptBaseline, superscriptHeight + axis + superscriptShiftValue);
322
323            topPadding = std::max<LayoutUnit>(topPadding, minBaseline - baseBaseline);
324        }
325
326        if (RenderBox* subscript = m_kind == Super ? 0 : subSupPair->firstChildBox()) {
327            LayoutUnit subscriptHeight = subscript->logicalHeight();
328            LayoutUnit subscriptBaseline = subscript->firstLineBaseline();
329            if (subscriptBaseline == -1)
330                subscriptBaseline = subscriptHeight;
331            LayoutUnit baseExtendUnderBaseline = baseHeight - baseBaseline;
332            LayoutUnit subscriptUnderItsBaseline = subscriptHeight - subscriptBaseline;
333            LayoutUnit minExtendUnderBaseline = std::max<LayoutUnit>(fontSize / 5 + 1 + subscriptUnderItsBaseline, subscriptHeight + subscriptShiftValue - axis);
334
335            bottomPadding = std::max<LayoutUnit>(bottomPadding, minExtendUnderBaseline - baseExtendUnderBaseline);
336        }
337    }
338
339    Length newPadding(topPadding, Fixed);
340    if (newPadding != m_baseWrapper->style().paddingTop()) {
341        m_baseWrapper->style().setPaddingTop(newPadding);
342        needsSecondLayout = true;
343    }
344
345    newPadding = Length(bottomPadding, Fixed);
346    if (newPadding != m_baseWrapper->style().paddingBottom()) {
347        m_baseWrapper->style().setPaddingBottom(newPadding);
348        needsSecondLayout = true;
349    }
350
351    if (!needsSecondLayout)
352        return;
353
354    setNeedsLayout(MarkOnlyThis);
355    m_baseWrapper->setChildNeedsLayout(MarkOnlyThis);
356
357    RenderMathMLBlock::layout();
358}
359
360int RenderMathMLScripts::firstLineBaseline() const
361{
362    if (m_baseWrapper) {
363        LayoutUnit baseline = m_baseWrapper->firstLineBaseline();
364        if (baseline != -1)
365            return baseline;
366    }
367    return RenderMathMLBlock::firstLineBaseline();
368}
369
370RenderMathMLScriptsWrapper* RenderMathMLScriptsWrapper::createAnonymousWrapper(RenderMathMLScripts* renderObject, WrapperType type)
371{
372    RenderMathMLScriptsWrapper* newBlock = new RenderMathMLScriptsWrapper(renderObject->document(), RenderStyle::createAnonymousStyleWithDisplay(&renderObject->style(), FLEX), type);
373    newBlock->initializeStyle();
374    return newBlock;
375}
376
377void RenderMathMLScriptsWrapper::addChildInternal(bool doNotRestructure, RenderObject* child, RenderObject* beforeChild)
378{
379    if (doNotRestructure) {
380        RenderMathMLBlock::addChild(child, beforeChild);
381        return;
382    }
383
384    RenderMathMLScripts* parentNode = toRenderMathMLScripts(parent());
385
386    if (m_kind == Base) {
387        RenderObject* sibling = nextSibling();
388
389        if (!isEmpty() && !beforeChild) {
390            // This is like inserting the child after the base wrapper.
391            parentNode->addChildInternal(false, sibling);
392            return;
393        }
394
395        // The old base (if any) becomes a script ; the new child becomes either the base or an <mprescripts> separator.
396        RenderObject* oldBase = firstChild();
397        if (oldBase)
398            RenderMathMLBlock::removeChild(*oldBase);
399        if (isPrescript(child))
400            parentNode->addChildInternal(true, child, sibling);
401        else
402            RenderMathMLBlock::addChild(child);
403        if (oldBase)
404            parentNode->addChildInternal(false, oldBase, sibling);
405        return;
406    }
407
408    if (isPrescript(child)) {
409        // We insert an <mprescripts> element.
410        if (!beforeChild)
411            parentNode->addChildInternal(true, child, nextSibling());
412        else if (beforeChild == firstChild())
413            parentNode->addChildInternal(true, child, this);
414        else {
415            // We insert the <mprescripts> in the middle of a subSup pair so we must split that pair.
416            RenderObject* sibling = nextSibling();
417            parentNode->removeChildInternal(true, *this);
418            parentNode->addChildInternal(true, child, sibling);
419
420            RenderObject* script = firstChild();
421            RenderMathMLBlock::removeChild(*script);
422            parentNode->addChildInternal(false, script, child);
423
424            script = beforeChild;
425            RenderMathMLBlock::removeChild(*script);
426            parentNode->addChildInternal(false, script, sibling);
427            destroy();
428        }
429        return;
430    }
431
432    // We first move to the last subSup pair in the curent sequence of scripts.
433    RenderMathMLScriptsWrapper* subSupPair = this;
434    while (subSupPair->nextSibling() && !isPrescript(subSupPair->nextSibling()))
435        subSupPair = toRenderMathMLScriptsWrapper(subSupPair->nextSibling());
436    if (subSupPair->firstChild()->nextSibling()) {
437        // The last pair has two children so we need to create a new pair to leave room for the new child.
438        RenderMathMLScriptsWrapper* newPair = createAnonymousWrapper(parentNode, RenderMathMLScriptsWrapper::SubSupPair);
439        parentNode->addChildInternal(true, newPair, subSupPair->nextSibling());
440        subSupPair = newPair;
441    }
442
443    // We shift the successors in the current sequence of scripts.
444    for (RenderObject* previousSibling = subSupPair->previousSibling(); subSupPair != this; previousSibling = previousSibling->previousSibling()) {
445        RenderMathMLScriptsWrapper* previousSubSupPair = toRenderMathMLScriptsWrapper(previousSibling);
446        RenderObject* script = previousSubSupPair->lastChild();
447        previousSubSupPair->removeChildInternal(true, *script);
448        subSupPair->addChildInternal(true, script, subSupPair->firstChild());
449        subSupPair = toRenderMathMLScriptsWrapper(previousSibling);
450    }
451
452    // This subSup pair now contain one element which is either beforeChild or the script that was before. Hence we can insert the new child before of after that element.
453    RenderMathMLBlock::addChild(child, firstChild() == beforeChild ? beforeChild : 0);
454}
455
456void RenderMathMLScriptsWrapper::addChild(RenderObject* child, RenderObject* beforeChild)
457{
458    RenderMathMLScripts* parentNode = toRenderMathMLScripts(parent());
459
460    addChildInternal(false, child, beforeChild);
461
462    parentNode->fixAnonymousStyles();
463}
464
465RenderObject* RenderMathMLScriptsWrapper::removeChildInternal(bool doNotRestructure, RenderObject& child)
466{
467    if (doNotRestructure)
468        return RenderMathMLBlock::removeChild(child);
469
470    RenderMathMLScripts* parentNode = toRenderMathMLScripts(parent());
471
472    if (m_kind == Base) {
473        // We remove the child from the base wrapper.
474        RenderObject* sibling = nextSibling();
475        RenderMathMLBlock::removeChild(child);
476        if (sibling && !isPrescript(sibling)) {
477            // If there are postscripts, the first one becomes the base.
478            RenderMathMLScriptsWrapper* wrapper = toRenderMathMLScriptsWrapper(sibling);
479            RenderObject* script = wrapper->firstChild();
480            wrapper->removeChildInternal(false, *script);
481            RenderMathMLBlock::addChild(script);
482        }
483        return sibling;
484    }
485
486    // We remove the child and shift the successors in the current sequence of scripts.
487    RenderObject* next = RenderMathMLBlock::removeChild(child);
488    RenderMathMLScriptsWrapper* subSupPair = this;
489    for (RenderObject* nextSibling = subSupPair->nextSibling(); nextSibling && !isPrescript(nextSibling); nextSibling = nextSibling->nextSibling()) {
490        RenderMathMLScriptsWrapper* nextSubSupPair = toRenderMathMLScriptsWrapper(nextSibling);
491        RenderObject* script = nextSubSupPair->firstChild();
492        nextSubSupPair->removeChildInternal(true, *script);
493        subSupPair->addChildInternal(true, script);
494        subSupPair = toRenderMathMLScriptsWrapper(nextSibling);
495    }
496
497    // We remove the last subSup pair if it became empty.
498    if (subSupPair->isEmpty()) {
499        parentNode->removeChildInternal(true, *subSupPair);
500        subSupPair->destroy();
501    }
502
503    return next;
504}
505
506RenderObject* RenderMathMLScriptsWrapper::removeChild(RenderObject& child)
507{
508    if (beingDestroyed() || documentBeingDestroyed()) {
509        // The renderer is being destroyed so we remove the child normally.
510        return RenderMathMLBlock::removeChild(child);
511    }
512
513    RenderMathMLScripts* parentNode = toRenderMathMLScripts(parent());
514    RenderObject* next = removeChildInternal(false, child);
515    parentNode->fixAnonymousStyles();
516    return next;
517}
518
519}
520
521#endif // ENABLE(MATHML)
522