Bug 1869043 allow a device to be specified with MediaTrackGraph::NotifyWhenDeviceStar...
[gecko.git] / layout / mathml / nsMathMLmunderoverFrame.cpp
blob05bf1d2886792f1fb14f52624a652100d586ea8f
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 "nsMathMLmunderoverFrame.h"
8 #include "nsLayoutUtils.h"
9 #include "nsPresContext.h"
10 #include "nsMathMLmmultiscriptsFrame.h"
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/dom/MathMLElement.h"
13 #include <algorithm>
14 #include "gfxContext.h"
15 #include "gfxMathTable.h"
16 #include "gfxTextRun.h"
17 #include "mozilla/PresShell.h"
18 #include "mozilla/StaticPrefs_mathml.h"
20 using namespace mozilla;
23 // <munderover> -- attach an underscript-overscript pair to a base
24 // implementation
25 // <mover> -- attach an overscript to a base - implementation
26 // <munder> -- attach an underscript to a base - implementation
29 nsIFrame* NS_NewMathMLmunderoverFrame(PresShell* aPresShell,
30 ComputedStyle* aStyle) {
31 return new (aPresShell)
32 nsMathMLmunderoverFrame(aStyle, aPresShell->GetPresContext());
35 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmunderoverFrame)
37 nsMathMLmunderoverFrame::~nsMathMLmunderoverFrame() = default;
39 nsresult nsMathMLmunderoverFrame::AttributeChanged(int32_t aNameSpaceID,
40 nsAtom* aAttribute,
41 int32_t aModType) {
42 if (nsGkAtoms::accent_ == aAttribute ||
43 nsGkAtoms::accentunder_ == aAttribute) {
44 // When we have automatic data to update within ourselves, we ask our
45 // parent to re-layout its children
46 return ReLayoutChildren(GetParent());
49 return nsMathMLContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
50 aModType);
53 NS_IMETHODIMP
54 nsMathMLmunderoverFrame::UpdatePresentationData(uint32_t aFlagsValues,
55 uint32_t aFlagsToUpdate) {
56 nsMathMLContainerFrame::UpdatePresentationData(aFlagsValues, aFlagsToUpdate);
57 // disable the stretch-all flag if we are going to act like a
58 // subscript-superscript pair
59 if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
60 StyleFont()->mMathStyle == StyleMathStyle::Compact) {
61 mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
62 } else {
63 mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
65 return NS_OK;
68 NS_IMETHODIMP
69 nsMathMLmunderoverFrame::InheritAutomaticData(nsIFrame* aParent) {
70 // let the base class get the default from our parent
71 nsMathMLContainerFrame::InheritAutomaticData(aParent);
73 mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
75 return NS_OK;
78 void nsMathMLmunderoverFrame::Destroy(DestroyContext& aContext) {
79 if (!mPostReflowIncrementScriptLevelCommands.IsEmpty()) {
80 PresShell()->CancelReflowCallback(this);
82 nsMathMLContainerFrame::Destroy(aContext);
85 uint8_t nsMathMLmunderoverFrame::ScriptIncrement(nsIFrame* aFrame) {
86 nsIFrame* child = mFrames.FirstChild();
87 if (!aFrame || aFrame == child) {
88 return 0;
90 child = child->GetNextSibling();
91 if (aFrame == child) {
92 if (mContent->IsMathMLElement(nsGkAtoms::mover_)) {
93 return mIncrementOver ? 1 : 0;
95 return mIncrementUnder ? 1 : 0;
97 if (child && aFrame == child->GetNextSibling()) {
98 // must be a over frame of munderover
99 return mIncrementOver ? 1 : 0;
101 return 0; // frame not found
104 void nsMathMLmunderoverFrame::SetIncrementScriptLevel(uint32_t aChildIndex,
105 bool aIncrement) {
106 nsIFrame* child = PrincipalChildList().FrameAt(aChildIndex);
107 if (!child || !child->GetContent()->IsMathMLElement() ||
108 child->GetContent()->GetPrimaryFrame() != child) {
109 return;
112 auto element = dom::MathMLElement::FromNode(child->GetContent());
113 if (element->GetIncrementScriptLevel() == aIncrement) {
114 return;
117 if (mPostReflowIncrementScriptLevelCommands.IsEmpty()) {
118 PresShell()->PostReflowCallback(this);
121 mPostReflowIncrementScriptLevelCommands.AppendElement(
122 SetIncrementScriptLevelCommand{aChildIndex, aIncrement});
125 bool nsMathMLmunderoverFrame::ReflowFinished() {
126 SetPendingPostReflowIncrementScriptLevel();
127 return true;
130 void nsMathMLmunderoverFrame::ReflowCallbackCanceled() {
131 // Do nothing, at this point our work will just be useless.
132 mPostReflowIncrementScriptLevelCommands.Clear();
135 void nsMathMLmunderoverFrame::SetPendingPostReflowIncrementScriptLevel() {
136 MOZ_ASSERT(!mPostReflowIncrementScriptLevelCommands.IsEmpty());
138 nsTArray<SetIncrementScriptLevelCommand> commands =
139 std::move(mPostReflowIncrementScriptLevelCommands);
141 for (const auto& command : commands) {
142 nsIFrame* child = PrincipalChildList().FrameAt(command.mChildIndex);
143 if (!child || !child->GetContent()->IsMathMLElement()) {
144 continue;
147 auto element = dom::MathMLElement::FromNode(child->GetContent());
148 element->SetIncrementScriptLevel(command.mDoIncrement, true);
152 NS_IMETHODIMP
153 nsMathMLmunderoverFrame::TransmitAutomaticData() {
154 // At this stage, all our children are in sync and we can fully
155 // resolve our own mEmbellishData struct
156 //---------------------------------------------------------------------
159 The REC says:
161 As regards munder (respectively mover) :
162 The default value of accentunder is false, unless underscript
163 is an <mo> element or an embellished operator. If underscript is
164 an <mo> element, the value of its accent attribute is used as the
165 default value of accentunder. If underscript is an embellished
166 operator, the accent attribute of the <mo> element at its
167 core is used as the default value. As with all attributes, an
168 explicitly given value overrides the default.
170 XXX The winner is the outermost setting in conflicting settings like these:
171 <munder accentunder='true'>
172 <mi>...</mi>
173 <mo accentunder='false'> ... </mo>
174 </munder>
176 As regards munderover:
177 The accent and accentunder attributes have the same effect as
178 the attributes with the same names on <mover> and <munder>,
179 respectively. Their default values are also computed in the
180 same manner as described for those elements, with the default
181 value of accent depending on overscript and the default value
182 of accentunder depending on underscript.
185 nsIFrame* overscriptFrame = nullptr;
186 nsIFrame* underscriptFrame = nullptr;
187 nsIFrame* baseFrame = mFrames.FirstChild();
189 if (baseFrame) {
190 if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_,
191 nsGkAtoms::munderover_)) {
192 underscriptFrame = baseFrame->GetNextSibling();
193 } else {
194 NS_ASSERTION(mContent->IsMathMLElement(nsGkAtoms::mover_),
195 "mContent->NodeInfo()->NameAtom() not recognized");
196 overscriptFrame = baseFrame->GetNextSibling();
199 if (underscriptFrame && mContent->IsMathMLElement(nsGkAtoms::munderover_)) {
200 overscriptFrame = underscriptFrame->GetNextSibling();
203 // if our base is an embellished operator, let its state bubble to us (in
204 // particular, this is where we get the flag for
205 // NS_MATHML_EMBELLISH_MOVABLELIMITS). Our flags are reset to the default
206 // values of false if the base frame isn't embellished.
207 mPresentationData.baseFrame = baseFrame;
208 GetEmbellishDataFrom(baseFrame, mEmbellishData);
210 // The default value of accentunder is false, unless the underscript is
211 // embellished and its core <mo> is an accent
212 nsEmbellishData embellishData;
213 nsAutoString value;
214 if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_,
215 nsGkAtoms::munderover_)) {
216 GetEmbellishDataFrom(underscriptFrame, embellishData);
217 if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) {
218 mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER;
219 } else {
220 mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER;
223 // if we have an accentunder attribute, it overrides what the underscript
224 // said
225 if (mContent->AsElement()->GetAttr(nsGkAtoms::accentunder_, value)) {
226 if (value.EqualsLiteral("true")) {
227 mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER;
228 } else if (value.EqualsLiteral("false")) {
229 mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER;
234 // The default value of accent is false, unless the overscript is embellished
235 // and its core <mo> is an accent
236 if (mContent->IsAnyOfMathMLElements(nsGkAtoms::mover_,
237 nsGkAtoms::munderover_)) {
238 GetEmbellishDataFrom(overscriptFrame, embellishData);
239 if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) {
240 mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER;
241 } else {
242 mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER;
245 // if we have an accent attribute, it overrides what the overscript said
246 if (mContent->AsElement()->GetAttr(nsGkAtoms::accent_, value)) {
247 if (value.EqualsLiteral("true")) {
248 mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER;
249 } else if (value.EqualsLiteral("false")) {
250 mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER;
255 bool subsupDisplay =
256 NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
257 StyleFont()->mMathStyle == StyleMathStyle::Compact;
259 // disable the stretch-all flag if we are going to act like a superscript
260 if (subsupDisplay) {
261 mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
264 // Now transmit any change that we want to our children so that they
265 // can update their mPresentationData structs
266 //---------------------------------------------------------------------
268 /* The REC says:
269 Within underscript, <munderover> always sets displaystyle to "false",
270 but increments scriptlevel by 1 only when accentunder is "false".
272 Within overscript, <munderover> always sets displaystyle to "false",
273 but increments scriptlevel by 1 only when accent is "false".
275 Within subscript and superscript it increments scriptlevel by 1, and
276 sets displaystyle to "false", but leaves both attributes unchanged within
277 base.
279 The TeXBook treats 'over' like a superscript, so p.141 or Rule 13a
280 say it shouldn't be compressed. However, The TeXBook says
281 that math accents and \overline change uncramped styles to their
282 cramped counterparts.
284 if (mContent->IsAnyOfMathMLElements(nsGkAtoms::mover_,
285 nsGkAtoms::munderover_)) {
286 uint32_t compress = NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)
287 ? NS_MATHML_COMPRESSED
288 : 0;
289 mIncrementOver = !NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) ||
290 subsupDisplay;
291 SetIncrementScriptLevel(
292 mContent->IsMathMLElement(nsGkAtoms::mover_) ? 1 : 2, mIncrementOver);
293 if (mIncrementOver) {
294 PropagateFrameFlagFor(overscriptFrame, NS_FRAME_MATHML_SCRIPT_DESCENDANT);
296 PropagatePresentationDataFor(overscriptFrame, compress, compress);
299 The TeXBook treats 'under' like a subscript, so p.141 or Rule 13a
300 say it should be compressed
302 if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_,
303 nsGkAtoms::munderover_)) {
304 mIncrementUnder =
305 !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags) ||
306 subsupDisplay;
307 SetIncrementScriptLevel(1, mIncrementUnder);
308 if (mIncrementUnder) {
309 PropagateFrameFlagFor(underscriptFrame,
310 NS_FRAME_MATHML_SCRIPT_DESCENDANT);
312 PropagatePresentationDataFor(underscriptFrame, NS_MATHML_COMPRESSED,
313 NS_MATHML_COMPRESSED);
316 /* Set flags for dtls font feature settings.
318 dtls
319 Dotless Forms
320 This feature provides dotless forms for Math Alphanumeric
321 characters, such as U+1D422 MATHEMATICAL BOLD SMALL I,
322 U+1D423 MATHEMATICAL BOLD SMALL J, U+1D456
323 U+MATHEMATICAL ITALIC SMALL I, U+1D457 MATHEMATICAL ITALIC
324 SMALL J, and so on.
325 The dotless forms are to be used as base forms for placing
326 mathematical accents over them.
328 To opt out of this change, add the following to the stylesheet:
329 "font-feature-settings: 'dtls' 0"
331 if (overscriptFrame &&
332 NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) &&
333 !NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags)) {
334 PropagatePresentationDataFor(baseFrame, NS_MATHML_DTLS, NS_MATHML_DTLS);
337 return NS_OK;
341 The REC says:
342 * If the base is an operator with movablelimits="true" (or an embellished
343 operator whose <mo> element core has movablelimits="true"), and
344 displaystyle="false", then underscript and overscript are drawn in
345 a subscript and superscript position, respectively. In this case,
346 the accent and accentunder attributes are ignored. This is often
347 used for limits on symbols such as &sum;.
349 i.e.,:
350 if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishDataflags) &&
351 StyleFont()->mMathStyle == StyleMathStyle::Compact) {
352 // place like subscript-superscript pair
354 else {
355 // place like underscript-overscript pair
359 /* virtual */
360 nsresult nsMathMLmunderoverFrame::Place(DrawTarget* aDrawTarget,
361 bool aPlaceOrigin,
362 ReflowOutput& aDesiredSize) {
363 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
364 if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
365 StyleFont()->mMathStyle == StyleMathStyle::Compact) {
366 // place like sub sup or subsup
367 if (mContent->IsMathMLElement(nsGkAtoms::munderover_)) {
368 return nsMathMLmmultiscriptsFrame::PlaceMultiScript(
369 PresContext(), aDrawTarget, aPlaceOrigin, aDesiredSize, this, 0, 0,
370 fontSizeInflation);
371 } else if (mContent->IsMathMLElement(nsGkAtoms::munder_)) {
372 return nsMathMLmmultiscriptsFrame::PlaceMultiScript(
373 PresContext(), aDrawTarget, aPlaceOrigin, aDesiredSize, this, 0, 0,
374 fontSizeInflation);
375 } else {
376 NS_ASSERTION(mContent->IsMathMLElement(nsGkAtoms::mover_),
377 "mContent->NodeInfo()->NameAtom() not recognized");
378 return nsMathMLmmultiscriptsFrame::PlaceMultiScript(
379 PresContext(), aDrawTarget, aPlaceOrigin, aDesiredSize, this, 0, 0,
380 fontSizeInflation);
384 ////////////////////////////////////
385 // Get the children's desired sizes
387 nsBoundingMetrics bmBase, bmUnder, bmOver;
388 ReflowOutput baseSize(aDesiredSize.GetWritingMode());
389 ReflowOutput underSize(aDesiredSize.GetWritingMode());
390 ReflowOutput overSize(aDesiredSize.GetWritingMode());
391 nsIFrame* overFrame = nullptr;
392 nsIFrame* underFrame = nullptr;
393 nsIFrame* baseFrame = mFrames.FirstChild();
394 underSize.SetBlockStartAscent(0);
395 overSize.SetBlockStartAscent(0);
396 bool haveError = false;
397 if (baseFrame) {
398 if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_,
399 nsGkAtoms::munderover_)) {
400 underFrame = baseFrame->GetNextSibling();
401 } else if (mContent->IsMathMLElement(nsGkAtoms::mover_)) {
402 overFrame = baseFrame->GetNextSibling();
405 if (underFrame && mContent->IsMathMLElement(nsGkAtoms::munderover_)) {
406 overFrame = underFrame->GetNextSibling();
409 if (mContent->IsMathMLElement(nsGkAtoms::munder_)) {
410 if (!baseFrame || !underFrame || underFrame->GetNextSibling()) {
411 // report an error, encourage people to get their markups in order
412 haveError = true;
415 if (mContent->IsMathMLElement(nsGkAtoms::mover_)) {
416 if (!baseFrame || !overFrame || overFrame->GetNextSibling()) {
417 // report an error, encourage people to get their markups in order
418 haveError = true;
421 if (mContent->IsMathMLElement(nsGkAtoms::munderover_)) {
422 if (!baseFrame || !underFrame || !overFrame ||
423 overFrame->GetNextSibling()) {
424 // report an error, encourage people to get their markups in order
425 haveError = true;
428 if (haveError) {
429 if (aPlaceOrigin) {
430 ReportChildCountError();
432 return PlaceAsMrow(aDrawTarget, aPlaceOrigin, aDesiredSize);
434 GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
435 if (underFrame) {
436 GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder);
438 if (overFrame) {
439 GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver);
442 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
444 ////////////////////
445 // Place Children
447 RefPtr<nsFontMetrics> fm =
448 nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
450 nscoord xHeight = fm->XHeight();
451 nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
452 RefPtr<gfxFont> mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
454 nscoord ruleThickness;
455 GetRuleThickness(aDrawTarget, fm, ruleThickness);
457 nscoord correction = 0;
458 GetItalicCorrection(bmBase, correction);
460 // there are 2 different types of placement depending on
461 // whether we want an accented under or not
463 nscoord underDelta1 = 0; // gap between base and underscript
464 nscoord underDelta2 = 0; // extra space beneath underscript
466 if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {
467 // Rule 13a, App. G, TeXbook
468 nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy;
469 GetBigOpSpacings(fm, dummy, bigOpSpacing2, dummy, bigOpSpacing4,
470 bigOpSpacing5);
471 if (mathFont) {
472 // XXXfredw The Open Type MATH table has some StretchStack* parameters
473 // that we may use when the base is a stretchy horizontal operator. See
474 // bug 963131.
475 bigOpSpacing2 = mathFont->MathTable()->Constant(
476 gfxMathTable::LowerLimitGapMin, oneDevPixel);
477 bigOpSpacing4 = mathFont->MathTable()->Constant(
478 gfxMathTable::LowerLimitBaselineDropMin, oneDevPixel);
479 bigOpSpacing5 = 0;
481 underDelta1 = std::max(bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent));
482 underDelta2 = bigOpSpacing5;
483 } else {
484 // No corresponding rule in TeXbook - we are on our own here
485 // XXX tune the gap delta between base and underscript
486 // XXX Should we use Rule 10 like \underline does?
487 // XXXfredw Perhaps use the Underbar* parameters of the MATH table. See
488 // bug 963125.
489 underDelta1 = ruleThickness + onePixel / 2;
490 underDelta2 = ruleThickness;
492 // empty under?
493 if (!(bmUnder.ascent + bmUnder.descent)) {
494 underDelta1 = 0;
495 underDelta2 = 0;
498 nscoord overDelta1 = 0; // gap between base and overscript
499 nscoord overDelta2 = 0; // extra space above overscript
501 if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {
502 // Rule 13a, App. G, TeXbook
503 // XXXfredw The Open Type MATH table has some StretchStack* parameters
504 // that we may use when the base is a stretchy horizontal operator. See
505 // bug 963131.
506 nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy;
507 GetBigOpSpacings(fm, bigOpSpacing1, dummy, bigOpSpacing3, dummy,
508 bigOpSpacing5);
509 if (mathFont) {
510 // XXXfredw The Open Type MATH table has some StretchStack* parameters
511 // that we may use when the base is a stretchy horizontal operator. See
512 // bug 963131.
513 bigOpSpacing1 = mathFont->MathTable()->Constant(
514 gfxMathTable::UpperLimitGapMin, oneDevPixel);
515 bigOpSpacing3 = mathFont->MathTable()->Constant(
516 gfxMathTable::UpperLimitBaselineRiseMin, oneDevPixel);
517 bigOpSpacing5 = 0;
519 overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - bmOver.descent));
520 overDelta2 = bigOpSpacing5;
522 // XXX This is not a TeX rule...
523 // delta1 (as computed abvove) can become really big when bmOver.descent is
524 // negative, e.g., if the content is &OverBar. In such case, we use the
525 // height
526 if (bmOver.descent < 0)
527 overDelta1 = std::max(bigOpSpacing1,
528 (bigOpSpacing3 - (bmOver.ascent + bmOver.descent)));
529 } else {
530 // Rule 12, App. G, TeXbook
531 // We are going to modify this rule to make it more general.
532 // The idea behind Rule 12 in the TeXBook is to keep the accent
533 // as close to the base as possible, while ensuring that the
534 // distance between the *baseline* of the accent char and
535 // the *baseline* of the base is atleast x-height.
536 // The idea is that for normal use, we would like all the accents
537 // on a line to line up atleast x-height above the baseline
538 // if possible.
539 // When the ascent of the base is >= x-height,
540 // the baseline of the accent char is placed just above the base
541 // (specifically, the baseline of the accent char is placed
542 // above the baseline of the base by the ascent of the base).
543 // For ease of implementation,
544 // this assumes that the font-designer designs accents
545 // in such a way that the bottom of the accent is atleast x-height
546 // above its baseline, otherwise there will be collisions
547 // with the base. Also there should be proper padding between
548 // the bottom of the accent char and its baseline.
549 // The above rule may not be obvious from a first
550 // reading of rule 12 in the TeXBook !!!
551 // The mathml <mover> tag can use accent chars that
552 // do not follow this convention. So we modify TeX's rule
553 // so that TeX's rule gets subsumed for accents that follow
554 // TeX's convention,
555 // while also allowing accents that do not follow the convention :
556 // we try to keep the *bottom* of the accent char atleast x-height
557 // from the baseline of the base char. we also slap on an extra
558 // padding between the accent and base chars.
559 overDelta1 = ruleThickness + onePixel / 2;
560 nscoord accentBaseHeight = xHeight;
561 if (mathFont) {
562 accentBaseHeight = mathFont->MathTable()->Constant(
563 gfxMathTable::AccentBaseHeight, oneDevPixel);
565 if (bmBase.ascent < accentBaseHeight) {
566 // also ensure at least accentBaseHeight above the baseline of the base
567 overDelta1 += accentBaseHeight - bmBase.ascent;
569 overDelta2 = ruleThickness;
571 // empty over?
572 if (!(bmOver.ascent + bmOver.descent)) {
573 overDelta1 = 0;
574 overDelta2 = 0;
577 nscoord dxBase = 0, dxOver = 0, dxUnder = 0;
578 nsAutoString valueAlign;
580 //////////
581 // pass 1, do what <mover> does: attach the overscript on the base
583 // Ad-hoc - This is to override fonts which have ready-made _accent_
584 // glyphs with negative lbearing and rbearing. We want to position
585 // the overscript ourselves
586 nscoord overWidth = bmOver.width;
587 if (!overWidth && (bmOver.rightBearing - bmOver.leftBearing > 0)) {
588 overWidth = bmOver.rightBearing - bmOver.leftBearing;
589 dxOver = -bmOver.leftBearing;
592 if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {
593 mBoundingMetrics.width = bmBase.width;
594 dxOver += correction;
595 } else {
596 mBoundingMetrics.width = std::max(bmBase.width, overWidth);
597 dxOver += correction / 2;
600 dxOver += (mBoundingMetrics.width - overWidth) / 2;
601 dxBase = (mBoundingMetrics.width - bmBase.width) / 2;
603 mBoundingMetrics.ascent =
604 bmBase.ascent + overDelta1 + bmOver.ascent + bmOver.descent;
605 mBoundingMetrics.descent = bmBase.descent;
606 mBoundingMetrics.leftBearing =
607 std::min(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing);
608 mBoundingMetrics.rightBearing =
609 std::max(dxBase + bmBase.rightBearing, dxOver + bmOver.rightBearing);
611 //////////
612 // pass 2, do what <munder> does: attach the underscript on the previous
613 // result. We conceptually view the previous result as an "anynomous base"
614 // from where to attach the underscript. Hence if the underscript is empty,
615 // we should end up like <mover>. If the overscript is empty, we should
616 // end up like <munder>.
618 nsBoundingMetrics bmAnonymousBase = mBoundingMetrics;
619 nscoord ascentAnonymousBase =
620 std::max(mBoundingMetrics.ascent + overDelta2,
621 overSize.BlockStartAscent() + bmOver.descent + overDelta1 +
622 bmBase.ascent);
623 ascentAnonymousBase =
624 std::max(ascentAnonymousBase, baseSize.BlockStartAscent());
626 // Width of non-spacing marks is zero so use left and right bearing.
627 nscoord underWidth = bmUnder.width;
628 if (!underWidth) {
629 underWidth = bmUnder.rightBearing - bmUnder.leftBearing;
630 dxUnder = -bmUnder.leftBearing;
633 nscoord maxWidth = std::max(bmAnonymousBase.width, underWidth);
634 if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {
635 GetItalicCorrection(bmAnonymousBase, correction);
636 dxUnder += -correction / 2;
638 nscoord dxAnonymousBase = 0;
639 dxUnder += (maxWidth - underWidth) / 2;
640 dxAnonymousBase = (maxWidth - bmAnonymousBase.width) / 2;
642 // adjust the offsets of the real base and overscript since their
643 // final offsets should be relative to us...
644 dxOver += dxAnonymousBase;
645 dxBase += dxAnonymousBase;
647 mBoundingMetrics.width = std::max(dxAnonymousBase + bmAnonymousBase.width,
648 dxUnder + bmUnder.width);
649 // At this point, mBoundingMetrics.ascent = bmAnonymousBase.ascent
650 mBoundingMetrics.descent =
651 bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + bmUnder.descent;
652 mBoundingMetrics.leftBearing =
653 std::min(dxAnonymousBase + bmAnonymousBase.leftBearing,
654 dxUnder + bmUnder.leftBearing);
655 mBoundingMetrics.rightBearing =
656 std::max(dxAnonymousBase + bmAnonymousBase.rightBearing,
657 dxUnder + bmUnder.rightBearing);
659 aDesiredSize.SetBlockStartAscent(ascentAnonymousBase);
660 aDesiredSize.Height() =
661 aDesiredSize.BlockStartAscent() +
662 std::max(mBoundingMetrics.descent + underDelta2,
663 bmAnonymousBase.descent + underDelta1 + bmUnder.ascent +
664 underSize.Height() - underSize.BlockStartAscent());
665 aDesiredSize.Height() =
666 std::max(aDesiredSize.Height(), aDesiredSize.BlockStartAscent() +
667 baseSize.Height() -
668 baseSize.BlockStartAscent());
669 aDesiredSize.Width() = mBoundingMetrics.width;
670 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
672 mReference.x = 0;
673 mReference.y = aDesiredSize.BlockStartAscent();
675 if (aPlaceOrigin) {
676 nscoord dy;
677 // place overscript
678 if (overFrame) {
679 dy = aDesiredSize.BlockStartAscent() - mBoundingMetrics.ascent +
680 bmOver.ascent - overSize.BlockStartAscent();
681 FinishReflowChild(overFrame, PresContext(), overSize, nullptr, dxOver, dy,
682 ReflowChildFlags::Default);
684 // place base
685 dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
686 FinishReflowChild(baseFrame, PresContext(), baseSize, nullptr, dxBase, dy,
687 ReflowChildFlags::Default);
688 // place underscript
689 if (underFrame) {
690 dy = aDesiredSize.BlockStartAscent() + mBoundingMetrics.descent -
691 bmUnder.descent - underSize.BlockStartAscent();
692 FinishReflowChild(underFrame, PresContext(), underSize, nullptr, dxUnder,
693 dy, ReflowChildFlags::Default);
696 return NS_OK;