CTTE: Pass RenderArena around by reference.
[WebKit-https.git] / Source / WebCore / rendering / mathml / RenderMathMLScripts.cpp
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 "MathMLNames.h"
34
35 namespace WebCore {
36     
37 using 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
53 static bool isPrescript(RenderObject* renderObject)
54 {
55     ASSERT(renderObject);
56     return renderObject->node() && renderObject->node()->hasTagName(MathMLNames::mprescriptsTag);
57 }
58
59 RenderMathMLScripts::RenderMathMLScripts(Element* element)
60     : RenderMathMLBlock(element)
61     , m_baseWrapper(0)
62 {
63     // Determine what kind of sub/sup expression we have by element name
64     if (element->hasLocalName(MathMLNames::msubTag))
65         m_kind = Sub;
66     else if (element->hasLocalName(MathMLNames::msupTag))
67         m_kind = Super;
68     else if (element->hasLocalName(MathMLNames::msubsupTag))
69         m_kind = SubSup;
70     else {
71         ASSERT(element->hasLocalName(MathMLNames::mmultiscriptsTag));
72         m_kind = Multiscripts;
73     }
74 }
75
76 RenderBoxModelObject* 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
86 void 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
114 void 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
145 void 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             RenderMathMLScriptsWrapper* wrapper = toRenderMathMLScriptsWrapper(parent);
157             wrapper->addChildInternal(false, child, beforeChild);
158             return;
159         }
160     }
161
162     if (beforeChild == m_baseWrapper) {
163         // This is like inserting the child at the beginning of the base wrapper.
164         m_baseWrapper->addChildInternal(false, child, m_baseWrapper->firstChild());
165         return;
166     }
167     
168     if (isPrescript(child)) {
169         // The new child becomes an <mprescripts/> separator.
170         RenderMathMLBlock::addChild(child, beforeChild);
171         return;
172     }
173
174     if (!beforeChild || isPrescript(beforeChild)) {
175         // We are at the end of a sequence of subSup pairs.
176         RenderMathMLBlock* previousSibling = toRenderMathMLBlock(beforeChild ? beforeChild->previousSibling() : lastChild());
177         if (previousSibling && previousSibling->isRenderMathMLScriptsWrapper()) {
178             RenderMathMLScriptsWrapper* wrapper = toRenderMathMLScriptsWrapper(previousSibling);
179             if ((wrapper->m_kind == RenderMathMLScriptsWrapper::Base && wrapper->isEmpty()) || (wrapper->m_kind == RenderMathMLScriptsWrapper::SubSupPair && !wrapper->firstChild()->nextSibling())) {
180                 // 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.
181                 wrapper->addChildInternal(true, child);
182                 return;
183             }
184         }
185         // Otherwise we create a new subSupPair to store the new child.
186         RenderMathMLScriptsWrapper* subSupPair = RenderMathMLScriptsWrapper::createAnonymousWrapper(this, RenderMathMLScriptsWrapper::SubSupPair);
187         subSupPair->addChildInternal(true, child);
188         RenderMathMLBlock::addChild(subSupPair, beforeChild);
189         return;
190     }
191
192     // beforeChild is a subSup pair. This is like inserting the new child at the beginning of the subSup wrapper.
193     RenderMathMLScriptsWrapper* wrapper = toRenderMathMLScriptsWrapper(beforeChild);
194     ASSERT(wrapper->m_kind == RenderMathMLScriptsWrapper::SubSupPair);
195     ASSERT(!(m_baseWrapper->isEmpty() && m_baseWrapper->nextSibling() == beforeChild));
196     wrapper->addChildInternal(false, child, wrapper->firstChild());
197 }
198
199 void RenderMathMLScripts::removeChildInternal(bool doNotRestructure, RenderObject* child)
200 {
201     if (doNotRestructure) {
202         RenderMathMLBlock::removeChild(child);
203         return;
204     }
205
206     ASSERT(isPrescript(child));
207
208     RenderObject* previousSibling = child->previousSibling();
209     RenderObject* nextSibling = child->nextSibling();
210     ASSERT(previousSibling);
211
212     if (nextSibling && !isPrescript(previousSibling) && !isPrescript(nextSibling)) {
213         RenderMathMLScriptsWrapper* previousWrapper = toRenderMathMLScriptsWrapper(previousSibling);
214         RenderMathMLScriptsWrapper* nextWrapper = toRenderMathMLScriptsWrapper(nextSibling);
215         ASSERT(nextWrapper->m_kind == RenderMathMLScriptsWrapper::SubSupPair && !nextWrapper->isEmpty());
216         if ((previousWrapper->m_kind == RenderMathMLScriptsWrapper::Base && previousWrapper->isEmpty()) || (previousWrapper->m_kind == RenderMathMLScriptsWrapper::SubSupPair && !previousWrapper->firstChild()->nextSibling())) {
217             RenderObject* script = nextWrapper->firstChild();
218             nextWrapper->removeChildInternal(false, script);
219             previousWrapper->addChildInternal(true, script);
220         }
221     }
222
223     RenderMathMLBlock::removeChild(child);
224 }
225
226 void RenderMathMLScripts::addChild(RenderObject* child, RenderObject* beforeChild)
227 {
228     if (isEmpty()) {
229         m_baseWrapper = RenderMathMLScriptsWrapper::createAnonymousWrapper(this, RenderMathMLScriptsWrapper::Base);
230         RenderMathMLBlock::addChild(m_baseWrapper);
231     }
232
233     addChildInternal(false, child, beforeChild);
234
235     fixAnonymousStyles();
236 }
237
238 void RenderMathMLScripts::removeChild(RenderObject* child)
239 {
240     if (beingDestroyed() || documentBeingDestroyed()) {
241         // The renderer is being destroyed so we remove the child normally.
242         RenderMathMLBlock::removeChild(child);
243         return;
244     }
245
246     removeChildInternal(false, child);
247     
248     fixAnonymousStyles();
249 }
250
251 void RenderMathMLScripts::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
252 {
253     RenderMathMLBlock::styleDidChange(diff, oldStyle);
254     
255     if (!isEmpty())
256         fixAnonymousStyles();
257 }
258
259 RenderMathMLOperator* RenderMathMLScripts::unembellishedOperator()
260 {
261     RenderBoxModelObject* base = this->base();
262     if (!base || !base->isRenderMathMLBlock())
263         return 0;
264     return toRenderMathMLBlock(base)->unembellishedOperator();
265 }
266
267 void RenderMathMLScripts::layout()
268 {
269     RenderMathMLBlock::layout();
270
271     if (!m_baseWrapper)
272         return;
273     RenderBox* base = m_baseWrapper->firstChildBox();
274     if (!base)
275         return;
276
277     // Our layout rules include: Don't let the superscript go below the "axis" (half x-height above the
278     // baseline), or the subscript above the axis. Also, don't let the superscript's top edge be
279     // below the base's top edge, or the subscript's bottom edge above the base's bottom edge.
280     //
281     // FIXME: Check any subscriptshift or superscriptshift attributes, and maybe use more sophisticated
282     // heuristics from TeX or elsewhere. See https://bugs.webkit.org/show_bug.cgi?id=79274#c5.
283
284     LayoutUnit baseHeight = base->logicalHeight();
285     LayoutUnit baseBaseline = base->firstLineBoxBaseline();
286     if (baseBaseline == -1)
287         baseBaseline = baseHeight;
288     LayoutUnit axis = style()->fontMetrics().xHeight() / 2;
289     int fontSize = style()->fontSize();
290
291     ASSERT(m_baseWrapper->style()->hasOneRef());
292     bool needsSecondLayout = false;
293
294     LayoutUnit topPadding = 0;
295     LayoutUnit bottomPadding = 0;
296
297     bool isPostScript = true;
298     RenderMathMLBlock* subSupPair = toRenderMathMLBlock(m_baseWrapper->nextSibling());
299     for (; subSupPair; subSupPair = toRenderMathMLBlock(subSupPair->nextSibling())) {
300
301         // We skip the base and <mprescripts/> elements.
302         if (isPrescript(subSupPair)) {
303             if (!isPostScript)
304                 break;
305             isPostScript = false;
306             continue;
307         }
308
309         if (RenderBox* superscript = m_kind == Sub ? 0 : subSupPair->lastChildBox()) {
310             LayoutUnit superscriptHeight = superscript->logicalHeight();
311             LayoutUnit superscriptBaseline = superscript->firstLineBoxBaseline();
312             if (superscriptBaseline == -1)
313                 superscriptBaseline = superscriptHeight;
314             LayoutUnit minBaseline = max<LayoutUnit>(fontSize / 3 + 1 + superscriptBaseline, superscriptHeight + axis);
315
316             topPadding = max<LayoutUnit>(topPadding, minBaseline - baseBaseline);
317         }
318
319         if (RenderBox* subscript = m_kind == Super ? 0 : subSupPair->firstChildBox()) {
320             LayoutUnit subscriptHeight = subscript->logicalHeight();
321             LayoutUnit subscriptBaseline = subscript->firstLineBoxBaseline();
322             if (subscriptBaseline == -1)
323                 subscriptBaseline = subscriptHeight;
324             LayoutUnit baseExtendUnderBaseline = baseHeight - baseBaseline;
325             LayoutUnit subscriptUnderItsBaseline = subscriptHeight - subscriptBaseline;
326             LayoutUnit minExtendUnderBaseline = max<LayoutUnit>(fontSize / 5 + 1 + subscriptUnderItsBaseline, subscriptHeight - axis);
327
328             bottomPadding = max<LayoutUnit>(bottomPadding, minExtendUnderBaseline - baseExtendUnderBaseline);
329         }
330     }
331
332     Length newPadding(topPadding, Fixed);
333     if (newPadding != m_baseWrapper->style()->paddingTop()) {
334         m_baseWrapper->style()->setPaddingTop(newPadding);
335         needsSecondLayout = true;
336     }
337
338     newPadding = Length(bottomPadding, Fixed);
339     if (newPadding != m_baseWrapper->style()->paddingBottom()) {
340         m_baseWrapper->style()->setPaddingBottom(newPadding);
341         needsSecondLayout = true;
342     }
343
344     if (!needsSecondLayout)
345         return;
346
347     setNeedsLayout(true, MarkOnlyThis);
348     m_baseWrapper->setChildNeedsLayout(true, MarkOnlyThis);
349
350     RenderMathMLBlock::layout();
351 }
352
353 int RenderMathMLScripts::firstLineBoxBaseline() const
354 {
355     if (m_baseWrapper) {
356         LayoutUnit baseline = m_baseWrapper->firstLineBoxBaseline();
357         if (baseline != -1)
358             return baseline;
359     }
360     return RenderMathMLBlock::firstLineBoxBaseline();
361 }
362
363 RenderMathMLScriptsWrapper* RenderMathMLScriptsWrapper::createAnonymousWrapper(RenderMathMLScripts* renderObject, WrapperType type)
364 {
365     RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(renderObject->style(), FLEX);
366     RenderMathMLScriptsWrapper* newBlock = new (renderObject->renderArena()) RenderMathMLScriptsWrapper(0, type);
367     newBlock->setDocumentForAnonymous(renderObject->document());
368     newBlock->setStyle(newStyle.release());
369     return newBlock;
370 }
371
372 void RenderMathMLScriptsWrapper::addChildInternal(bool doNotRestructure, RenderObject* child, RenderObject* beforeChild)
373 {
374     if (doNotRestructure) {
375         RenderMathMLBlock::addChild(child, beforeChild);
376         return;
377     }
378
379     RenderMathMLScripts* parentNode = toRenderMathMLScripts(parent());
380
381     if (m_kind == Base) {
382         RenderObject* sibling = nextSibling();
383
384         if (!isEmpty() && !beforeChild) {
385             // This is like inserting the child after the base wrapper.
386             parentNode->addChildInternal(false, sibling);
387             return;
388         }
389
390         // The old base (if any) becomes a script ; the new child becomes either the base or an <mprescripts> separator.
391         RenderObject* oldBase = firstChild();
392         if (oldBase)
393             RenderMathMLBlock::removeChild(oldBase);
394         if (isPrescript(child))
395             parentNode->addChildInternal(true, child, sibling);
396         else
397             RenderMathMLBlock::addChild(child);
398         if (oldBase)
399             parentNode->addChildInternal(false, oldBase, sibling);
400         return;
401     }
402
403     if (isPrescript(child)) {
404         // We insert an <mprescripts> element.
405         if (!beforeChild)
406             parentNode->addChildInternal(true, child, nextSibling());
407         else if (beforeChild == firstChild())
408             parentNode->addChildInternal(true, child, this);
409         else {
410             // We insert the <mprescripts> in the middle of a subSup pair so we must split that pair.
411             RenderObject* sibling = nextSibling();
412             parentNode->removeChildInternal(true, this);
413             parentNode->addChildInternal(true, child, sibling);
414
415             RenderObject* script = firstChild();
416             RenderMathMLBlock::removeChild(script);
417             parentNode->addChildInternal(false, script, child);
418
419             script = beforeChild;
420             RenderMathMLBlock::removeChild(script);
421             parentNode->addChildInternal(false, script, sibling);
422             destroy();
423         }
424         return;
425     }
426
427     // We first move to the last subSup pair in the curent sequence of scripts.
428     RenderMathMLScriptsWrapper* subSupPair = this;
429     while (subSupPair->nextSibling() && !isPrescript(subSupPair->nextSibling()))
430         subSupPair = toRenderMathMLScriptsWrapper(subSupPair->nextSibling());
431     if (subSupPair->firstChild()->nextSibling()) {
432         // The last pair has two children so we need to create a new pair to leave room for the new child.
433         RenderMathMLScriptsWrapper* newPair = createAnonymousWrapper(parentNode, RenderMathMLScriptsWrapper::SubSupPair);
434         parentNode->addChildInternal(true, newPair, subSupPair->nextSibling());
435         subSupPair = newPair;
436     }
437
438     // We shift the successors in the current sequence of scripts.
439     for (RenderObject* previousSibling = subSupPair->previousSibling(); subSupPair != this; previousSibling = previousSibling->previousSibling()) {
440         RenderMathMLScriptsWrapper* previousSubSupPair = toRenderMathMLScriptsWrapper(previousSibling);
441         RenderObject* script = previousSubSupPair->lastChild();
442         previousSubSupPair->removeChildInternal(true, script);
443         subSupPair->addChildInternal(true, script, subSupPair->firstChild());
444         subSupPair = toRenderMathMLScriptsWrapper(previousSibling);
445     }
446
447     // 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.
448     RenderMathMLBlock::addChild(child, firstChild() == beforeChild ? beforeChild : 0);
449 }
450
451 void RenderMathMLScriptsWrapper::addChild(RenderObject* child, RenderObject* beforeChild)
452 {
453     RenderMathMLScripts* parentNode = toRenderMathMLScripts(parent());
454
455     addChildInternal(false, child, beforeChild);
456
457     parentNode->fixAnonymousStyles();
458 }
459
460 void RenderMathMLScriptsWrapper::removeChildInternal(bool doNotRestructure, RenderObject* child)
461 {
462     if (doNotRestructure) {
463         RenderMathMLBlock::removeChild(child);
464         return;
465     }
466
467     RenderMathMLScripts* parentNode = toRenderMathMLScripts(parent());
468
469     if (m_kind == Base) {
470         // We remove the child from the base wrapper.
471         RenderObject* sibling = nextSibling();
472         RenderMathMLBlock::removeChild(child);
473         if (sibling && !isPrescript(sibling)) {
474             // If there are postscripts, the first one becomes the base.
475             RenderMathMLScriptsWrapper* wrapper = toRenderMathMLScriptsWrapper(sibling);
476             RenderObject* script = wrapper->firstChild();
477             wrapper->removeChildInternal(false, script);
478             RenderMathMLBlock::addChild(script);
479         }
480         return;
481     }
482
483     // We remove the child and shift the successors in the current sequence of scripts.
484     RenderMathMLBlock::removeChild(child);
485     RenderMathMLScriptsWrapper* subSupPair = this;
486     for (RenderObject* nextSibling = subSupPair->nextSibling(); nextSibling && !isPrescript(nextSibling); nextSibling = nextSibling->nextSibling()) {
487         RenderMathMLScriptsWrapper* nextSubSupPair = toRenderMathMLScriptsWrapper(nextSibling);
488         RenderObject* script = nextSubSupPair->firstChild();
489         nextSubSupPair->removeChildInternal(true, script);
490         subSupPair->addChildInternal(true, script);
491         subSupPair = toRenderMathMLScriptsWrapper(nextSibling);
492     }
493
494     // We remove the last subSup pair if it became empty.
495     if (subSupPair->isEmpty()) {
496         parentNode->removeChildInternal(true, subSupPair);
497         subSupPair->destroy();
498     }
499 }
500
501 void RenderMathMLScriptsWrapper::removeChild(RenderObject* child)
502 {
503     if (beingDestroyed() || documentBeingDestroyed()) {
504         // The renderer is being destroyed so we remove the child normally.
505         RenderMathMLBlock::removeChild(child);
506         return;
507     }
508
509     RenderMathMLScripts* parentNode = toRenderMathMLScripts(parent());
510
511     removeChildInternal(false, child);
512
513     parentNode->fixAnonymousStyles();
514 }
515
516 }    
517
518 #endif // ENABLE(MATHML)