Unreviewed. Mark some more WTF unit tests as slow for GTK and WPE
[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  * Copyright (C) 2016 Igalia S.L.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "RenderMathMLScripts.h"
30
31 #if ENABLE(MATHML)
32
33 #include "MathMLElement.h"
34 #include "MathMLScriptsElement.h"
35 #include "RenderMathMLOperator.h"
36 #include <wtf/IsoMallocInlines.h>
37
38 namespace WebCore {
39
40 WTF_MAKE_ISO_ALLOCATED_IMPL(RenderMathMLScripts);
41
42 static bool isPrescriptDelimiter(const RenderObject& renderObject)
43 {
44     return renderObject.node() && renderObject.node()->hasTagName(MathMLNames::mprescriptsTag);
45 }
46
47 RenderMathMLScripts::RenderMathMLScripts(MathMLScriptsElement& element, RenderStyle&& style)
48     : RenderMathMLBlock(element, WTFMove(style))
49 {
50 }
51
52 MathMLScriptsElement& RenderMathMLScripts::element() const
53 {
54     return static_cast<MathMLScriptsElement&>(nodeForNonAnonymous());
55 }
56
57 MathMLScriptsElement::ScriptType RenderMathMLScripts::scriptType() const
58 {
59     return element().scriptType();
60 }
61
62 RenderMathMLOperator* RenderMathMLScripts::unembellishedOperator() const
63 {
64     auto base = firstChildBox();
65     if (!is<RenderMathMLBlock>(base))
66         return nullptr;
67     return downcast<RenderMathMLBlock>(base)->unembellishedOperator();
68 }
69
70 Optional<RenderMathMLScripts::ReferenceChildren> RenderMathMLScripts::validateAndGetReferenceChildren()
71 {
72     // All scripted elements must have at least one child.
73     // The first child is the base.
74     auto base = firstChildBox();
75     if (!base)
76         return WTF::nullopt;
77
78     ReferenceChildren reference;
79     reference.base = base;
80     reference.firstPostScript = nullptr;
81     reference.firstPreScript = nullptr;
82     reference.prescriptDelimiter = nullptr;
83
84     switch (scriptType()) {
85     case MathMLScriptsElement::ScriptType::Sub:
86     case MathMLScriptsElement::ScriptType::Super:
87     case MathMLScriptsElement::ScriptType::Under:
88     case MathMLScriptsElement::ScriptType::Over: {
89         // These elements must have exactly two children.
90         // The second child is a postscript and there are no prescripts.
91         // <msub> base subscript </msub>
92         // <msup> base superscript </msup>
93         // <munder> base underscript </munder>
94         // <mover> base overscript </mover>
95         auto script = base->nextSiblingBox();
96         if (!script || isPrescriptDelimiter(*script) || script->nextSiblingBox())
97             return WTF::nullopt;
98         reference.firstPostScript = script;
99         return reference;
100     }
101     case MathMLScriptsElement::ScriptType::SubSup:
102     case MathMLScriptsElement::ScriptType::UnderOver: {
103         // These elements must have exactly three children.
104         // The second and third children are postscripts and there are no prescripts.
105         // <msubsup> base subscript superscript </msubsup>
106         // <munderover> base subscript superscript </munderover>
107         auto subScript = base->nextSiblingBox();
108         if (!subScript || isPrescriptDelimiter(*subScript))
109             return WTF::nullopt;
110         auto superScript = subScript->nextSiblingBox();
111         if (!superScript || isPrescriptDelimiter(*superScript) || superScript->nextSiblingBox())
112             return WTF::nullopt;
113         reference.firstPostScript = subScript;
114         return reference;
115     }
116     case MathMLScriptsElement::ScriptType::Multiscripts: {
117         // This element accepts the following syntax:
118         //
119         // <mmultiscripts>
120         //   base
121         //   (subscript superscript)*
122         //   [ <mprescripts/> (presubscript presuperscript)* ]
123         // </mmultiscripts>
124         //
125         // https://www.w3.org/TR/MathML3/chapter3.html#presm.mmultiscripts
126         //
127         // We set the first postscript, unless (subscript superscript)* is empty.
128         if (base->nextSiblingBox() && !isPrescriptDelimiter(*base->nextSiblingBox()))
129             reference.firstPostScript = base->nextSiblingBox();
130
131         // We browse the children in order to
132         // 1) Set the first prescript, unless (presubscript presuperscript)* is empty.
133         // 2) Ensure the validity of the element i.e.
134         //   a) That the list of postscripts can be grouped into pairs of subscript/superscript.
135         //   b) That the list of prescripts can be grouped into pairs of subscript/superscript.
136         //   c) That there is at most one <mprescripts/>.
137         bool numberOfScriptIsEven = true;
138         for (auto script = base->nextSiblingBox(); script; script = script->nextSiblingBox()) {
139             if (isPrescriptDelimiter(*script)) {
140                 // This is a <mprescripts/>. Let's check 2a) and 2c).
141                 if (!numberOfScriptIsEven || reference.firstPreScript)
142                     return WTF::nullopt;
143                 reference.firstPreScript = script->nextSiblingBox(); // We do 1).
144                 reference.prescriptDelimiter = script;
145                 continue;
146             }
147             numberOfScriptIsEven = !numberOfScriptIsEven;
148         }
149         return numberOfScriptIsEven ? Optional<ReferenceChildren>(reference) : WTF::nullopt; // We verify 2b).
150     }
151     }
152
153     ASSERT_NOT_REACHED();
154     return WTF::nullopt;
155 }
156
157 LayoutUnit RenderMathMLScripts::spaceAfterScript()
158 {
159     const auto& primaryFont = style().fontCascade().primaryFont();
160     if (auto* mathData = primaryFont.mathData())
161         return LayoutUnit(mathData->getMathConstant(primaryFont, OpenTypeMathData::SpaceAfterScript));
162     return LayoutUnit(style().fontCascade().size() / 5);
163 }
164
165 LayoutUnit RenderMathMLScripts::italicCorrection(const ReferenceChildren& reference)
166 {
167     if (is<RenderMathMLBlock>(*reference.base)) {
168         if (auto* renderOperator = downcast<RenderMathMLBlock>(*reference.base).unembellishedOperator())
169             return renderOperator->italicCorrection();
170     }
171     return 0;
172 }
173
174 void RenderMathMLScripts::computePreferredLogicalWidths()
175 {
176     ASSERT(preferredLogicalWidthsDirty());
177
178     m_minPreferredLogicalWidth = 0;
179     m_maxPreferredLogicalWidth = 0;
180
181     auto possibleReference = validateAndGetReferenceChildren();
182     if (!possibleReference) {
183         setPreferredLogicalWidthsDirty(false);
184         return;
185     }
186     auto& reference = possibleReference.value();
187
188     LayoutUnit baseItalicCorrection = std::min(reference.base->maxPreferredLogicalWidth(), italicCorrection(reference));
189     LayoutUnit space = spaceAfterScript();
190
191     switch (scriptType()) {
192     case MathMLScriptsElement::ScriptType::Sub:
193     case MathMLScriptsElement::ScriptType::Under:
194         m_maxPreferredLogicalWidth += reference.base->maxPreferredLogicalWidth();
195         m_maxPreferredLogicalWidth += std::max(0_lu, reference.firstPostScript->maxPreferredLogicalWidth() - baseItalicCorrection + space);
196         break;
197     case MathMLScriptsElement::ScriptType::Super:
198     case MathMLScriptsElement::ScriptType::Over:
199         m_maxPreferredLogicalWidth += reference.base->maxPreferredLogicalWidth();
200         m_maxPreferredLogicalWidth += std::max(0_lu, reference.firstPostScript->maxPreferredLogicalWidth() + space);
201         break;
202     case MathMLScriptsElement::ScriptType::SubSup:
203     case MathMLScriptsElement::ScriptType::UnderOver:
204     case MathMLScriptsElement::ScriptType::Multiscripts: {
205         auto subScript = reference.firstPreScript;
206         while (subScript) {
207             auto supScript = subScript->nextSiblingBox();
208             ASSERT(supScript);
209             LayoutUnit subSupPairWidth = std::max(subScript->maxPreferredLogicalWidth(), supScript->maxPreferredLogicalWidth());
210             m_maxPreferredLogicalWidth += subSupPairWidth + space;
211             subScript = supScript->nextSiblingBox();
212         }
213         m_maxPreferredLogicalWidth += reference.base->maxPreferredLogicalWidth();
214         subScript = reference.firstPostScript;
215         while (subScript && subScript != reference.prescriptDelimiter) {
216             auto supScript = subScript->nextSiblingBox();
217             ASSERT(supScript);
218             LayoutUnit subSupPairWidth = std::max(std::max(0_lu, subScript->maxPreferredLogicalWidth() - baseItalicCorrection), supScript->maxPreferredLogicalWidth());
219             m_maxPreferredLogicalWidth += subSupPairWidth + space;
220             subScript = supScript->nextSiblingBox();
221         }
222     }
223     }
224
225     m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
226
227     setPreferredLogicalWidthsDirty(false);
228 }
229
230 auto RenderMathMLScripts::verticalParameters() const -> VerticalParameters
231 {
232     VerticalParameters parameters;
233     const auto& primaryFont = style().fontCascade().primaryFont();
234     if (auto* mathData = primaryFont.mathData()) {
235         parameters.subscriptShiftDown = mathData->getMathConstant(primaryFont, OpenTypeMathData::SubscriptShiftDown);
236         parameters.superscriptShiftUp = mathData->getMathConstant(primaryFont, OpenTypeMathData::SuperscriptShiftUp);
237         parameters.subscriptBaselineDropMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::SubscriptBaselineDropMin);
238         parameters.superScriptBaselineDropMax = mathData->getMathConstant(primaryFont, OpenTypeMathData::SuperscriptBaselineDropMax);
239         parameters.subSuperscriptGapMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::SubSuperscriptGapMin);
240         parameters.superscriptBottomMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::SuperscriptBottomMin);
241         parameters.subscriptTopMax = mathData->getMathConstant(primaryFont, OpenTypeMathData::SubscriptTopMax);
242         parameters.superscriptBottomMaxWithSubscript = mathData->getMathConstant(primaryFont, OpenTypeMathData::SuperscriptBottomMaxWithSubscript);
243     } else {
244         // Default heuristic values when you do not have a font.
245         parameters.subscriptShiftDown = style().fontMetrics().xHeight() / 3;
246         parameters.superscriptShiftUp = style().fontMetrics().xHeight();
247         parameters.subscriptBaselineDropMin = style().fontMetrics().xHeight() / 2;
248         parameters.superScriptBaselineDropMax = style().fontMetrics().xHeight() / 2;
249         parameters.subSuperscriptGapMin = style().fontCascade().size() / 5;
250         parameters.superscriptBottomMin = style().fontMetrics().xHeight() / 4;
251         parameters.subscriptTopMax = 4 * style().fontMetrics().xHeight() / 5;
252         parameters.superscriptBottomMaxWithSubscript = 4 * style().fontMetrics().xHeight() / 5;
253     }
254     return parameters;
255 }
256
257 RenderMathMLScripts::VerticalMetrics RenderMathMLScripts::verticalMetrics(const ReferenceChildren& reference)
258 {
259     VerticalParameters parameters = verticalParameters();
260     VerticalMetrics metrics = { 0, 0, 0, 0 };
261
262     LayoutUnit baseAscent = ascentForChild(*reference.base);
263     LayoutUnit baseDescent = reference.base->logicalHeight() - baseAscent;
264     if (scriptType() == MathMLScriptsElement::ScriptType::Sub || scriptType() == MathMLScriptsElement::ScriptType::SubSup || scriptType() == MathMLScriptsElement::ScriptType::Multiscripts || scriptType() == MathMLScriptsElement::ScriptType::Under || scriptType() == MathMLScriptsElement::ScriptType::UnderOver) {
265         metrics.subShift = std::max(parameters.subscriptShiftDown, baseDescent + parameters.subscriptBaselineDropMin);
266         if (!isRenderMathMLUnderOver()) {
267             // It is not clear how to interpret the default shift and it is not available yet anyway.
268             // Hence we just pass 0 as the default value used by toUserUnits.
269             LayoutUnit specifiedMinSubShift = toUserUnits(element().subscriptShift(), style(), 0);
270             metrics.subShift = std::max(metrics.subShift, specifiedMinSubShift);
271         }
272     }
273     if (scriptType() == MathMLScriptsElement::ScriptType::Super || scriptType() == MathMLScriptsElement::ScriptType::SubSup || scriptType() == MathMLScriptsElement::ScriptType::Multiscripts  || scriptType() == MathMLScriptsElement::ScriptType::Over || scriptType() == MathMLScriptsElement::ScriptType::UnderOver) {
274         metrics.supShift = std::max(parameters.superscriptShiftUp, baseAscent - parameters.superScriptBaselineDropMax);
275         if (!isRenderMathMLUnderOver()) {
276             // It is not clear how to interpret the default shift and it is not available yet anyway.
277             // Hence we just pass 0 as the default value used by toUserUnits.
278             LayoutUnit specifiedMinSupShift = toUserUnits(element().superscriptShift(), style(), 0);
279             metrics.supShift = std::max(metrics.supShift, specifiedMinSupShift);
280         }
281     }
282
283     switch (scriptType()) {
284     case MathMLScriptsElement::ScriptType::Sub:
285     case MathMLScriptsElement::ScriptType::Under: {
286         LayoutUnit subAscent = ascentForChild(*reference.firstPostScript);
287         LayoutUnit subDescent = reference.firstPostScript->logicalHeight() - subAscent;
288         metrics.descent = subDescent;
289         metrics.subShift = std::max(metrics.subShift, subAscent - parameters.subscriptTopMax);
290     }
291         break;
292     case MathMLScriptsElement::ScriptType::Super:
293     case MathMLScriptsElement::ScriptType::Over: {
294         LayoutUnit supAscent = ascentForChild(*reference.firstPostScript);
295         LayoutUnit supDescent = reference.firstPostScript->logicalHeight() - supAscent;
296         metrics.ascent = supAscent;
297         metrics.supShift = std::max(metrics.supShift, parameters.superscriptBottomMin + supDescent);
298     }
299         break;
300     case MathMLScriptsElement::ScriptType::SubSup:
301     case MathMLScriptsElement::ScriptType::UnderOver:
302     case MathMLScriptsElement::ScriptType::Multiscripts: {
303         // FIXME: We should move the code updating VerticalMetrics for each sub/sup pair in a helper
304         // function. That way, SubSup/UnderOver can just make one call and the loop for Multiscripts
305         // can be rewritten in a more readable.
306         auto subScript = reference.firstPostScript ? reference.firstPostScript : reference.firstPreScript;
307         while (subScript) {
308             auto supScript = subScript->nextSiblingBox();
309             ASSERT(supScript);
310             LayoutUnit subAscent = ascentForChild(*subScript);
311             LayoutUnit subDescent = subScript->logicalHeight() - subAscent;
312             LayoutUnit supAscent = ascentForChild(*supScript);
313             LayoutUnit supDescent = supScript->logicalHeight() - supAscent;
314             metrics.ascent = std::max(metrics.ascent, supAscent);
315             metrics.descent = std::max(metrics.descent, subDescent);
316             LayoutUnit subScriptShift = std::max(parameters.subscriptShiftDown, baseDescent + parameters.subscriptBaselineDropMin);
317             subScriptShift = std::max(subScriptShift, subAscent - parameters.subscriptTopMax);
318             LayoutUnit supScriptShift = std::max(parameters.superscriptShiftUp, baseAscent - parameters.superScriptBaselineDropMax);
319             supScriptShift = std::max(supScriptShift, parameters.superscriptBottomMin + supDescent);
320
321             LayoutUnit subSuperscriptGap = (subScriptShift - subAscent) + (supScriptShift - supDescent);
322             if (subSuperscriptGap < parameters.subSuperscriptGapMin) {
323                 // First, we try and push the superscript up.
324                 LayoutUnit delta = parameters.superscriptBottomMaxWithSubscript - (supScriptShift - supDescent);
325                 if (delta > 0) {
326                     delta = std::min(delta, parameters.subSuperscriptGapMin - subSuperscriptGap);
327                     supScriptShift += delta;
328                     subSuperscriptGap += delta;
329                 }
330                 // If that is not enough, we push the subscript down.
331                 if (subSuperscriptGap < parameters.subSuperscriptGapMin)
332                     subScriptShift += parameters.subSuperscriptGapMin - subSuperscriptGap;
333             }
334
335             metrics.subShift = std::max(metrics.subShift, subScriptShift);
336             metrics.supShift = std::max(metrics.supShift, supScriptShift);
337
338             subScript = supScript->nextSiblingBox();
339             if (subScript == reference.prescriptDelimiter)
340                 subScript = reference.firstPreScript;
341         }
342     }
343     }
344
345     return metrics;
346 }
347
348 void RenderMathMLScripts::layoutBlock(bool relayoutChildren, LayoutUnit)
349 {
350     ASSERT(needsLayout());
351
352     if (!relayoutChildren && simplifiedLayout())
353         return;
354
355     auto possibleReference = validateAndGetReferenceChildren();
356     if (!possibleReference) {
357         layoutInvalidMarkup(relayoutChildren);
358         return;
359     }
360     auto& reference = possibleReference.value();
361
362     recomputeLogicalWidth();
363     for (auto child = firstChildBox(); child; child = child->nextSiblingBox())
364         child->layoutIfNeeded();
365
366     LayoutUnit space = spaceAfterScript();
367
368     // We determine the minimal shift/size of each script and take the maximum of the values.
369     VerticalMetrics metrics = verticalMetrics(reference);
370
371     LayoutUnit baseAscent = ascentForChild(*reference.base);
372     LayoutUnit baseDescent = reference.base->logicalHeight() - baseAscent;
373     LayoutUnit baseItalicCorrection = std::min(reference.base->logicalWidth(), italicCorrection(reference));
374     LayoutUnit horizontalOffset;
375
376     LayoutUnit ascent = std::max(baseAscent, metrics.ascent + metrics.supShift);
377     LayoutUnit descent = std::max(baseDescent, metrics.descent + metrics.subShift);
378     setLogicalHeight(ascent + descent);
379
380     switch (scriptType()) {
381     case MathMLScriptsElement::ScriptType::Sub:
382     case MathMLScriptsElement::ScriptType::Under: {
383         setLogicalWidth(reference.base->logicalWidth() + std::max(0_lu, reference.firstPostScript->logicalWidth() - baseItalicCorrection + space));
384         LayoutPoint baseLocation(mirrorIfNeeded(horizontalOffset, *reference.base), ascent - baseAscent);
385         reference.base->setLocation(baseLocation);
386         horizontalOffset += reference.base->logicalWidth();
387         LayoutUnit scriptAscent = ascentForChild(*reference.firstPostScript);
388         LayoutPoint scriptLocation(mirrorIfNeeded(horizontalOffset - baseItalicCorrection, *reference.firstPostScript), ascent + metrics.subShift - scriptAscent);
389         reference.firstPostScript->setLocation(scriptLocation);
390     }
391         break;
392     case MathMLScriptsElement::ScriptType::Super:
393     case MathMLScriptsElement::ScriptType::Over: {
394         setLogicalWidth(reference.base->logicalWidth() + std::max(0_lu, reference.firstPostScript->logicalWidth() + space));
395         LayoutPoint baseLocation(mirrorIfNeeded(horizontalOffset, *reference.base), ascent - baseAscent);
396         reference.base->setLocation(baseLocation);
397         horizontalOffset += reference.base->logicalWidth();
398         LayoutUnit scriptAscent = ascentForChild(*reference.firstPostScript);
399         LayoutPoint scriptLocation(mirrorIfNeeded(horizontalOffset, *reference.firstPostScript), ascent - metrics.supShift - scriptAscent);
400         reference.firstPostScript->setLocation(scriptLocation);
401     }
402         break;
403     case MathMLScriptsElement::ScriptType::SubSup:
404     case MathMLScriptsElement::ScriptType::UnderOver:
405     case MathMLScriptsElement::ScriptType::Multiscripts: {
406         // Calculate the logical width.
407         LayoutUnit logicalWidth;
408         auto subScript = reference.firstPreScript;
409         while (subScript) {
410             auto supScript = subScript->nextSiblingBox();
411             ASSERT(supScript);
412             LayoutUnit subSupPairWidth = std::max(subScript->logicalWidth(), supScript->logicalWidth());
413             logicalWidth += subSupPairWidth + space;
414             subScript = supScript->nextSiblingBox();
415         }
416         logicalWidth += reference.base->logicalWidth();
417         subScript = reference.firstPostScript;
418         while (subScript && subScript != reference.prescriptDelimiter) {
419             auto supScript = subScript->nextSiblingBox();
420             ASSERT(supScript);
421             LayoutUnit subSupPairWidth = std::max(std::max(0_lu, subScript->logicalWidth() - baseItalicCorrection), supScript->logicalWidth());
422             logicalWidth += subSupPairWidth + space;
423             subScript = supScript->nextSiblingBox();
424         }
425         setLogicalWidth(logicalWidth);
426
427         subScript = reference.firstPreScript;
428         while (subScript) {
429             auto supScript = subScript->nextSiblingBox();
430             ASSERT(supScript);
431             LayoutUnit subSupPairWidth = std::max(subScript->logicalWidth(), supScript->logicalWidth());
432             horizontalOffset += space + subSupPairWidth;
433             LayoutUnit subAscent = ascentForChild(*subScript);
434             LayoutPoint subScriptLocation(mirrorIfNeeded(horizontalOffset - subScript->logicalWidth(), *subScript), ascent + metrics.subShift - subAscent);
435             subScript->setLocation(subScriptLocation);
436             LayoutUnit supAscent = ascentForChild(*supScript);
437             LayoutPoint supScriptLocation(mirrorIfNeeded(horizontalOffset - supScript->logicalWidth(), *supScript), ascent - metrics.supShift - supAscent);
438             supScript->setLocation(supScriptLocation);
439             subScript = supScript->nextSiblingBox();
440         }
441         LayoutPoint baseLocation(mirrorIfNeeded(horizontalOffset, *reference.base), ascent - baseAscent);
442         reference.base->setLocation(baseLocation);
443         horizontalOffset += reference.base->logicalWidth();
444         subScript = reference.firstPostScript;
445         while (subScript && subScript != reference.prescriptDelimiter) {
446             auto supScript = subScript->nextSiblingBox();
447             ASSERT(supScript);
448             LayoutUnit subAscent = ascentForChild(*subScript);
449             LayoutPoint subScriptLocation(mirrorIfNeeded(horizontalOffset - baseItalicCorrection, *subScript), ascent + metrics.subShift - subAscent);
450             subScript->setLocation(subScriptLocation);
451             LayoutUnit supAscent = ascentForChild(*supScript);
452             LayoutPoint supScriptLocation(mirrorIfNeeded(horizontalOffset, *supScript), ascent - metrics.supShift - supAscent);
453             supScript->setLocation(supScriptLocation);
454
455             LayoutUnit subSupPairWidth = std::max(subScript->logicalWidth(), supScript->logicalWidth());
456             horizontalOffset += subSupPairWidth + space;
457             subScript = supScript->nextSiblingBox();
458         }
459     }
460     }
461
462     layoutPositionedObjects(relayoutChildren);
463
464     updateScrollInfoAfterLayout();
465
466     clearNeedsLayout();
467 }
468
469 Optional<int> RenderMathMLScripts::firstLineBaseline() const
470 {
471     auto* base = firstChildBox();
472     if (!base)
473         return Optional<int>();
474     return Optional<int>(static_cast<int>(lroundf(ascentForChild(*base) + base->logicalTop())));
475 }
476
477 }
478
479 #endif // ENABLE(MATHML)