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
38namespace WebCore {
39
40WTF_MAKE_ISO_ALLOCATED_IMPL(RenderMathMLScripts);
41
42static bool isPrescriptDelimiter(const RenderObject& renderObject)
43{
44 return renderObject.node() && renderObject.node()->hasTagName(MathMLNames::mprescriptsTag);
45}
46
47RenderMathMLScripts::RenderMathMLScripts(MathMLScriptsElement& element, RenderStyle&& style)
48 : RenderMathMLBlock(element, WTFMove(style))
49{
50}
51
52MathMLScriptsElement& RenderMathMLScripts::element() const
53{
54 return static_cast<MathMLScriptsElement&>(nodeForNonAnonymous());
55}
56
57MathMLScriptsElement::ScriptType RenderMathMLScripts::scriptType() const
58{
59 return element().scriptType();
60}
61
62RenderMathMLOperator* RenderMathMLScripts::unembellishedOperator() const
63{
64 auto base = firstChildBox();
65 if (!is<RenderMathMLBlock>(base))
66 return nullptr;
67 return downcast<RenderMathMLBlock>(base)->unembellishedOperator();
68}
69
70Optional<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
157LayoutUnit RenderMathMLScripts::spaceAfterScript()
158{
159 const auto& primaryFont = style().fontCascade().primaryFont();
160 if (auto* mathData = primaryFont.mathData())
161 return mathData->getMathConstant(primaryFont, OpenTypeMathData::SpaceAfterScript);
162 return style().fontCascade().size() / 5;
163}
164
165LayoutUnit 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
174void 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
230auto 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
257RenderMathMLScripts::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
348void 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
469Optional<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)
480