no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / layout / mathml / nsMathMLmmultiscriptsFrame.cpp
blobfdc9743792e638bc501d79ead439525bd82c4713
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsMathMLmmultiscriptsFrame.h"
9 #include "mozilla/dom/Document.h"
10 #include "mozilla/dom/Element.h"
11 #include "mozilla/PresShell.h"
12 #include "mozilla/StaticPrefs_mathml.h"
13 #include "nsLayoutUtils.h"
14 #include "nsPresContext.h"
15 #include <algorithm>
16 #include "gfxContext.h"
17 #include "gfxMathTable.h"
18 #include "gfxTextRun.h"
20 using namespace mozilla;
23 // <mmultiscripts> -- attach prescripts and tensor indices to a base -
24 // implementation <msub> -- attach a subscript to a base - implementation
25 // <msubsup> -- attach a subscript-superscript pair to a base - implementation
26 // <msup> -- attach a superscript to a base - implementation
29 nsIFrame* NS_NewMathMLmmultiscriptsFrame(PresShell* aPresShell,
30 ComputedStyle* aStyle) {
31 return new (aPresShell)
32 nsMathMLmmultiscriptsFrame(aStyle, aPresShell->GetPresContext());
35 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmmultiscriptsFrame)
37 nsMathMLmmultiscriptsFrame::~nsMathMLmmultiscriptsFrame() = default;
39 uint8_t nsMathMLmmultiscriptsFrame::ScriptIncrement(nsIFrame* aFrame) {
40 if (!aFrame) return 0;
41 if (mFrames.ContainsFrame(aFrame)) {
42 if (mFrames.FirstChild() == aFrame ||
43 aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) {
44 return 0; // No script increment for base frames or prescript markers
46 return 1;
48 return 0; // not a child
51 NS_IMETHODIMP
52 nsMathMLmmultiscriptsFrame::TransmitAutomaticData() {
53 // if our base is an embellished operator, let its state bubble to us
54 mPresentationData.baseFrame = mFrames.FirstChild();
55 GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData);
57 // The TeXbook (Ch 17. p.141) says the superscript inherits the compression
58 // while the subscript is compressed. So here we collect subscripts and set
59 // the compression flag in them.
61 int32_t count = 0;
62 bool isSubScript = !mContent->IsMathMLElement(nsGkAtoms::msup_);
64 AutoTArray<nsIFrame*, 8> subScriptFrames;
65 nsIFrame* childFrame = mFrames.FirstChild();
66 while (childFrame) {
67 if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) {
68 // mprescripts frame
69 } else if (0 == count) {
70 // base frame
71 } else {
72 // super/subscript block
73 if (isSubScript) {
74 // subscript
75 subScriptFrames.AppendElement(childFrame);
76 } else {
77 // superscript
79 PropagateFrameFlagFor(childFrame, NS_FRAME_MATHML_SCRIPT_DESCENDANT);
80 isSubScript = !isSubScript;
82 count++;
83 childFrame = childFrame->GetNextSibling();
85 for (int32_t i = subScriptFrames.Length() - 1; i >= 0; i--) {
86 childFrame = subScriptFrames[i];
87 PropagatePresentationDataFor(childFrame, NS_MATHML_COMPRESSED,
88 NS_MATHML_COMPRESSED);
91 return NS_OK;
94 /* virtual */
95 nsresult nsMathMLmmultiscriptsFrame::Place(DrawTarget* aDrawTarget,
96 bool aPlaceOrigin,
97 ReflowOutput& aDesiredSize) {
98 nscoord subScriptShift = 0;
99 nscoord supScriptShift = 0;
100 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
102 return PlaceMultiScript(PresContext(), aDrawTarget, aPlaceOrigin,
103 aDesiredSize, this, subScriptShift, supScriptShift,
104 fontSizeInflation);
107 // exported routine that both munderover and mmultiscripts share.
108 // munderover uses this when movablelimits is set.
109 nsresult nsMathMLmmultiscriptsFrame::PlaceMultiScript(
110 nsPresContext* aPresContext, DrawTarget* aDrawTarget, bool aPlaceOrigin,
111 ReflowOutput& aDesiredSize, nsMathMLContainerFrame* aFrame,
112 nscoord aUserSubScriptShift, nscoord aUserSupScriptShift,
113 float aFontSizeInflation) {
114 nsAtom* tag = aFrame->GetContent()->NodeInfo()->NameAtom();
116 // This function deals with both munderover etc. as well as msubsup etc.
117 // As the former behaves identically to the later, we treat it as such
118 // to avoid additional checks later.
119 if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mover_))
120 tag = nsGkAtoms::msup_;
121 else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munder_))
122 tag = nsGkAtoms::msub_;
123 else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munderover_))
124 tag = nsGkAtoms::msubsup_;
126 nsBoundingMetrics bmFrame;
128 nscoord minShiftFromXHeight, subDrop, supDrop;
130 ////////////////////////////////////////
131 // Initialize super/sub shifts that
132 // depend only on the current font
133 ////////////////////////////////////////
135 nsIFrame* baseFrame = aFrame->PrincipalChildList().FirstChild();
137 if (!baseFrame) {
138 if (tag == nsGkAtoms::mmultiscripts_)
139 aFrame->ReportErrorToConsole("NoBase");
140 else
141 aFrame->ReportChildCountError();
142 return aFrame->PlaceAsMrow(aDrawTarget, aPlaceOrigin, aDesiredSize);
145 // get x-height (an ex)
146 const nsStyleFont* font = aFrame->StyleFont();
147 RefPtr<nsFontMetrics> fm =
148 nsLayoutUtils::GetFontMetricsForFrame(baseFrame, aFontSizeInflation);
150 nscoord xHeight = fm->XHeight();
152 nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
153 RefPtr<gfxFont> mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
154 // scriptspace from TeX for extra spacing after sup/subscript
155 nscoord scriptSpace;
156 if (mathFont) {
157 scriptSpace = mathFont->MathTable()->Constant(
158 gfxMathTable::SpaceAfterScript, oneDevPixel);
159 } else {
160 // (0.5pt in plain TeX)
161 scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f);
164 // Try and read sub and sup drops from the MATH table.
165 if (mathFont) {
166 subDrop = mathFont->MathTable()->Constant(
167 gfxMathTable::SubscriptBaselineDropMin, oneDevPixel);
168 supDrop = mathFont->MathTable()->Constant(
169 gfxMathTable::SuperscriptBaselineDropMax, oneDevPixel);
172 // force the scriptSpace to be at least 1 pixel
173 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
174 scriptSpace = std::max(onePixel, scriptSpace);
176 /////////////////////////////////////
177 // first the shift for the subscript
179 nscoord subScriptShift;
180 if (mathFont) {
181 // Try and get the sub script shift from the MATH table. Note that contrary
182 // to TeX we only have one parameter.
183 subScriptShift = mathFont->MathTable()->Constant(
184 gfxMathTable::SubscriptShiftDown, oneDevPixel);
185 } else {
186 // subScriptShift{1,2}
187 // = minimum amount to shift the subscript down
188 // = sub{1,2} in TeXbook
189 // subScriptShift1 = subscriptshift attribute * x-height
190 nscoord subScriptShift1, subScriptShift2;
191 // Get subScriptShift{1,2} default from font
192 GetSubScriptShifts(fm, subScriptShift1, subScriptShift2);
193 if (tag == nsGkAtoms::msub_) {
194 subScriptShift = subScriptShift1;
195 } else {
196 subScriptShift = std::max(subScriptShift1, subScriptShift2);
200 if (0 < aUserSubScriptShift) {
201 // the user has set the subscriptshift attribute
202 subScriptShift = std::max(subScriptShift, aUserSubScriptShift);
205 /////////////////////////////////////
206 // next the shift for the superscript
208 nscoord supScriptShift;
209 nsPresentationData presentationData;
210 aFrame->GetPresentationData(presentationData);
211 if (mathFont) {
212 // Try and get the super script shift from the MATH table. Note that
213 // contrary to TeX we only have two parameters.
214 supScriptShift = mathFont->MathTable()->Constant(
215 NS_MATHML_IS_COMPRESSED(presentationData.flags)
216 ? gfxMathTable::SuperscriptShiftUpCramped
217 : gfxMathTable::SuperscriptShiftUp,
218 oneDevPixel);
219 } else {
220 // supScriptShift{1,2,3}
221 // = minimum amount to shift the supscript up
222 // = sup{1,2,3} in TeX
223 // supScriptShift1 = superscriptshift attribute * x-height
224 // Note that there are THREE values for supscript shifts depending
225 // on the current style
226 nscoord supScriptShift1, supScriptShift2, supScriptShift3;
227 // Set supScriptShift{1,2,3} default from font
228 GetSupScriptShifts(fm, supScriptShift1, supScriptShift2, supScriptShift3);
230 // get sup script shift depending on current script level and display style
231 // Rule 18c, App. G, TeXbook
232 if (font->mMathDepth == 0 && font->mMathStyle == StyleMathStyle::Normal &&
233 !NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
234 // Style D in TeXbook
235 supScriptShift = supScriptShift1;
236 } else if (NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
237 // Style C' in TeXbook = D',T',S',SS'
238 supScriptShift = supScriptShift3;
239 } else {
240 // everything else = T,S,SS
241 supScriptShift = supScriptShift2;
245 if (0 < aUserSupScriptShift) {
246 // the user has set the supscriptshift attribute
247 supScriptShift = std::max(supScriptShift, aUserSupScriptShift);
250 ////////////////////////////////////
251 // Get the children's sizes
252 ////////////////////////////////////
254 const WritingMode wm(aDesiredSize.GetWritingMode());
255 nscoord width = 0, prescriptsWidth = 0, rightBearing = 0;
256 nscoord minSubScriptShift = 0, minSupScriptShift = 0;
257 nscoord trySubScriptShift = subScriptShift;
258 nscoord trySupScriptShift = supScriptShift;
259 nscoord maxSubScriptShift = subScriptShift;
260 nscoord maxSupScriptShift = supScriptShift;
261 ReflowOutput baseSize(wm);
262 ReflowOutput subScriptSize(wm);
263 ReflowOutput supScriptSize(wm);
264 ReflowOutput multiSubSize(wm), multiSupSize(wm);
265 baseFrame = nullptr;
266 nsIFrame* subScriptFrame = nullptr;
267 nsIFrame* supScriptFrame = nullptr;
268 nsIFrame* prescriptsFrame = nullptr; // frame of <mprescripts/>, if there.
270 bool firstPrescriptsPair = false;
271 nsBoundingMetrics bmBase, bmSubScript, bmSupScript, bmMultiSub, bmMultiSup;
272 multiSubSize.SetBlockStartAscent(-0x7FFFFFFF);
273 multiSupSize.SetBlockStartAscent(-0x7FFFFFFF);
274 bmMultiSub.ascent = bmMultiSup.ascent = -0x7FFFFFFF;
275 bmMultiSub.descent = bmMultiSup.descent = -0x7FFFFFFF;
276 nscoord italicCorrection = 0;
278 nsBoundingMetrics boundingMetrics;
279 boundingMetrics.width = 0;
280 boundingMetrics.ascent = boundingMetrics.descent = -0x7FFFFFFF;
281 aDesiredSize.Width() = aDesiredSize.Height() = 0;
283 int32_t count = 0;
285 // Boolean to determine whether the current child is a subscript.
286 // Note that only msup starts with a superscript.
287 bool isSubScript = (tag != nsGkAtoms::msup_);
289 nsIFrame* childFrame = aFrame->PrincipalChildList().FirstChild();
290 while (childFrame) {
291 if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) {
292 if (tag != nsGkAtoms::mmultiscripts_) {
293 if (aPlaceOrigin) {
294 aFrame->ReportInvalidChildError(nsGkAtoms::mprescripts_);
296 return aFrame->PlaceAsMrow(aDrawTarget, aPlaceOrigin, aDesiredSize);
298 if (prescriptsFrame) {
299 // duplicate <mprescripts/> found
300 // report an error, encourage people to get their markups in order
301 if (aPlaceOrigin) {
302 aFrame->ReportErrorToConsole("DuplicateMprescripts");
304 return aFrame->PlaceAsMrow(aDrawTarget, aPlaceOrigin, aDesiredSize);
306 if (!isSubScript) {
307 if (aPlaceOrigin) {
308 aFrame->ReportErrorToConsole("SubSupMismatch");
310 return aFrame->PlaceAsMrow(aDrawTarget, aPlaceOrigin, aDesiredSize);
313 prescriptsFrame = childFrame;
314 firstPrescriptsPair = true;
315 } else if (0 == count) {
316 // base
317 baseFrame = childFrame;
318 GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
320 if (tag != nsGkAtoms::msub_) {
321 // Apply italics correction if there is the potential for a
322 // postsupscript.
323 GetItalicCorrection(bmBase, italicCorrection);
324 // If italics correction is applied, we always add "a little to spare"
325 // (see TeXbook Ch.11, p.64), as we estimate the italic creation
326 // ourselves and it isn't the same as TeX.
327 italicCorrection += onePixel;
330 // we update boundingMetrics.{ascent,descent} with that
331 // of the baseFrame only after processing all the sup/sub pairs
332 boundingMetrics.width = bmBase.width;
333 boundingMetrics.rightBearing = bmBase.rightBearing;
334 boundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten
335 } else {
336 // super/subscript block
337 if (isSubScript) {
338 // subscript
339 subScriptFrame = childFrame;
340 GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize,
341 bmSubScript);
342 if (!mathFont) {
343 // get the subdrop from the subscript font
344 GetSubDropFromChild(subScriptFrame, subDrop, aFontSizeInflation);
347 // parameter v, Rule 18a, App. G, TeXbook
348 minSubScriptShift = bmBase.descent + subDrop;
349 trySubScriptShift = std::max(minSubScriptShift, subScriptShift);
350 multiSubSize.SetBlockStartAscent(std::max(
351 multiSubSize.BlockStartAscent(), subScriptSize.BlockStartAscent()));
352 bmMultiSub.ascent = std::max(bmMultiSub.ascent, bmSubScript.ascent);
353 bmMultiSub.descent = std::max(bmMultiSub.descent, bmSubScript.descent);
354 multiSubSize.Height() =
355 std::max(multiSubSize.Height(),
356 subScriptSize.Height() - subScriptSize.BlockStartAscent());
357 if (bmSubScript.width) width = bmSubScript.width + scriptSpace;
358 rightBearing = bmSubScript.rightBearing;
360 if (tag == nsGkAtoms::msub_) {
361 boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
362 boundingMetrics.width += width;
364 nscoord subscriptTopMax;
365 if (mathFont) {
366 subscriptTopMax = mathFont->MathTable()->Constant(
367 gfxMathTable::SubscriptTopMax, oneDevPixel);
368 } else {
369 // get min subscript shift limit from x-height
370 // = h(x) - 4/5 * sigma_5, Rule 18b, App. G, TeXbook
371 subscriptTopMax = NSToCoordRound((4.0f / 5.0f) * xHeight);
373 nscoord minShiftFromXHeight = bmSubScript.ascent - subscriptTopMax;
374 maxSubScriptShift = std::max(trySubScriptShift, minShiftFromXHeight);
376 maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
377 trySubScriptShift = subScriptShift;
379 } else {
380 // supscript
381 supScriptFrame = childFrame;
382 GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize,
383 bmSupScript);
384 if (!mathFont) {
385 // get the supdrop from the supscript font
386 GetSupDropFromChild(supScriptFrame, supDrop, aFontSizeInflation);
388 // parameter u, Rule 18a, App. G, TeXbook
389 minSupScriptShift = bmBase.ascent - supDrop;
390 nscoord superscriptBottomMin;
391 if (mathFont) {
392 superscriptBottomMin = mathFont->MathTable()->Constant(
393 gfxMathTable::SuperscriptBottomMin, oneDevPixel);
394 } else {
395 // get min supscript shift limit from x-height
396 // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook
397 superscriptBottomMin = NSToCoordRound((1.0f / 4.0f) * xHeight);
399 minShiftFromXHeight = bmSupScript.descent + superscriptBottomMin;
400 trySupScriptShift = std::max(
401 minSupScriptShift, std::max(minShiftFromXHeight, supScriptShift));
402 multiSupSize.SetBlockStartAscent(std::max(
403 multiSupSize.BlockStartAscent(), supScriptSize.BlockStartAscent()));
404 bmMultiSup.ascent = std::max(bmMultiSup.ascent, bmSupScript.ascent);
405 bmMultiSup.descent = std::max(bmMultiSup.descent, bmSupScript.descent);
406 multiSupSize.Height() =
407 std::max(multiSupSize.Height(),
408 supScriptSize.Height() - supScriptSize.BlockStartAscent());
410 if (bmSupScript.width)
411 width = std::max(width, bmSupScript.width + scriptSpace);
413 if (!prescriptsFrame) { // we are still looping over base & postscripts
414 rightBearing = std::max(rightBearing,
415 italicCorrection + bmSupScript.rightBearing);
416 boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
417 boundingMetrics.width += width;
418 } else {
419 prescriptsWidth += width;
420 if (firstPrescriptsPair) {
421 firstPrescriptsPair = false;
422 boundingMetrics.leftBearing =
423 std::min(bmSubScript.leftBearing, bmSupScript.leftBearing);
426 width = rightBearing = 0;
428 // negotiate between the various shifts so that
429 // there is enough gap between the sup and subscripts
430 // Rule 18e, App. G, TeXbook
431 if (tag == nsGkAtoms::mmultiscripts_ || tag == nsGkAtoms::msubsup_) {
432 nscoord subSuperscriptGapMin;
433 if (mathFont) {
434 subSuperscriptGapMin = mathFont->MathTable()->Constant(
435 gfxMathTable::SubSuperscriptGapMin, oneDevPixel);
436 } else {
437 nscoord ruleSize;
438 GetRuleThickness(aDrawTarget, fm, ruleSize);
439 subSuperscriptGapMin = 4 * ruleSize;
441 nscoord gap = (trySupScriptShift - bmSupScript.descent) -
442 (bmSubScript.ascent - trySubScriptShift);
443 if (gap < subSuperscriptGapMin) {
444 // adjust trySubScriptShift to get a gap of subSuperscriptGapMin
445 trySubScriptShift += subSuperscriptGapMin - gap;
448 // next we want to ensure that the bottom of the superscript
449 // will be > superscriptBottomMaxWithSubscript
450 nscoord superscriptBottomMaxWithSubscript;
451 if (mathFont) {
452 superscriptBottomMaxWithSubscript = mathFont->MathTable()->Constant(
453 gfxMathTable::SuperscriptBottomMaxWithSubscript, oneDevPixel);
454 } else {
455 superscriptBottomMaxWithSubscript =
456 NSToCoordRound((4.0f / 5.0f) * xHeight);
458 gap = superscriptBottomMaxWithSubscript -
459 (trySupScriptShift - bmSupScript.descent);
460 if (gap > 0) {
461 trySupScriptShift += gap;
462 trySubScriptShift -= gap;
466 maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
467 maxSupScriptShift = std::max(maxSupScriptShift, trySupScriptShift);
469 trySubScriptShift = subScriptShift;
470 trySupScriptShift = supScriptShift;
473 isSubScript = !isSubScript;
475 count++;
476 childFrame = childFrame->GetNextSibling();
479 // NoBase error may also have been reported above
480 if ((count != 2 && (tag == nsGkAtoms::msup_ || tag == nsGkAtoms::msub_)) ||
481 (count != 3 && tag == nsGkAtoms::msubsup_) || !baseFrame ||
482 (!isSubScript && tag == nsGkAtoms::mmultiscripts_)) {
483 // report an error, encourage people to get their markups in order
484 if (aPlaceOrigin) {
485 if ((count != 2 &&
486 (tag == nsGkAtoms::msup_ || tag == nsGkAtoms::msub_)) ||
487 (count != 3 && tag == nsGkAtoms::msubsup_)) {
488 aFrame->ReportChildCountError();
489 } else if (!baseFrame) {
490 aFrame->ReportErrorToConsole("NoBase");
491 } else {
492 aFrame->ReportErrorToConsole("SubSupMismatch");
495 return aFrame->PlaceAsMrow(aDrawTarget, aPlaceOrigin, aDesiredSize);
498 // we left out the width of prescripts, so ...
499 boundingMetrics.rightBearing += prescriptsWidth;
500 boundingMetrics.width += prescriptsWidth;
502 // Zero out the shifts in where a frame isn't present to avoid the potential
503 // for overflow.
504 if (!subScriptFrame) maxSubScriptShift = 0;
505 if (!supScriptFrame) maxSupScriptShift = 0;
507 // we left out the base during our bounding box updates, so ...
508 if (tag == nsGkAtoms::msub_) {
509 boundingMetrics.ascent =
510 std::max(bmBase.ascent, bmMultiSub.ascent - maxSubScriptShift);
511 } else {
512 boundingMetrics.ascent =
513 std::max(bmBase.ascent, (bmMultiSup.ascent + maxSupScriptShift));
515 if (tag == nsGkAtoms::msup_) {
516 boundingMetrics.descent =
517 std::max(bmBase.descent, bmMultiSup.descent - maxSupScriptShift);
518 } else {
519 boundingMetrics.descent =
520 std::max(bmBase.descent, (bmMultiSub.descent + maxSubScriptShift));
522 aFrame->SetBoundingMetrics(boundingMetrics);
524 // get the reflow metrics ...
525 aDesiredSize.SetBlockStartAscent(
526 std::max(baseSize.BlockStartAscent(),
527 std::max(multiSubSize.BlockStartAscent() - maxSubScriptShift,
528 multiSupSize.BlockStartAscent() + maxSupScriptShift)));
529 aDesiredSize.Height() =
530 aDesiredSize.BlockStartAscent() +
531 std::max(baseSize.Height() - baseSize.BlockStartAscent(),
532 std::max(multiSubSize.Height() + maxSubScriptShift,
533 multiSupSize.Height() - maxSupScriptShift));
534 aDesiredSize.Width() = boundingMetrics.width;
535 aDesiredSize.mBoundingMetrics = boundingMetrics;
537 aFrame->SetReference(nsPoint(0, aDesiredSize.BlockStartAscent()));
539 //////////////////
540 // Place Children
542 // Place prescripts, followed by base, and then postscripts.
543 // The list of frames is in the order: {base} {postscripts} {prescripts}
544 // We go over the list in a circular manner, starting at <prescripts/>
546 if (aPlaceOrigin) {
547 nscoord dx = 0, dy = 0;
549 // With msub and msup there is only one element and
550 // subscriptFrame/supScriptFrame have already been set above where
551 // relevant. In these cases we skip to the reflow part.
552 if (tag == nsGkAtoms::msub_ || tag == nsGkAtoms::msup_)
553 count = 1;
554 else
555 count = 0;
556 childFrame = prescriptsFrame;
557 bool isPreScript = true;
558 do {
559 if (!childFrame) { // end of prescripts,
560 isPreScript = false;
561 // place the base ...
562 childFrame = baseFrame;
563 dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
564 FinishReflowChild(
565 baseFrame, aPresContext, baseSize, nullptr,
566 aFrame->MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx), dy,
567 ReflowChildFlags::Default);
568 dx += bmBase.width;
569 } else if (prescriptsFrame == childFrame) {
570 // Clear reflow flags of prescripts frame.
571 prescriptsFrame->DidReflow(aPresContext, nullptr);
572 } else {
573 // process each sup/sub pair
574 if (0 == count) {
575 subScriptFrame = childFrame;
576 count = 1;
577 } else if (1 == count) {
578 if (tag != nsGkAtoms::msub_) supScriptFrame = childFrame;
579 count = 0;
581 // get the ascent/descent of sup/subscripts stored in their rects
582 // rect.x = descent, rect.y = ascent
583 if (subScriptFrame)
584 GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize,
585 bmSubScript);
586 if (supScriptFrame)
587 GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize,
588 bmSupScript);
590 width = std::max(subScriptSize.Width(), supScriptSize.Width());
592 if (subScriptFrame) {
593 nscoord x = dx;
594 // prescripts should be right aligned
595 // https://bugzilla.mozilla.org/show_bug.cgi?id=928675
596 if (isPreScript) x += width - subScriptSize.Width();
597 dy = aDesiredSize.BlockStartAscent() -
598 subScriptSize.BlockStartAscent() + maxSubScriptShift;
599 FinishReflowChild(subScriptFrame, aPresContext, subScriptSize,
600 nullptr,
601 aFrame->MirrorIfRTL(aDesiredSize.Width(),
602 subScriptSize.Width(), x),
603 dy, ReflowChildFlags::Default);
606 if (supScriptFrame) {
607 nscoord x = dx;
608 if (isPreScript) {
609 x += width - supScriptSize.Width();
610 } else {
611 // post superscripts are shifted by the italic correction value
612 x += italicCorrection;
614 dy = aDesiredSize.BlockStartAscent() -
615 supScriptSize.BlockStartAscent() - maxSupScriptShift;
616 FinishReflowChild(supScriptFrame, aPresContext, supScriptSize,
617 nullptr,
618 aFrame->MirrorIfRTL(aDesiredSize.Width(),
619 supScriptSize.Width(), x),
620 dy, ReflowChildFlags::Default);
622 dx += width + scriptSpace;
625 childFrame = childFrame->GetNextSibling();
626 } while (prescriptsFrame != childFrame);
629 return NS_OK;