Bug 1472840 [wpt PR 11759] - [css-logical] Implement flow-relative margin, padding...
[gecko.git] / layout / mathml / nsMathMLmmultiscriptsFrame.cpp
blobcf887f651a7c7e4317da345a046285e861384c06
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/. */
8 #include "nsMathMLmmultiscriptsFrame.h"
9 #include "nsPresContext.h"
10 #include <algorithm>
11 #include "gfxContext.h"
12 #include "gfxMathTable.h"
14 using mozilla::WritingMode;
17 // <mmultiscripts> -- attach prescripts and tensor indices to a base - implementation
18 // <msub> -- attach a subscript to a base - implementation
19 // <msubsup> -- attach a subscript-superscript pair to a base - implementation
20 // <msup> -- attach a superscript to a base - implementation
23 nsIFrame*
24 NS_NewMathMLmmultiscriptsFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle)
26 return new (aPresShell) nsMathMLmmultiscriptsFrame(aStyle);
29 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmmultiscriptsFrame)
31 nsMathMLmmultiscriptsFrame::~nsMathMLmmultiscriptsFrame()
35 uint8_t
36 nsMathMLmmultiscriptsFrame::ScriptIncrement(nsIFrame* aFrame)
38 if (!aFrame)
39 return 0;
40 if (mFrames.ContainsFrame(aFrame)) {
41 if (mFrames.FirstChild() == aFrame ||
42 aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) {
43 return 0; // No script increment for base frames or prescript markers
45 return 1;
47 return 0; //not a child
50 NS_IMETHODIMP
51 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,
88 NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
91 return NS_OK;
94 /* virtual */ nsresult
95 nsMathMLmmultiscriptsFrame::Place(DrawTarget* aDrawTarget,
96 bool aPlaceOrigin,
97 ReflowOutput& aDesiredSize)
99 nscoord subScriptShift = 0;
100 nscoord supScriptShift = 0;
101 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
103 // subscriptshift
105 // "Specifies the minimum amount to shift the baseline of subscript down; the
106 // default is for the rendering agent to use its own positioning rules."
108 // values: length
109 // default: automatic
111 // We use 0 as the default value so unitless values can be ignored.
112 // As a minimum, negative values can be ignored.
114 nsAutoString value;
115 if (!mContent->IsMathMLElement(nsGkAtoms::msup_)) {
116 mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::subscriptshift_, value);
117 if (!value.IsEmpty()) {
118 ParseNumericValue(value, &subScriptShift, 0, PresContext(),
119 mComputedStyle, fontSizeInflation);
122 // superscriptshift
124 // "Specifies the minimum amount to shift the baseline of superscript up; the
125 // default is for the rendering agent to use its own positioning rules."
127 // values: length
128 // default: automatic
130 // We use 0 as the default value so unitless values can be ignored.
131 // As a minimum, negative values can be ignored.
133 if (!mContent->IsMathMLElement(nsGkAtoms::msub_)) {
134 mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::superscriptshift_, value);
135 if (!value.IsEmpty()) {
136 ParseNumericValue(value, &supScriptShift, 0, PresContext(),
137 mComputedStyle, fontSizeInflation);
140 return PlaceMultiScript(PresContext(), aDrawTarget, aPlaceOrigin,
141 aDesiredSize, this, subScriptShift, supScriptShift,
142 fontSizeInflation);
145 // exported routine that both munderover and mmultiscripts share.
146 // munderover uses this when movablelimits is set.
147 nsresult
148 nsMathMLmmultiscriptsFrame::PlaceMultiScript(nsPresContext* aPresContext,
149 DrawTarget* aDrawTarget,
150 bool aPlaceOrigin,
151 ReflowOutput& aDesiredSize,
152 nsMathMLContainerFrame* aFrame,
153 nscoord aUserSubScriptShift,
154 nscoord aUserSupScriptShift,
155 float aFontSizeInflation)
157 nsAtom* tag = aFrame->GetContent()->NodeInfo()->NameAtom();
159 // This function deals with both munderover etc. as well as msubsup etc.
160 // As the former behaves identically to the later, we treat it as such
161 // to avoid additional checks later.
162 if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mover_))
163 tag = nsGkAtoms::msup_;
164 else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munder_))
165 tag = nsGkAtoms::msub_;
166 else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munderover_))
167 tag = nsGkAtoms::msubsup_;
169 nsBoundingMetrics bmFrame;
171 nscoord minShiftFromXHeight, subDrop, supDrop;
173 ////////////////////////////////////////
174 // Initialize super/sub shifts that
175 // depend only on the current font
176 ////////////////////////////////////////
178 nsIFrame* baseFrame = aFrame->PrincipalChildList().FirstChild();
180 if (!baseFrame) {
181 if (tag == nsGkAtoms::mmultiscripts_)
182 aFrame->ReportErrorToConsole("NoBase");
183 else
184 aFrame->ReportChildCountError();
185 return aFrame->ReflowError(aDrawTarget, aDesiredSize);
188 // get x-height (an ex)
189 const nsStyleFont* font = aFrame->StyleFont();
190 RefPtr<nsFontMetrics> fm =
191 nsLayoutUtils::GetFontMetricsForFrame(baseFrame, aFontSizeInflation);
193 nscoord xHeight = fm->XHeight();
195 nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
196 gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
197 // scriptspace from TeX for extra spacing after sup/subscript
198 nscoord scriptSpace;
199 if (mathFont) {
200 scriptSpace = mathFont->MathTable()->
201 Constant(gfxMathTable::SpaceAfterScript, oneDevPixel);
202 } else {
203 // (0.5pt in plain TeX)
204 scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f);
207 // Try and read sub and sup drops from the MATH table.
208 if (mathFont) {
209 subDrop = mathFont->MathTable()->
210 Constant(gfxMathTable::SubscriptBaselineDropMin, oneDevPixel);
211 supDrop = mathFont->MathTable()->
212 Constant(gfxMathTable::SuperscriptBaselineDropMax, oneDevPixel);
215 // force the scriptSpace to be at least 1 pixel
216 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
217 scriptSpace = std::max(onePixel, scriptSpace);
219 /////////////////////////////////////
220 // first the shift for the subscript
222 nscoord subScriptShift;
223 if (mathFont) {
224 // Try and get the sub script shift from the MATH table. Note that contrary
225 // to TeX we only have one parameter.
226 subScriptShift = mathFont->MathTable()->
227 Constant(gfxMathTable::SubscriptShiftDown, oneDevPixel);
228 } else {
229 // subScriptShift{1,2}
230 // = minimum amount to shift the subscript down
231 // = sub{1,2} in TeXbook
232 // subScriptShift1 = subscriptshift attribute * x-height
233 nscoord subScriptShift1, subScriptShift2;
234 // Get subScriptShift{1,2} default from font
235 GetSubScriptShifts (fm, subScriptShift1, subScriptShift2);
236 if (tag == nsGkAtoms::msub_) {
237 subScriptShift = subScriptShift1;
238 } else {
239 subScriptShift = std::max(subScriptShift1, subScriptShift2);
243 if (0 < aUserSubScriptShift) {
244 // the user has set the subscriptshift attribute
245 subScriptShift = std::max(subScriptShift, aUserSubScriptShift);
248 /////////////////////////////////////
249 // next the shift for the superscript
251 nscoord supScriptShift;
252 nsPresentationData presentationData;
253 aFrame->GetPresentationData(presentationData);
254 if (mathFont) {
255 // Try and get the super script shift from the MATH table. Note that
256 // contrary to TeX we only have two parameters.
257 supScriptShift = mathFont->
258 MathTable()->Constant(NS_MATHML_IS_COMPRESSED(presentationData.flags) ?
259 gfxMathTable::SuperscriptShiftUpCramped :
260 gfxMathTable::SuperscriptShiftUp,
261 oneDevPixel);
262 } else {
263 // supScriptShift{1,2,3}
264 // = minimum amount to shift the supscript up
265 // = sup{1,2,3} in TeX
266 // supScriptShift1 = superscriptshift attribute * x-height
267 // Note that there are THREE values for supscript shifts depending
268 // on the current style
269 nscoord supScriptShift1, supScriptShift2, supScriptShift3;
270 // Set supScriptShift{1,2,3} default from font
271 GetSupScriptShifts (fm, supScriptShift1, supScriptShift2, supScriptShift3);
273 // get sup script shift depending on current script level and display style
274 // Rule 18c, App. G, TeXbook
275 if (font->mScriptLevel == 0 &&
276 font->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK &&
277 !NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
278 // Style D in TeXbook
279 supScriptShift = supScriptShift1;
280 } else if (NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
281 // Style C' in TeXbook = D',T',S',SS'
282 supScriptShift = supScriptShift3;
283 } else {
284 // everything else = T,S,SS
285 supScriptShift = supScriptShift2;
289 if (0 < aUserSupScriptShift) {
290 // the user has set the supscriptshift attribute
291 supScriptShift = std::max(supScriptShift, aUserSupScriptShift);
294 ////////////////////////////////////
295 // Get the children's sizes
296 ////////////////////////////////////
298 const WritingMode wm(aDesiredSize.GetWritingMode());
299 nscoord width = 0, prescriptsWidth = 0, rightBearing = 0;
300 nscoord minSubScriptShift = 0, minSupScriptShift = 0;
301 nscoord trySubScriptShift = subScriptShift;
302 nscoord trySupScriptShift = supScriptShift;
303 nscoord maxSubScriptShift = subScriptShift;
304 nscoord maxSupScriptShift = supScriptShift;
305 ReflowOutput baseSize(wm);
306 ReflowOutput subScriptSize(wm);
307 ReflowOutput supScriptSize(wm);
308 ReflowOutput multiSubSize(wm), multiSupSize(wm);
309 baseFrame = nullptr;
310 nsIFrame* subScriptFrame = nullptr;
311 nsIFrame* supScriptFrame = nullptr;
312 nsIFrame* prescriptsFrame = nullptr; // frame of <mprescripts/>, if there.
314 bool firstPrescriptsPair = false;
315 nsBoundingMetrics bmBase, bmSubScript, bmSupScript, bmMultiSub, bmMultiSup;
316 multiSubSize.SetBlockStartAscent(-0x7FFFFFFF);
317 multiSupSize.SetBlockStartAscent(-0x7FFFFFFF);
318 bmMultiSub.ascent = bmMultiSup.ascent = -0x7FFFFFFF;
319 bmMultiSub.descent = bmMultiSup.descent = -0x7FFFFFFF;
320 nscoord italicCorrection = 0;
322 nsBoundingMetrics boundingMetrics;
323 boundingMetrics.width = 0;
324 boundingMetrics.ascent = boundingMetrics.descent = -0x7FFFFFFF;
325 aDesiredSize.Width() = aDesiredSize.Height() = 0;
327 int32_t count = 0;
328 bool foundNoneTag = false;
330 // Boolean to determine whether the current child is a subscript.
331 // Note that only msup starts with a superscript.
332 bool isSubScript = (tag != nsGkAtoms::msup_);
334 nsIFrame* childFrame = aFrame->PrincipalChildList().FirstChild();
335 while (childFrame) {
336 if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) {
337 if (tag != nsGkAtoms::mmultiscripts_) {
338 if (aPlaceOrigin) {
339 aFrame->ReportInvalidChildError(nsGkAtoms::mprescripts_);
341 return aFrame->ReflowError(aDrawTarget, aDesiredSize);
343 if (prescriptsFrame) {
344 // duplicate <mprescripts/> found
345 // report an error, encourage people to get their markups in order
346 if (aPlaceOrigin) {
347 aFrame->ReportErrorToConsole("DuplicateMprescripts");
349 return aFrame->ReflowError(aDrawTarget, aDesiredSize);
351 if (!isSubScript) {
352 if (aPlaceOrigin) {
353 aFrame->ReportErrorToConsole("SubSupMismatch");
355 return aFrame->ReflowError(aDrawTarget, aDesiredSize);
358 prescriptsFrame = childFrame;
359 firstPrescriptsPair = true;
360 } else if (0 == count) {
361 // base
363 if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::none)) {
364 if (tag == nsGkAtoms::mmultiscripts_) {
365 if (aPlaceOrigin) {
366 aFrame->ReportErrorToConsole("NoBase");
368 return aFrame->ReflowError(aDrawTarget, aDesiredSize);
369 } else {
370 //A different error message is triggered later for the other tags
371 foundNoneTag = true;
374 baseFrame = childFrame;
375 GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
377 if (tag != nsGkAtoms::msub_) {
378 // Apply italics correction if there is the potential for a
379 // postsupscript.
380 GetItalicCorrection(bmBase, italicCorrection);
381 // If italics correction is applied, we always add "a little to spare"
382 // (see TeXbook Ch.11, p.64), as we estimate the italic creation
383 // ourselves and it isn't the same as TeX.
384 italicCorrection += onePixel;
387 // we update boundingMetrics.{ascent,descent} with that
388 // of the baseFrame only after processing all the sup/sub pairs
389 boundingMetrics.width = bmBase.width;
390 boundingMetrics.rightBearing = bmBase.rightBearing;
391 boundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten
392 } else {
393 // super/subscript block
394 if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::none)) {
395 foundNoneTag = true;
398 if (isSubScript) {
399 // subscript
400 subScriptFrame = childFrame;
401 GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
402 if (!mathFont) {
403 // get the subdrop from the subscript font
404 GetSubDropFromChild (subScriptFrame, subDrop, aFontSizeInflation);
407 // parameter v, Rule 18a, App. G, TeXbook
408 minSubScriptShift = bmBase.descent + subDrop;
409 trySubScriptShift = std::max(minSubScriptShift,subScriptShift);
410 multiSubSize.SetBlockStartAscent(
411 std::max(multiSubSize.BlockStartAscent(),
412 subScriptSize.BlockStartAscent()));
413 bmMultiSub.ascent = std::max(bmMultiSub.ascent, bmSubScript.ascent);
414 bmMultiSub.descent = std::max(bmMultiSub.descent, bmSubScript.descent);
415 multiSubSize.Height() =
416 std::max(multiSubSize.Height(),
417 subScriptSize.Height() - subScriptSize.BlockStartAscent());
418 if (bmSubScript.width)
419 width = bmSubScript.width + scriptSpace;
420 rightBearing = bmSubScript.rightBearing;
422 if (tag == nsGkAtoms::msub_) {
423 boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
424 boundingMetrics.width += width;
426 nscoord subscriptTopMax;
427 if (mathFont) {
428 subscriptTopMax =
429 mathFont->MathTable()->Constant(gfxMathTable::SubscriptTopMax,
430 oneDevPixel);
431 } else {
432 // get min subscript shift limit from x-height
433 // = h(x) - 4/5 * sigma_5, Rule 18b, App. G, TeXbook
434 subscriptTopMax = NSToCoordRound((4.0f/5.0f) * xHeight);
436 nscoord minShiftFromXHeight = bmSubScript.ascent - subscriptTopMax;
437 maxSubScriptShift = std::max(trySubScriptShift,minShiftFromXHeight);
439 maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
440 trySubScriptShift = subScriptShift;
442 } else {
443 // supscript
444 supScriptFrame = childFrame;
445 GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
446 if (!mathFont) {
447 // get the supdrop from the supscript font
448 GetSupDropFromChild (supScriptFrame, supDrop, aFontSizeInflation);
450 // parameter u, Rule 18a, App. G, TeXbook
451 minSupScriptShift = bmBase.ascent - supDrop;
452 nscoord superscriptBottomMin;
453 if (mathFont) {
454 superscriptBottomMin =
455 mathFont->MathTable()->Constant(gfxMathTable::SuperscriptBottomMin,
456 oneDevPixel);
457 } else {
458 // get min supscript shift limit from x-height
459 // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook
460 superscriptBottomMin = NSToCoordRound((1.0f / 4.0f) * xHeight);
462 minShiftFromXHeight = bmSupScript.descent + superscriptBottomMin;
463 trySupScriptShift = std::max(minSupScriptShift,
464 std::max(minShiftFromXHeight,
465 supScriptShift));
466 multiSupSize.SetBlockStartAscent(
467 std::max(multiSupSize.BlockStartAscent(),
468 supScriptSize.BlockStartAscent()));
469 bmMultiSup.ascent = std::max(bmMultiSup.ascent, bmSupScript.ascent);
470 bmMultiSup.descent = std::max(bmMultiSup.descent, bmSupScript.descent);
471 multiSupSize.Height() =
472 std::max(multiSupSize.Height(),
473 supScriptSize.Height() - supScriptSize.BlockStartAscent());
475 if (bmSupScript.width)
476 width = std::max(width, bmSupScript.width + scriptSpace);
478 if (!prescriptsFrame) { // we are still looping over base & postscripts
479 rightBearing = std::max(rightBearing,
480 italicCorrection + bmSupScript.rightBearing);
481 boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
482 boundingMetrics.width += width;
483 } else {
484 prescriptsWidth += width;
485 if (firstPrescriptsPair) {
486 firstPrescriptsPair = false;
487 boundingMetrics.leftBearing =
488 std::min(bmSubScript.leftBearing, bmSupScript.leftBearing);
491 width = rightBearing = 0;
493 // negotiate between the various shifts so that
494 // there is enough gap between the sup and subscripts
495 // Rule 18e, App. G, TeXbook
496 if (tag == nsGkAtoms::mmultiscripts_ ||
497 tag == nsGkAtoms::msubsup_) {
498 nscoord subSuperscriptGapMin;
499 if (mathFont) {
500 subSuperscriptGapMin = mathFont->MathTable()->
501 Constant(gfxMathTable::SubSuperscriptGapMin, oneDevPixel);
502 } else {
503 nscoord ruleSize;
504 GetRuleThickness(aDrawTarget, fm, ruleSize);
505 subSuperscriptGapMin = 4 * ruleSize;
507 nscoord gap =
508 (trySupScriptShift - bmSupScript.descent) -
509 (bmSubScript.ascent - trySubScriptShift);
510 if (gap < subSuperscriptGapMin) {
511 // adjust trySubScriptShift to get a gap of subSuperscriptGapMin
512 trySubScriptShift += subSuperscriptGapMin - gap;
515 // next we want to ensure that the bottom of the superscript
516 // will be > superscriptBottomMaxWithSubscript
517 nscoord superscriptBottomMaxWithSubscript;
518 if (mathFont) {
519 superscriptBottomMaxWithSubscript = mathFont->MathTable()->
520 Constant(gfxMathTable::SuperscriptBottomMaxWithSubscript,
521 oneDevPixel);
522 } else {
523 superscriptBottomMaxWithSubscript =
524 NSToCoordRound((4.0f / 5.0f) * xHeight);
526 gap = superscriptBottomMaxWithSubscript -
527 (trySupScriptShift - bmSupScript.descent);
528 if (gap > 0) {
529 trySupScriptShift += gap;
530 trySubScriptShift -= gap;
534 maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
535 maxSupScriptShift = std::max(maxSupScriptShift, trySupScriptShift);
537 trySubScriptShift = subScriptShift;
538 trySupScriptShift = supScriptShift;
541 isSubScript = !isSubScript;
543 count++;
544 childFrame = childFrame->GetNextSibling();
547 //NoBase error may also have been reported above
548 if ((count != 2 && (tag == nsGkAtoms::msup_ || tag == nsGkAtoms::msub_)) ||
549 (count != 3 && tag == nsGkAtoms::msubsup_) || !baseFrame ||
550 (foundNoneTag && tag != nsGkAtoms::mmultiscripts_) ||
551 (!isSubScript && tag == nsGkAtoms::mmultiscripts_)) {
552 // report an error, encourage people to get their markups in order
553 if (aPlaceOrigin) {
554 if ((count != 2 && (tag == nsGkAtoms::msup_ ||
555 tag == nsGkAtoms::msub_)) ||
556 (count != 3 && tag == nsGkAtoms::msubsup_ )) {
557 aFrame->ReportChildCountError();
558 } else if (foundNoneTag && tag != nsGkAtoms::mmultiscripts_) {
559 aFrame->ReportInvalidChildError(nsGkAtoms::none);
560 } else if (!baseFrame) {
561 aFrame->ReportErrorToConsole("NoBase");
562 } else {
563 aFrame->ReportErrorToConsole("SubSupMismatch");
566 return aFrame->ReflowError(aDrawTarget, aDesiredSize);
569 // we left out the width of prescripts, so ...
570 boundingMetrics.rightBearing += prescriptsWidth;
571 boundingMetrics.width += prescriptsWidth;
573 // Zero out the shifts in where a frame isn't present to avoid the potential
574 // for overflow.
575 if (!subScriptFrame)
576 maxSubScriptShift = 0;
577 if (!supScriptFrame)
578 maxSupScriptShift = 0;
580 // we left out the base during our bounding box updates, so ...
581 if (tag == nsGkAtoms::msub_) {
582 boundingMetrics.ascent = std::max(bmBase.ascent,
583 bmMultiSub.ascent - maxSubScriptShift);
584 } else {
585 boundingMetrics.ascent =
586 std::max(bmBase.ascent, (bmMultiSup.ascent + maxSupScriptShift));
588 if (tag == nsGkAtoms::msup_) {
589 boundingMetrics.descent = std::max(bmBase.descent,
590 bmMultiSup.descent - maxSupScriptShift);
591 } else {
592 boundingMetrics.descent =
593 std::max(bmBase.descent, (bmMultiSub.descent + maxSubScriptShift));
595 aFrame->SetBoundingMetrics(boundingMetrics);
597 // get the reflow metrics ...
598 aDesiredSize.SetBlockStartAscent(
599 std::max(baseSize.BlockStartAscent(),
600 std::max(multiSubSize.BlockStartAscent() - maxSubScriptShift,
601 multiSupSize.BlockStartAscent() + maxSupScriptShift)));
602 aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
603 std::max(baseSize.Height() - baseSize.BlockStartAscent(),
604 std::max(multiSubSize.Height() + maxSubScriptShift,
605 multiSupSize.Height() - maxSupScriptShift));
606 aDesiredSize.Width() = boundingMetrics.width;
607 aDesiredSize.mBoundingMetrics = boundingMetrics;
609 aFrame->SetReference(nsPoint(0, aDesiredSize.BlockStartAscent()));
611 //////////////////
612 // Place Children
614 // Place prescripts, followed by base, and then postscripts.
615 // The list of frames is in the order: {base} {postscripts} {prescripts}
616 // We go over the list in a circular manner, starting at <prescripts/>
618 if (aPlaceOrigin) {
619 nscoord dx = 0, dy = 0;
621 // With msub and msup there is only one element and
622 // subscriptFrame/supScriptFrame have already been set above where
623 // relevant. In these cases we skip to the reflow part.
624 if (tag == nsGkAtoms::msub_ || tag == nsGkAtoms::msup_)
625 count = 1;
626 else
627 count = 0;
628 childFrame = prescriptsFrame;
629 bool isPreScript = true;
630 do {
631 if (!childFrame) { // end of prescripts,
632 isPreScript = false;
633 // place the base ...
634 childFrame = baseFrame;
635 dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
636 FinishReflowChild (baseFrame, aPresContext, baseSize, nullptr,
637 aFrame->MirrorIfRTL(aDesiredSize.Width(),
638 baseSize.Width(),
639 dx),
640 dy, 0);
641 dx += bmBase.width;
642 } else if (prescriptsFrame == childFrame) {
643 // Clear reflow flags of prescripts frame.
644 prescriptsFrame->DidReflow(aPresContext, nullptr);
645 } else {
646 // process each sup/sub pair
647 if (0 == count) {
648 subScriptFrame = childFrame;
649 count = 1;
650 } else if (1 == count) {
651 if (tag != nsGkAtoms::msub_)
652 supScriptFrame = childFrame;
653 count = 0;
655 // get the ascent/descent of sup/subscripts stored in their rects
656 // rect.x = descent, rect.y = ascent
657 if (subScriptFrame)
658 GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
659 if (supScriptFrame)
660 GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
662 width = std::max(subScriptSize.Width(), supScriptSize.Width());
664 if (subScriptFrame) {
665 nscoord x = dx;
666 // prescripts should be right aligned
667 // https://bugzilla.mozilla.org/show_bug.cgi?id=928675
668 if (isPreScript)
669 x += width - subScriptSize.Width();
670 dy = aDesiredSize.BlockStartAscent() - subScriptSize.BlockStartAscent() +
671 maxSubScriptShift;
672 FinishReflowChild (subScriptFrame, aPresContext, subScriptSize,
673 nullptr,
674 aFrame->MirrorIfRTL(aDesiredSize.Width(),
675 subScriptSize.Width(),
677 dy, 0);
680 if (supScriptFrame) {
681 nscoord x = dx;
682 if (isPreScript) {
683 x += width - supScriptSize.Width();
684 } else {
685 // post superscripts are shifted by the italic correction value
686 x += italicCorrection;
688 dy = aDesiredSize.BlockStartAscent() - supScriptSize.BlockStartAscent() -
689 maxSupScriptShift;
690 FinishReflowChild (supScriptFrame, aPresContext, supScriptSize,
691 nullptr,
692 aFrame->MirrorIfRTL(aDesiredSize.Width(),
693 supScriptSize.Width(),
695 dy, 0);
697 dx += width + scriptSpace;
700 childFrame = childFrame->GetNextSibling();
701 } while (prescriptsFrame != childFrame);
704 return NS_OK;