Bumping manifests a=b2g-bump
[gecko.git] / layout / mathml / nsMathMLmunderoverFrame.cpp
blob5f7df60faafc9bd6d5a2e931f0f724c2997dc403
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsMathMLmunderoverFrame.h"
7 #include "nsPresContext.h"
8 #include "nsRenderingContext.h"
9 #include "nsMathMLmmultiscriptsFrame.h"
10 #include <algorithm>
13 // <munderover> -- attach an underscript-overscript pair to a base - implementation
14 // <mover> -- attach an overscript to a base - implementation
15 // <munder> -- attach an underscript to a base - implementation
18 nsIFrame*
19 NS_NewMathMLmunderoverFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
21 return new (aPresShell) nsMathMLmunderoverFrame(aContext);
24 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmunderoverFrame)
26 nsMathMLmunderoverFrame::~nsMathMLmunderoverFrame()
30 nsresult
31 nsMathMLmunderoverFrame::AttributeChanged(int32_t aNameSpaceID,
32 nsIAtom* aAttribute,
33 int32_t aModType)
35 if (nsGkAtoms::accent_ == aAttribute ||
36 nsGkAtoms::accentunder_ == aAttribute) {
37 // When we have automatic data to update within ourselves, we ask our
38 // parent to re-layout its children
39 return ReLayoutChildren(GetParent());
42 return nsMathMLContainerFrame::
43 AttributeChanged(aNameSpaceID, aAttribute, aModType);
46 NS_IMETHODIMP
47 nsMathMLmunderoverFrame::UpdatePresentationData(uint32_t aFlagsValues,
48 uint32_t aFlagsToUpdate)
50 nsMathMLContainerFrame::UpdatePresentationData(aFlagsValues, aFlagsToUpdate);
51 // disable the stretch-all flag if we are going to act like a subscript-superscript pair
52 if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
53 StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) {
54 mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
56 else {
57 mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
59 return NS_OK;
62 NS_IMETHODIMP
63 nsMathMLmunderoverFrame::InheritAutomaticData(nsIFrame* aParent)
65 // let the base class get the default from our parent
66 nsMathMLContainerFrame::InheritAutomaticData(aParent);
68 mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
70 return NS_OK;
73 uint8_t
74 nsMathMLmunderoverFrame::ScriptIncrement(nsIFrame* aFrame)
76 nsIFrame* child = mFrames.FirstChild();
77 if (!aFrame || aFrame == child) {
78 return 0;
80 child = child->GetNextSibling();
81 if (aFrame == child) {
82 if (mContent->Tag() == nsGkAtoms::mover_) {
83 return mIncrementOver ? 1 : 0;
85 return mIncrementUnder ? 1 : 0;
87 if (child && aFrame == child->GetNextSibling()) {
88 // must be a over frame of munderover
89 return mIncrementOver ? 1 : 0;
91 return 0; // frame not found
94 NS_IMETHODIMP
95 nsMathMLmunderoverFrame::TransmitAutomaticData()
97 // At this stage, all our children are in sync and we can fully
98 // resolve our own mEmbellishData struct
99 //---------------------------------------------------------------------
102 The REC says:
104 As regards munder (respectively mover) :
105 The default value of accentunder is false, unless underscript
106 is an <mo> element or an embellished operator. If underscript is
107 an <mo> element, the value of its accent attribute is used as the
108 default value of accentunder. If underscript is an embellished
109 operator, the accent attribute of the <mo> element at its
110 core is used as the default value. As with all attributes, an
111 explicitly given value overrides the default.
113 XXX The winner is the outermost setting in conflicting settings like these:
114 <munder accentunder='true'>
115 <mi>...</mi>
116 <mo accentunder='false'> ... </mo>
117 </munder>
119 As regards munderover:
120 The accent and accentunder attributes have the same effect as
121 the attributes with the same names on <mover> and <munder>,
122 respectively. Their default values are also computed in the
123 same manner as described for those elements, with the default
124 value of accent depending on overscript and the default value
125 of accentunder depending on underscript.
128 nsIFrame* overscriptFrame = nullptr;
129 nsIFrame* underscriptFrame = nullptr;
130 nsIFrame* baseFrame = mFrames.FirstChild();
131 nsIAtom* tag = mContent->Tag();
133 if (baseFrame) {
134 if (tag == nsGkAtoms::munder_ ||
135 tag == nsGkAtoms::munderover_) {
136 underscriptFrame = baseFrame->GetNextSibling();
137 } else {
138 NS_ASSERTION(tag == nsGkAtoms::mover_, "mContent->Tag() not recognized");
139 overscriptFrame = baseFrame->GetNextSibling();
142 if (underscriptFrame &&
143 tag == nsGkAtoms::munderover_) {
144 overscriptFrame = underscriptFrame->GetNextSibling();
148 // if our base is an embellished operator, let its state bubble to us (in particular,
149 // this is where we get the flag for NS_MATHML_EMBELLISH_MOVABLELIMITS). Our flags
150 // are reset to the default values of false if the base frame isn't embellished.
151 mPresentationData.baseFrame = baseFrame;
152 GetEmbellishDataFrom(baseFrame, mEmbellishData);
154 // The default value of accentunder is false, unless the underscript is embellished
155 // and its core <mo> is an accent
156 nsEmbellishData embellishData;
157 nsAutoString value;
158 if (tag == nsGkAtoms::munder_ ||
159 tag == nsGkAtoms::munderover_) {
160 GetEmbellishDataFrom(underscriptFrame, embellishData);
161 if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) {
162 mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER;
163 } else {
164 mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER;
167 // if we have an accentunder attribute, it overrides what the underscript said
168 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accentunder_, value)) {
169 if (value.EqualsLiteral("true")) {
170 mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER;
171 } else if (value.EqualsLiteral("false")) {
172 mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER;
177 // The default value of accent is false, unless the overscript is embellished
178 // and its core <mo> is an accent
179 if (tag == nsGkAtoms::mover_ ||
180 tag == nsGkAtoms::munderover_) {
181 GetEmbellishDataFrom(overscriptFrame, embellishData);
182 if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) {
183 mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER;
184 } else {
185 mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER;
188 // if we have an accent attribute, it overrides what the overscript said
189 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accent_, value)) {
190 if (value.EqualsLiteral("true")) {
191 mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER;
192 } else if (value.EqualsLiteral("false")) {
193 mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER;
198 bool subsupDisplay =
199 NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
200 StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE;
202 // disable the stretch-all flag if we are going to act like a superscript
203 if (subsupDisplay) {
204 mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
207 // Now transmit any change that we want to our children so that they
208 // can update their mPresentationData structs
209 //---------------------------------------------------------------------
211 /* The REC says:
212 Within underscript, <munderover> always sets displaystyle to "false",
213 but increments scriptlevel by 1 only when accentunder is "false".
215 Within overscript, <munderover> always sets displaystyle to "false",
216 but increments scriptlevel by 1 only when accent is "false".
218 Within subscript and superscript it increments scriptlevel by 1, and
219 sets displaystyle to "false", but leaves both attributes unchanged within
220 base.
222 The TeXBook treats 'over' like a superscript, so p.141 or Rule 13a
223 say it shouldn't be compressed. However, The TeXBook says
224 that math accents and \overline change uncramped styles to their
225 cramped counterparts.
227 if (tag == nsGkAtoms::mover_ ||
228 tag == nsGkAtoms::munderover_) {
229 uint32_t compress = NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)
230 ? NS_MATHML_COMPRESSED : 0;
231 mIncrementOver =
232 !NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) ||
233 subsupDisplay;
234 SetIncrementScriptLevel(tag == nsGkAtoms::mover_ ? 1 : 2, mIncrementOver);
235 if (mIncrementOver) {
236 PropagateFrameFlagFor(overscriptFrame,
237 NS_FRAME_MATHML_SCRIPT_DESCENDANT);
239 PropagatePresentationDataFor(overscriptFrame, compress, compress);
242 The TeXBook treats 'under' like a subscript, so p.141 or Rule 13a
243 say it should be compressed
245 if (tag == nsGkAtoms::munder_ ||
246 tag == nsGkAtoms::munderover_) {
247 mIncrementUnder =
248 !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags) ||
249 subsupDisplay;
250 SetIncrementScriptLevel(1, mIncrementUnder);
251 if (mIncrementUnder) {
252 PropagateFrameFlagFor(underscriptFrame,
253 NS_FRAME_MATHML_SCRIPT_DESCENDANT);
255 PropagatePresentationDataFor(underscriptFrame,
256 NS_MATHML_COMPRESSED,
257 NS_MATHML_COMPRESSED);
259 return NS_OK;
263 The REC says:
264 * If the base is an operator with movablelimits="true" (or an embellished
265 operator whose <mo> element core has movablelimits="true"), and
266 displaystyle="false", then underscript and overscript are drawn in
267 a subscript and superscript position, respectively. In this case,
268 the accent and accentunder attributes are ignored. This is often
269 used for limits on symbols such as &sum;.
271 i.e.,:
272 if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishDataflags) &&
273 StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) {
274 // place like subscript-superscript pair
276 else {
277 // place like underscript-overscript pair
281 /* virtual */ nsresult
282 nsMathMLmunderoverFrame::Place(nsRenderingContext& aRenderingContext,
283 bool aPlaceOrigin,
284 nsHTMLReflowMetrics& aDesiredSize)
286 nsIAtom* tag = mContent->Tag();
287 if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
288 StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) {
289 //place like sub sup or subsup
290 if (tag == nsGkAtoms::munderover_) {
291 return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(),
292 aRenderingContext,
293 aPlaceOrigin,
294 aDesiredSize,
295 this, 0, 0);
296 } else if (tag == nsGkAtoms::munder_) {
297 return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(),
298 aRenderingContext,
299 aPlaceOrigin,
300 aDesiredSize,
301 this, 0, 0);
302 } else {
303 NS_ASSERTION(tag == nsGkAtoms::mover_, "mContent->Tag() not recognized");
304 return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(),
305 aRenderingContext,
306 aPlaceOrigin,
307 aDesiredSize,
308 this, 0, 0);
313 ////////////////////////////////////
314 // Get the children's desired sizes
316 nsBoundingMetrics bmBase, bmUnder, bmOver;
317 nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode());
318 nsHTMLReflowMetrics underSize(aDesiredSize.GetWritingMode());
319 nsHTMLReflowMetrics overSize(aDesiredSize.GetWritingMode());
320 nsIFrame* overFrame = nullptr;
321 nsIFrame* underFrame = nullptr;
322 nsIFrame* baseFrame = mFrames.FirstChild();
323 underSize.SetBlockStartAscent(0);
324 overSize.SetBlockStartAscent(0);
325 bool haveError = false;
326 if (baseFrame) {
327 if (tag == nsGkAtoms::munder_ ||
328 tag == nsGkAtoms::munderover_) {
329 underFrame = baseFrame->GetNextSibling();
330 } else if (tag == nsGkAtoms::mover_) {
331 overFrame = baseFrame->GetNextSibling();
334 if (underFrame && tag == nsGkAtoms::munderover_) {
335 overFrame = underFrame->GetNextSibling();
338 if (tag == nsGkAtoms::munder_) {
339 if (!baseFrame || !underFrame || underFrame->GetNextSibling()) {
340 // report an error, encourage people to get their markups in order
341 haveError = true;
344 if (tag == nsGkAtoms::mover_) {
345 if (!baseFrame || !overFrame || overFrame->GetNextSibling()) {
346 // report an error, encourage people to get their markups in order
347 haveError = true;
350 if (tag == nsGkAtoms::munderover_) {
351 if (!baseFrame || !underFrame || !overFrame || overFrame->GetNextSibling()) {
352 // report an error, encourage people to get their markups in order
353 haveError = true;
356 if (haveError) {
357 if (aPlaceOrigin) {
358 ReportChildCountError();
360 return ReflowError(aRenderingContext, aDesiredSize);
362 GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
363 if (underFrame) {
364 GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder);
366 if (overFrame) {
367 GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver);
370 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
372 ////////////////////
373 // Place Children
375 nsRefPtr<nsFontMetrics> fm;
376 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
377 aRenderingContext.SetFont(fm);
379 nscoord xHeight = fm->XHeight();
380 nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
381 gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
383 nscoord ruleThickness;
384 GetRuleThickness (aRenderingContext, fm, ruleThickness);
386 nscoord correction = 0;
387 GetItalicCorrection (bmBase, correction);
389 // there are 2 different types of placement depending on
390 // whether we want an accented under or not
392 nscoord underDelta1 = 0; // gap between base and underscript
393 nscoord underDelta2 = 0; // extra space beneath underscript
395 if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {
396 // Rule 13a, App. G, TeXbook
397 nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy;
398 GetBigOpSpacings (fm,
399 dummy, bigOpSpacing2,
400 dummy, bigOpSpacing4,
401 bigOpSpacing5);
402 if (mathFont) {
403 // XXXfredw The Open Type MATH table has some StretchStack* parameters
404 // that we may use when the base is a stretchy horizontal operator. See
405 // bug 963131.
406 bigOpSpacing2 =
407 mathFont->GetMathConstant(gfxFontEntry::LowerLimitGapMin,
408 oneDevPixel);
409 bigOpSpacing4 =
410 mathFont->GetMathConstant(gfxFontEntry::LowerLimitBaselineDropMin,
411 oneDevPixel);
412 bigOpSpacing5 = 0;
414 underDelta1 = std::max(bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent));
415 underDelta2 = bigOpSpacing5;
417 else {
418 // No corresponding rule in TeXbook - we are on our own here
419 // XXX tune the gap delta between base and underscript
420 // XXX Should we use Rule 10 like \underline does?
421 // XXXfredw Perhaps use the Underbar* parameters of the MATH table. See
422 // bug 963125.
423 underDelta1 = ruleThickness + onePixel/2;
424 underDelta2 = ruleThickness;
426 // empty under?
427 if (!(bmUnder.ascent + bmUnder.descent)) {
428 underDelta1 = 0;
429 underDelta2 = 0;
432 nscoord overDelta1 = 0; // gap between base and overscript
433 nscoord overDelta2 = 0; // extra space above overscript
435 if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {
436 // Rule 13a, App. G, TeXbook
437 // XXXfredw The Open Type MATH table has some StretchStack* parameters
438 // that we may use when the base is a stretchy horizontal operator. See
439 // bug 963131.
440 nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy;
441 GetBigOpSpacings (fm,
442 bigOpSpacing1, dummy,
443 bigOpSpacing3, dummy,
444 bigOpSpacing5);
445 if (mathFont) {
446 // XXXfredw The Open Type MATH table has some StretchStack* parameters
447 // that we may use when the base is a stretchy horizontal operator. See
448 // bug 963131.
449 bigOpSpacing1 =
450 mathFont->GetMathConstant(gfxFontEntry::UpperLimitGapMin,
451 oneDevPixel);
452 bigOpSpacing3 =
453 mathFont->GetMathConstant(gfxFontEntry::UpperLimitBaselineRiseMin,
454 oneDevPixel);
455 bigOpSpacing5 = 0;
457 overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - bmOver.descent));
458 overDelta2 = bigOpSpacing5;
460 // XXX This is not a TeX rule...
461 // delta1 (as computed abvove) can become really big when bmOver.descent is
462 // negative, e.g., if the content is &OverBar. In such case, we use the height
463 if (bmOver.descent < 0)
464 overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - (bmOver.ascent + bmOver.descent)));
466 else {
467 // Rule 12, App. G, TeXbook
468 // We are going to modify this rule to make it more general.
469 // The idea behind Rule 12 in the TeXBook is to keep the accent
470 // as close to the base as possible, while ensuring that the
471 // distance between the *baseline* of the accent char and
472 // the *baseline* of the base is atleast x-height.
473 // The idea is that for normal use, we would like all the accents
474 // on a line to line up atleast x-height above the baseline
475 // if possible.
476 // When the ascent of the base is >= x-height,
477 // the baseline of the accent char is placed just above the base
478 // (specifically, the baseline of the accent char is placed
479 // above the baseline of the base by the ascent of the base).
480 // For ease of implementation,
481 // this assumes that the font-designer designs accents
482 // in such a way that the bottom of the accent is atleast x-height
483 // above its baseline, otherwise there will be collisions
484 // with the base. Also there should be proper padding between
485 // the bottom of the accent char and its baseline.
486 // The above rule may not be obvious from a first
487 // reading of rule 12 in the TeXBook !!!
488 // The mathml <mover> tag can use accent chars that
489 // do not follow this convention. So we modify TeX's rule
490 // so that TeX's rule gets subsumed for accents that follow
491 // TeX's convention,
492 // while also allowing accents that do not follow the convention :
493 // we try to keep the *bottom* of the accent char atleast x-height
494 // from the baseline of the base char. we also slap on an extra
495 // padding between the accent and base chars.
496 overDelta1 = ruleThickness + onePixel/2;
497 nscoord accentBaseHeight = xHeight;
498 if (mathFont) {
499 accentBaseHeight =
500 mathFont->GetMathConstant(gfxFontEntry::AccentBaseHeight,
501 oneDevPixel);
503 if (bmBase.ascent < accentBaseHeight) {
504 // also ensure at least accentBaseHeight above the baseline of the base
505 overDelta1 += accentBaseHeight - bmBase.ascent;
507 overDelta2 = ruleThickness;
509 // empty over?
510 if (!(bmOver.ascent + bmOver.descent)) {
511 overDelta1 = 0;
512 overDelta2 = 0;
515 nscoord dxBase = 0, dxOver = 0, dxUnder = 0;
516 nsAutoString valueAlign;
517 enum {
518 center,
519 left,
520 right
521 } alignPosition = center;
523 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, valueAlign)) {
524 if (valueAlign.EqualsLiteral("left")) {
525 alignPosition = left;
526 } else if (valueAlign.EqualsLiteral("right")) {
527 alignPosition = right;
531 //////////
532 // pass 1, do what <mover> does: attach the overscript on the base
534 // Ad-hoc - This is to override fonts which have ready-made _accent_
535 // glyphs with negative lbearing and rbearing. We want to position
536 // the overscript ourselves
537 nscoord overWidth = bmOver.width;
538 if (!overWidth && (bmOver.rightBearing - bmOver.leftBearing > 0)) {
539 overWidth = bmOver.rightBearing - bmOver.leftBearing;
540 dxOver = -bmOver.leftBearing;
543 if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {
544 mBoundingMetrics.width = bmBase.width;
545 if (alignPosition == center) {
546 dxOver += correction;
549 else {
550 mBoundingMetrics.width = std::max(bmBase.width, overWidth);
551 if (alignPosition == center) {
552 dxOver += correction/2;
556 if (alignPosition == center) {
557 dxOver += (mBoundingMetrics.width - overWidth)/2;
558 dxBase = (mBoundingMetrics.width - bmBase.width)/2;
559 } else if (alignPosition == right) {
560 dxOver += mBoundingMetrics.width - overWidth;
561 dxBase = mBoundingMetrics.width - bmBase.width;
564 mBoundingMetrics.ascent =
565 bmBase.ascent + overDelta1 + bmOver.ascent + bmOver.descent;
566 mBoundingMetrics.descent = bmBase.descent;
567 mBoundingMetrics.leftBearing =
568 std::min(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing);
569 mBoundingMetrics.rightBearing =
570 std::max(dxBase + bmBase.rightBearing, dxOver + bmOver.rightBearing);
572 //////////
573 // pass 2, do what <munder> does: attach the underscript on the previous
574 // result. We conceptually view the previous result as an "anynomous base"
575 // from where to attach the underscript. Hence if the underscript is empty,
576 // we should end up like <mover>. If the overscript is empty, we should
577 // end up like <munder>.
579 nsBoundingMetrics bmAnonymousBase = mBoundingMetrics;
580 nscoord ascentAnonymousBase =
581 std::max(mBoundingMetrics.ascent + overDelta2,
582 overSize.BlockStartAscent() + bmOver.descent +
583 overDelta1 + bmBase.ascent);
584 ascentAnonymousBase = std::max(ascentAnonymousBase,
585 baseSize.BlockStartAscent());
587 // Width of non-spacing marks is zero so use left and right bearing.
588 nscoord underWidth = bmUnder.width;
589 if (!underWidth) {
590 underWidth = bmUnder.rightBearing - bmUnder.leftBearing;
591 dxUnder = -bmUnder.leftBearing;
594 nscoord maxWidth = std::max(bmAnonymousBase.width, underWidth);
595 if (alignPosition == center &&
596 !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {
597 GetItalicCorrection(bmAnonymousBase, correction);
598 dxUnder += -correction/2;
600 nscoord dxAnonymousBase = 0;
601 if (alignPosition == center) {
602 dxUnder += (maxWidth - underWidth)/2;
603 dxAnonymousBase = (maxWidth - bmAnonymousBase.width)/2;
604 } else if (alignPosition == right) {
605 dxUnder += maxWidth - underWidth;
606 dxAnonymousBase = maxWidth - bmAnonymousBase.width;
609 // adjust the offsets of the real base and overscript since their
610 // final offsets should be relative to us...
611 dxOver += dxAnonymousBase;
612 dxBase += dxAnonymousBase;
614 mBoundingMetrics.width =
615 std::max(dxAnonymousBase + bmAnonymousBase.width, dxUnder + bmUnder.width);
616 // At this point, mBoundingMetrics.ascent = bmAnonymousBase.ascent
617 mBoundingMetrics.descent =
618 bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + bmUnder.descent;
619 mBoundingMetrics.leftBearing =
620 std::min(dxAnonymousBase + bmAnonymousBase.leftBearing, dxUnder + bmUnder.leftBearing);
621 mBoundingMetrics.rightBearing =
622 std::max(dxAnonymousBase + bmAnonymousBase.rightBearing, dxUnder + bmUnder.rightBearing);
624 aDesiredSize.SetBlockStartAscent(ascentAnonymousBase);
625 aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
626 std::max(mBoundingMetrics.descent + underDelta2,
627 bmAnonymousBase.descent + underDelta1 + bmUnder.ascent +
628 underSize.Height() - underSize.BlockStartAscent());
629 aDesiredSize.Height() = std::max(aDesiredSize.Height(),
630 aDesiredSize.BlockStartAscent() +
631 baseSize.Height() - baseSize.BlockStartAscent());
632 aDesiredSize.Width() = mBoundingMetrics.width;
633 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
635 mReference.x = 0;
636 mReference.y = aDesiredSize.BlockStartAscent();
638 if (aPlaceOrigin) {
639 nscoord dy;
640 // place overscript
641 if (overFrame) {
642 dy = aDesiredSize.BlockStartAscent() -
643 mBoundingMetrics.ascent + bmOver.ascent -
644 overSize.BlockStartAscent();
645 FinishReflowChild (overFrame, PresContext(), overSize, nullptr, dxOver, dy, 0);
647 // place base
648 dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
649 FinishReflowChild (baseFrame, PresContext(), baseSize, nullptr, dxBase, dy, 0);
650 // place underscript
651 if (underFrame) {
652 dy = aDesiredSize.BlockStartAscent() +
653 mBoundingMetrics.descent - bmUnder.descent -
654 underSize.BlockStartAscent();
655 FinishReflowChild (underFrame, PresContext(), underSize, nullptr,
656 dxUnder, dy, 0);
659 return NS_OK;