tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsMathMLmunderoverFrame.cpp (30690B)


      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/. */
      6 
      7 #include "nsMathMLmunderoverFrame.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "gfxContext.h"
     12 #include "gfxMathTable.h"
     13 #include "gfxTextRun.h"
     14 #include "mozilla/PresShell.h"
     15 #include "mozilla/StaticPrefs_mathml.h"
     16 #include "mozilla/dom/Document.h"
     17 #include "mozilla/dom/MathMLElement.h"
     18 #include "nsIMathMLFrame.h"
     19 #include "nsLayoutUtils.h"
     20 #include "nsMathMLmmultiscriptsFrame.h"
     21 #include "nsPresContext.h"
     22 
     23 using namespace mozilla;
     24 
     25 //
     26 // <munderover> -- attach an underscript-overscript pair to a base
     27 //                 implementation
     28 // <mover> -- attach an overscript to a base - implementation
     29 // <munder> -- attach an underscript to a base - implementation
     30 //
     31 
     32 nsIFrame* NS_NewMathMLmunderoverFrame(PresShell* aPresShell,
     33                                      ComputedStyle* aStyle) {
     34  return new (aPresShell)
     35      nsMathMLmunderoverFrame(aStyle, aPresShell->GetPresContext());
     36 }
     37 
     38 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmunderoverFrame)
     39 
     40 nsMathMLmunderoverFrame::~nsMathMLmunderoverFrame() = default;
     41 
     42 nsresult nsMathMLmunderoverFrame::AttributeChanged(int32_t aNameSpaceID,
     43                                                   nsAtom* aAttribute,
     44                                                   AttrModType aModType) {
     45  if (aNameSpaceID == kNameSpaceID_None &&
     46      (nsGkAtoms::accent == aAttribute ||
     47       nsGkAtoms::accentunder == aAttribute)) {
     48    // When we have automatic data to update within ourselves, we ask our
     49    // parent to re-layout its children
     50    return ReLayoutChildren(GetParent());
     51  }
     52 
     53  return nsMathMLContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
     54                                                  aModType);
     55 }
     56 
     57 NS_IMETHODIMP
     58 nsMathMLmunderoverFrame::UpdatePresentationData(
     59    MathMLPresentationFlags aFlagsValues,
     60    MathMLPresentationFlags aFlagsToUpdate) {
     61  nsMathMLContainerFrame::UpdatePresentationData(aFlagsValues, aFlagsToUpdate);
     62  // disable the stretch-all flag if we are going to act like a
     63  // subscript-superscript pair
     64  if (mEmbellishData.flags.contains(MathMLEmbellishFlag::MovableLimits) &&
     65      StyleFont()->mMathStyle == StyleMathStyle::Compact) {
     66    mPresentationData.flags -=
     67        MathMLPresentationFlag::StretchAllChildrenHorizontally;
     68  } else {
     69    mPresentationData.flags +=
     70        MathMLPresentationFlag::StretchAllChildrenHorizontally;
     71  }
     72  return NS_OK;
     73 }
     74 
     75 NS_IMETHODIMP
     76 nsMathMLmunderoverFrame::InheritAutomaticData(nsIFrame* aParent) {
     77  // let the base class get the default from our parent
     78  nsMathMLContainerFrame::InheritAutomaticData(aParent);
     79 
     80  mPresentationData.flags +=
     81      MathMLPresentationFlag::StretchAllChildrenHorizontally;
     82 
     83  return NS_OK;
     84 }
     85 
     86 void nsMathMLmunderoverFrame::Destroy(DestroyContext& aContext) {
     87  if (!mPostReflowIncrementScriptLevelCommands.IsEmpty()) {
     88    PresShell()->CancelReflowCallback(this);
     89  }
     90  nsMathMLContainerFrame::Destroy(aContext);
     91 }
     92 
     93 uint8_t nsMathMLmunderoverFrame::ScriptIncrement(nsIFrame* aFrame) {
     94  nsIFrame* child = mFrames.FirstChild();
     95  if (!aFrame || aFrame == child) {
     96    return 0;
     97  }
     98  child = child->GetNextSibling();
     99  if (aFrame == child) {
    100    if (mContent->IsMathMLElement(nsGkAtoms::mover)) {
    101      return mIncrementOver ? 1 : 0;
    102    }
    103    return mIncrementUnder ? 1 : 0;
    104  }
    105  if (child && aFrame == child->GetNextSibling()) {
    106    // must be a over frame of munderover
    107    return mIncrementOver ? 1 : 0;
    108  }
    109  return 0;  // frame not found
    110 }
    111 
    112 void nsMathMLmunderoverFrame::SetIncrementScriptLevel(uint32_t aChildIndex,
    113                                                      bool aIncrement) {
    114  nsIFrame* child = PrincipalChildList().FrameAt(aChildIndex);
    115  if (!child || !child->GetContent()->IsMathMLElement() ||
    116      child->GetContent()->GetPrimaryFrame() != child) {
    117    return;
    118  }
    119 
    120  auto element = dom::MathMLElement::FromNode(child->GetContent());
    121  if (element->GetIncrementScriptLevel() == aIncrement) {
    122    return;
    123  }
    124 
    125  if (mPostReflowIncrementScriptLevelCommands.IsEmpty()) {
    126    PresShell()->PostReflowCallback(this);
    127  }
    128 
    129  mPostReflowIncrementScriptLevelCommands.AppendElement(
    130      SetIncrementScriptLevelCommand{aChildIndex, aIncrement});
    131 }
    132 
    133 bool nsMathMLmunderoverFrame::ReflowFinished() {
    134  SetPendingPostReflowIncrementScriptLevel();
    135  return true;
    136 }
    137 
    138 void nsMathMLmunderoverFrame::ReflowCallbackCanceled() {
    139  // Do nothing, at this point our work will just be useless.
    140  mPostReflowIncrementScriptLevelCommands.Clear();
    141 }
    142 
    143 void nsMathMLmunderoverFrame::SetPendingPostReflowIncrementScriptLevel() {
    144  MOZ_ASSERT(!mPostReflowIncrementScriptLevelCommands.IsEmpty());
    145 
    146  nsTArray<SetIncrementScriptLevelCommand> commands =
    147      std::move(mPostReflowIncrementScriptLevelCommands);
    148 
    149  for (const auto& command : commands) {
    150    nsIFrame* child = PrincipalChildList().FrameAt(command.mChildIndex);
    151    if (!child || !child->GetContent()->IsMathMLElement()) {
    152      continue;
    153    }
    154 
    155    auto element = dom::MathMLElement::FromNode(child->GetContent());
    156    element->SetIncrementScriptLevel(command.mDoIncrement, true);
    157  }
    158 }
    159 
    160 NS_IMETHODIMP
    161 nsMathMLmunderoverFrame::TransmitAutomaticData() {
    162  // At this stage, all our children are in sync and we can fully
    163  // resolve our own mEmbellishData struct
    164  //---------------------------------------------------------------------
    165 
    166  /*
    167  The REC says:
    168 
    169  As regards munder (respectively mover) :
    170  The default value of accentunder is false, unless underscript
    171  is an <mo> element or an embellished operator.  If underscript is
    172  an <mo> element, the value of its accent attribute is used as the
    173  default value of accentunder. If underscript is an embellished
    174  operator, the accent attribute of the <mo> element at its
    175  core is used as the default value. As with all attributes, an
    176  explicitly given value overrides the default.
    177 
    178 XXX The winner is the outermost setting in conflicting settings like these:
    179 <munder accentunder='true'>
    180  <mi>...</mi>
    181  <mo accentunder='false'> ... </mo>
    182 </munder>
    183 
    184  As regards munderover:
    185  The accent and accentunder attributes have the same effect as
    186  the attributes with the same names on <mover>  and <munder>,
    187  respectively. Their default values are also computed in the
    188  same manner as described for those elements, with the default
    189  value of accent depending on overscript and the default value
    190  of accentunder depending on underscript.
    191  */
    192 
    193  nsIFrame* overscriptFrame = nullptr;
    194  nsIFrame* underscriptFrame = nullptr;
    195  nsIFrame* baseFrame = mFrames.FirstChild();
    196 
    197  if (baseFrame) {
    198    if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder,
    199                                        nsGkAtoms::munderover)) {
    200      underscriptFrame = baseFrame->GetNextSibling();
    201    } else {
    202      NS_ASSERTION(mContent->IsMathMLElement(nsGkAtoms::mover),
    203                   "mContent->NodeInfo()->NameAtom() not recognized");
    204      overscriptFrame = baseFrame->GetNextSibling();
    205    }
    206  }
    207  if (underscriptFrame && mContent->IsMathMLElement(nsGkAtoms::munderover)) {
    208    overscriptFrame = underscriptFrame->GetNextSibling();
    209  }
    210 
    211  // if our base is an embellished operator, let its state bubble to us (in
    212  // particular, this is where we get the flag for
    213  // MovableLimits). Our flags are reset to the default
    214  // values of false if the base frame isn't embellished.
    215  mPresentationData.baseFrame = baseFrame;
    216  GetEmbellishDataFrom(baseFrame, mEmbellishData);
    217 
    218  // The default value of accentunder is false, unless the underscript is
    219  // embellished and its core <mo> is an accent
    220  nsEmbellishData embellishData;
    221  nsAutoString value;
    222  if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder,
    223                                      nsGkAtoms::munderover)) {
    224    GetEmbellishDataFrom(underscriptFrame, embellishData);
    225    if (embellishData.flags.contains(MathMLEmbellishFlag::Accent)) {
    226      mEmbellishData.flags += MathMLEmbellishFlag::AccentUnder;
    227    } else {
    228      mEmbellishData.flags -= MathMLEmbellishFlag::AccentUnder;
    229    }
    230 
    231    // if we have an accentunder attribute, it overrides what the underscript
    232    // said
    233    if (mContent->AsElement()->GetAttr(nsGkAtoms::accentunder, value)) {
    234      if (value.LowerCaseEqualsLiteral("true")) {
    235        mEmbellishData.flags += MathMLEmbellishFlag::AccentUnder;
    236      } else if (value.LowerCaseEqualsLiteral("false")) {
    237        mEmbellishData.flags -= MathMLEmbellishFlag::AccentUnder;
    238      }
    239    } else if (mEmbellishData.flags.contains(
    240                   MathMLEmbellishFlag::AccentUnder)) {
    241      AutoTArray<nsString, 1> params;
    242      params.AppendElement(mContent->NodeInfo()->NodeName());
    243      PresContext()->Document()->WarnOnceAbout(
    244          dom::DeprecatedOperations::
    245              eMathML_DeprecatedMunderNonExplicitAccentunder,
    246          false, params);
    247    }
    248  }
    249 
    250  // The default value of accent is false, unless the overscript is embellished
    251  // and its core <mo> is an accent
    252  if (mContent->IsAnyOfMathMLElements(nsGkAtoms::mover,
    253                                      nsGkAtoms::munderover)) {
    254    GetEmbellishDataFrom(overscriptFrame, embellishData);
    255    if (embellishData.flags.contains(MathMLEmbellishFlag::Accent)) {
    256      mEmbellishData.flags += MathMLEmbellishFlag::AccentOver;
    257    } else {
    258      mEmbellishData.flags -= MathMLEmbellishFlag::AccentOver;
    259    }
    260 
    261    // if we have an accent attribute, it overrides what the overscript said
    262    if (mContent->AsElement()->GetAttr(nsGkAtoms::accent, value)) {
    263      if (value.LowerCaseEqualsLiteral("true")) {
    264        mEmbellishData.flags += MathMLEmbellishFlag::AccentOver;
    265      } else if (value.LowerCaseEqualsLiteral("false")) {
    266        mEmbellishData.flags -= MathMLEmbellishFlag::AccentOver;
    267      }
    268    } else if (mEmbellishData.flags.contains(MathMLEmbellishFlag::AccentOver)) {
    269      AutoTArray<nsString, 1> params;
    270      params.AppendElement(mContent->NodeInfo()->NodeName());
    271      PresContext()->Document()->WarnOnceAbout(
    272          dom::DeprecatedOperations::eMathML_DeprecatedMoverNonExplicitAccent,
    273          false, params);
    274    }
    275  }
    276 
    277  bool subsupDisplay =
    278      mEmbellishData.flags.contains(MathMLEmbellishFlag::MovableLimits) &&
    279      StyleFont()->mMathStyle == StyleMathStyle::Compact;
    280 
    281  // disable the stretch-all flag if we are going to act like a superscript
    282  if (subsupDisplay) {
    283    mPresentationData.flags -=
    284        MathMLPresentationFlag::StretchAllChildrenHorizontally;
    285  }
    286 
    287  // Now transmit any change that we want to our children so that they
    288  // can update their mPresentationData structs
    289  //---------------------------------------------------------------------
    290 
    291  /* The REC says:
    292     Within underscript, <munderover> always sets displaystyle to "false",
    293     but increments scriptlevel by 1 only when accentunder is "false".
    294 
    295     Within overscript, <munderover> always sets displaystyle to "false",
    296     but increments scriptlevel by 1 only when accent is "false".
    297 
    298     Within subscript and superscript it increments scriptlevel by 1, and
    299     sets displaystyle to "false", but leaves both attributes unchanged within
    300     base.
    301 
    302     The TeXBook treats 'over' like a superscript, so p.141 or Rule 13a
    303     say it shouldn't be compressed. However, The TeXBook says
    304     that math accents and \overline change uncramped styles to their
    305     cramped counterparts.
    306  */
    307  if (mContent->IsAnyOfMathMLElements(nsGkAtoms::mover,
    308                                      nsGkAtoms::munderover)) {
    309    mIncrementOver =
    310        !mEmbellishData.flags.contains(MathMLEmbellishFlag::AccentOver) ||
    311        subsupDisplay;
    312    SetIncrementScriptLevel(mContent->IsMathMLElement(nsGkAtoms::mover) ? 1 : 2,
    313                            mIncrementOver);
    314    if (mIncrementOver) {
    315      PropagateFrameFlagFor(overscriptFrame, NS_FRAME_MATHML_SCRIPT_DESCENDANT);
    316    }
    317    if (!StaticPrefs::mathml_math_shift_enabled()) {
    318      MathMLPresentationFlags flags;
    319      if (mEmbellishData.flags.contains(MathMLEmbellishFlag::AccentOver)) {
    320        flags += MathMLPresentationFlag::Compressed;
    321      }
    322      PropagatePresentationDataFor(overscriptFrame, flags, flags);
    323    }
    324  }
    325  /*
    326     The TeXBook treats 'under' like a subscript, so p.141 or Rule 13a
    327     say it should be compressed
    328  */
    329  if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder,
    330                                      nsGkAtoms::munderover)) {
    331    mIncrementUnder =
    332        !mEmbellishData.flags.contains(MathMLEmbellishFlag::AccentUnder) ||
    333        subsupDisplay;
    334    SetIncrementScriptLevel(1, mIncrementUnder);
    335    if (mIncrementUnder) {
    336      PropagateFrameFlagFor(underscriptFrame,
    337                            NS_FRAME_MATHML_SCRIPT_DESCENDANT);
    338    }
    339    if (!StaticPrefs::mathml_math_shift_enabled()) {
    340      PropagatePresentationDataFor(underscriptFrame,
    341                                   MathMLPresentationFlag::Compressed,
    342                                   MathMLPresentationFlag::Compressed);
    343    }
    344  }
    345 
    346  /* Set flags for dtls font feature settings.
    347 
    348     dtls
    349     Dotless Forms
    350     This feature provides dotless forms for Math Alphanumeric
    351     characters, such as U+1D422 MATHEMATICAL BOLD SMALL I,
    352     U+1D423 MATHEMATICAL BOLD SMALL J, U+1D456
    353     U+MATHEMATICAL ITALIC SMALL I, U+1D457 MATHEMATICAL ITALIC
    354     SMALL J, and so on.
    355     The dotless forms are to be used as base forms for placing
    356     mathematical accents over them.
    357 
    358     To opt out of this change, add the following to the stylesheet:
    359     "font-feature-settings: 'dtls' 0"
    360   */
    361  if (overscriptFrame &&
    362      mEmbellishData.flags.contains(MathMLEmbellishFlag::AccentOver) &&
    363      !mEmbellishData.flags.contains(MathMLEmbellishFlag::MovableLimits)) {
    364    PropagatePresentationDataFor(baseFrame, MathMLPresentationFlag::Dtls,
    365                                 MathMLPresentationFlag::Dtls);
    366  }
    367 
    368  return NS_OK;
    369 }
    370 
    371 /*
    372 The REC says:
    373 *  If the base is an operator with movablelimits="true" (or an embellished
    374   operator whose <mo> element core has movablelimits="true"), and
    375   displaystyle="false", then underscript and overscript are drawn in
    376   a subscript and superscript position, respectively. In this case,
    377   the accent and accentunder attributes are ignored. This is often
    378   used for limits on symbols such as &sum;.
    379 
    380 i.e.,:
    381 if (mEmbellishDataflags.contains(MathMLEmbellishFlag::MovableLimits) &&
    382     StyleFont()->mMathStyle == StyleMathStyle::Compact) {
    383  // place like subscript-superscript pair
    384 }
    385 else {
    386  // place like underscript-overscript pair
    387 }
    388 */
    389 
    390 /* virtual */
    391 void nsMathMLmunderoverFrame::Place(DrawTarget* aDrawTarget,
    392                                    const PlaceFlags& aFlags,
    393                                    ReflowOutput& aDesiredSize) {
    394  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
    395  if (mEmbellishData.flags.contains(MathMLEmbellishFlag::MovableLimits) &&
    396      StyleFont()->mMathStyle == StyleMathStyle::Compact) {
    397    // place like sub sup or subsup
    398    if (mContent->IsMathMLElement(nsGkAtoms::munderover)) {
    399      return nsMathMLmmultiscriptsFrame::PlaceMultiScript(
    400          PresContext(), aDrawTarget, aFlags, aDesiredSize, this, 0, 0,
    401          fontSizeInflation);
    402    } else if (mContent->IsMathMLElement(nsGkAtoms::munder)) {
    403      return nsMathMLmmultiscriptsFrame::PlaceMultiScript(
    404          PresContext(), aDrawTarget, aFlags, aDesiredSize, this, 0, 0,
    405          fontSizeInflation);
    406    } else {
    407      NS_ASSERTION(mContent->IsMathMLElement(nsGkAtoms::mover),
    408                   "mContent->NodeInfo()->NameAtom() not recognized");
    409      return nsMathMLmmultiscriptsFrame::PlaceMultiScript(
    410          PresContext(), aDrawTarget, aFlags, aDesiredSize, this, 0, 0,
    411          fontSizeInflation);
    412    }
    413  }
    414 
    415  ////////////////////////////////////
    416  // Get the children's desired sizes
    417 
    418  nsBoundingMetrics bmBase, bmUnder, bmOver;
    419  ReflowOutput baseSize(aDesiredSize.GetWritingMode());
    420  ReflowOutput underSize(aDesiredSize.GetWritingMode());
    421  ReflowOutput overSize(aDesiredSize.GetWritingMode());
    422  nsIFrame* overFrame = nullptr;
    423  nsIFrame* underFrame = nullptr;
    424  nsIFrame* baseFrame = mFrames.FirstChild();
    425  underSize.SetBlockStartAscent(0);
    426  overSize.SetBlockStartAscent(0);
    427  bool haveError = false;
    428  if (baseFrame) {
    429    if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder,
    430                                        nsGkAtoms::munderover)) {
    431      underFrame = baseFrame->GetNextSibling();
    432    } else if (mContent->IsMathMLElement(nsGkAtoms::mover)) {
    433      overFrame = baseFrame->GetNextSibling();
    434    }
    435  }
    436  if (underFrame && mContent->IsMathMLElement(nsGkAtoms::munderover)) {
    437    overFrame = underFrame->GetNextSibling();
    438  }
    439 
    440  if (mContent->IsMathMLElement(nsGkAtoms::munder)) {
    441    if (!baseFrame || !underFrame || underFrame->GetNextSibling()) {
    442      // report an error, encourage people to get their markups in order
    443      haveError = true;
    444    }
    445  }
    446  if (mContent->IsMathMLElement(nsGkAtoms::mover)) {
    447    if (!baseFrame || !overFrame || overFrame->GetNextSibling()) {
    448      // report an error, encourage people to get their markups in order
    449      haveError = true;
    450    }
    451  }
    452  if (mContent->IsMathMLElement(nsGkAtoms::munderover)) {
    453    if (!baseFrame || !underFrame || !overFrame ||
    454        overFrame->GetNextSibling()) {
    455      // report an error, encourage people to get their markups in order
    456      haveError = true;
    457    }
    458  }
    459  if (haveError) {
    460    if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
    461      ReportChildCountError();
    462    }
    463    return PlaceAsMrow(aDrawTarget, aFlags, aDesiredSize);
    464  }
    465  GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
    466  nsMargin baseMargin = GetMarginForPlace(aFlags, baseFrame);
    467  nsMargin underMargin, overMargin;
    468  if (underFrame) {
    469    GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder);
    470    underMargin = GetMarginForPlace(aFlags, underFrame);
    471  }
    472  if (overFrame) {
    473    GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver);
    474    overMargin = GetMarginForPlace(aFlags, overFrame);
    475  }
    476 
    477  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
    478 
    479  ////////////////////
    480  // Place Children
    481 
    482  RefPtr<nsFontMetrics> fm =
    483      nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
    484 
    485  nscoord xHeight = fm->XHeight();
    486  nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
    487  RefPtr<gfxFont> mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
    488 
    489  nscoord ruleThickness;
    490  GetRuleThickness(aDrawTarget, fm, ruleThickness);
    491 
    492  nscoord correction = 0;
    493  GetItalicCorrection(bmBase, correction);
    494 
    495  // there are 2 different types of placement depending on
    496  // whether we want an accented under or not
    497 
    498  nscoord underDelta1 = 0;  // gap between base and underscript
    499  nscoord underDelta2 = 0;  // extra space beneath underscript
    500 
    501  if (!mEmbellishData.flags.contains(MathMLEmbellishFlag::AccentUnder)) {
    502    // Rule 13a, App. G, TeXbook
    503    nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy;
    504    GetBigOpSpacings(fm, dummy, bigOpSpacing2, dummy, bigOpSpacing4,
    505                     bigOpSpacing5);
    506    if (mathFont) {
    507      // XXXfredw The Open Type MATH table has some StretchStack* parameters
    508      // that we may use when the base is a stretchy horizontal operator. See
    509      // bug 963131.
    510      bigOpSpacing2 = mathFont->MathTable()->Constant(
    511          gfxMathTable::LowerLimitGapMin, oneDevPixel);
    512      bigOpSpacing4 = mathFont->MathTable()->Constant(
    513          gfxMathTable::LowerLimitBaselineDropMin, oneDevPixel);
    514      bigOpSpacing5 = 0;
    515    }
    516    underDelta1 = std::max(
    517        bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent - underMargin.bottom));
    518    underDelta2 = bigOpSpacing5;
    519  } else {
    520    // No corresponding rule in TeXbook - we are on our own here
    521    // XXX tune the gap delta between base and underscript
    522    // XXX Should we use Rule 10 like \underline does?
    523    // XXXfredw Perhaps use the Underbar* parameters of the MATH table. See
    524    // bug 963125.
    525    underDelta1 = ruleThickness + onePixel / 2;
    526    underDelta2 = ruleThickness;
    527  }
    528  // empty under?
    529  if (bmUnder.ascent + bmUnder.descent + underMargin.TopBottom() <= 0) {
    530    underDelta1 = 0;
    531    underDelta2 = 0;
    532  }
    533 
    534  nscoord overDelta1 = 0;  // gap between base and overscript
    535  nscoord overDelta2 = 0;  // extra space above overscript
    536 
    537  if (!mEmbellishData.flags.contains(MathMLEmbellishFlag::AccentOver)) {
    538    // Rule 13a, App. G, TeXbook
    539    // XXXfredw The Open Type MATH table has some StretchStack* parameters
    540    // that we may use when the base is a stretchy horizontal operator. See
    541    // bug 963131.
    542    nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy;
    543    GetBigOpSpacings(fm, bigOpSpacing1, dummy, bigOpSpacing3, dummy,
    544                     bigOpSpacing5);
    545    if (mathFont) {
    546      // XXXfredw The Open Type MATH table has some StretchStack* parameters
    547      // that we may use when the base is a stretchy horizontal operator. See
    548      // bug 963131.
    549      bigOpSpacing1 = mathFont->MathTable()->Constant(
    550          gfxMathTable::UpperLimitGapMin, oneDevPixel);
    551      bigOpSpacing3 = mathFont->MathTable()->Constant(
    552          gfxMathTable::UpperLimitBaselineRiseMin, oneDevPixel);
    553      bigOpSpacing5 = 0;
    554    }
    555    overDelta1 = std::max(bigOpSpacing1,
    556                          (bigOpSpacing3 - bmOver.descent - overMargin.bottom));
    557    overDelta2 = bigOpSpacing5;
    558 
    559    // XXX This is not a TeX rule...
    560    // delta1 (as computed abvove) can become really big when bmOver.descent is
    561    // negative,  e.g., if the content is &OverBar. In such case, we use the
    562    // height
    563    if (bmOver.descent + overMargin.bottom < 0) {
    564      overDelta1 = std::max(bigOpSpacing1,
    565                            (bigOpSpacing3 - (bmOver.ascent + bmOver.descent +
    566                                              overMargin.TopBottom())));
    567    }
    568  } else {
    569    // Rule 12, App. G, TeXbook
    570    // We are going to modify this rule to make it more general.
    571    // The idea behind Rule 12 in the TeXBook is to keep the accent
    572    // as close to the base as possible, while ensuring that the
    573    // distance between the *baseline* of the accent char and
    574    // the *baseline* of the base is atleast x-height.
    575    // The idea is that for normal use, we would like all the accents
    576    // on a line to line up atleast x-height above the baseline
    577    // if possible.
    578    // When the ascent of the base is >= x-height,
    579    // the baseline of the accent char is placed just above the base
    580    // (specifically, the baseline of the accent char is placed
    581    // above the baseline of the base by the ascent of the base).
    582    // For ease of implementation,
    583    // this assumes that the font-designer designs accents
    584    // in such a way that the bottom of the accent is atleast x-height
    585    // above its baseline, otherwise there will be collisions
    586    // with the base. Also there should be proper padding between
    587    // the bottom of the accent char and its baseline.
    588    // The above rule may not be obvious from a first
    589    // reading of rule 12 in the TeXBook !!!
    590    // The mathml <mover> tag can use accent chars that
    591    // do not follow this convention. So we modify TeX's rule
    592    // so that TeX's rule gets subsumed for accents that follow
    593    // TeX's convention,
    594    // while also allowing accents that do not follow the convention :
    595    // we try to keep the *bottom* of the accent char atleast x-height
    596    // from the baseline of the base char. we also slap on an extra
    597    // padding between the accent and base chars.
    598    overDelta1 = ruleThickness + onePixel / 2;
    599    nscoord accentBaseHeight = xHeight;
    600    if (mathFont) {
    601      accentBaseHeight = mathFont->MathTable()->Constant(
    602          gfxMathTable::AccentBaseHeight, oneDevPixel);
    603    }
    604    if (bmBase.ascent + baseMargin.top < accentBaseHeight) {
    605      // also ensure at least accentBaseHeight above the baseline of the base
    606      overDelta1 += accentBaseHeight - bmBase.ascent - baseMargin.top;
    607    }
    608    overDelta2 = ruleThickness;
    609  }
    610  // empty over?
    611  if (bmOver.ascent + bmOver.descent + overMargin.TopBottom() <= 0) {
    612    overDelta1 = 0;
    613    overDelta2 = 0;
    614  }
    615 
    616  nscoord dxBase = 0, dxOver = 0, dxUnder = 0;
    617  nsAutoString valueAlign;
    618 
    619  //////////
    620  // pass 1, do what <mover> does: attach the overscript on the base
    621 
    622  // Ad-hoc - This is to override fonts which have ready-made _accent_
    623  // glyphs with negative lbearing and rbearing. We want to position
    624  // the overscript ourselves
    625  nscoord overWidth = bmOver.width + overMargin.LeftRight();
    626  if (overWidth <= 0 && (bmOver.rightBearing - bmOver.leftBearing > 0)) {
    627    overWidth = bmOver.rightBearing - bmOver.leftBearing;
    628    dxOver = -bmOver.leftBearing;
    629  }
    630 
    631  if (mEmbellishData.flags.contains(MathMLEmbellishFlag::AccentOver)) {
    632    mBoundingMetrics.width = bmBase.width + baseMargin.LeftRight();
    633    dxOver += correction;
    634  } else {
    635    mBoundingMetrics.width =
    636        std::max(bmBase.width + baseMargin.LeftRight(), overWidth);
    637    dxOver += correction / 2;
    638  }
    639 
    640  dxOver += (mBoundingMetrics.width - overWidth) / 2;
    641  dxBase = (mBoundingMetrics.width - bmBase.width - baseMargin.LeftRight()) / 2;
    642 
    643  mBoundingMetrics.ascent = baseMargin.top + bmBase.ascent + overDelta1 +
    644                            bmOver.ascent + bmOver.descent +
    645                            overMargin.TopBottom();
    646  mBoundingMetrics.descent = bmBase.descent + baseMargin.bottom;
    647  mBoundingMetrics.leftBearing =
    648      std::min(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing);
    649  mBoundingMetrics.rightBearing =
    650      std::max(dxBase + bmBase.rightBearing + baseMargin.LeftRight(),
    651               dxOver + bmOver.rightBearing + overMargin.LeftRight());
    652 
    653  //////////
    654  // pass 2, do what <munder> does: attach the underscript on the previous
    655  // result. We conceptually view the previous result as an "anynomous base"
    656  // from where to attach the underscript. Hence if the underscript is empty,
    657  // we should end up like <mover>. If the overscript is empty, we should
    658  // end up like <munder>.
    659 
    660  nsBoundingMetrics bmAnonymousBase = mBoundingMetrics;
    661  nscoord ascentAnonymousBase = std::max(
    662      mBoundingMetrics.ascent + overDelta2,
    663      overMargin.TopBottom() + overSize.BlockStartAscent() + bmOver.descent +
    664          overDelta1 + baseMargin.top + bmBase.ascent);
    665  ascentAnonymousBase = std::max(ascentAnonymousBase,
    666                                 baseSize.BlockStartAscent() + baseMargin.top);
    667 
    668  // Width of non-spacing marks is zero so use left and right bearing.
    669  nscoord underWidth = bmUnder.width + underMargin.LeftRight();
    670  if (underWidth <= 0) {
    671    underWidth =
    672        bmUnder.rightBearing + underMargin.LeftRight() - bmUnder.leftBearing;
    673    dxUnder = -bmUnder.leftBearing;
    674  }
    675 
    676  nscoord maxWidth = std::max(bmAnonymousBase.width, underWidth);
    677  if (!mEmbellishData.flags.contains(MathMLEmbellishFlag::AccentUnder)) {
    678    GetItalicCorrection(bmAnonymousBase, correction);
    679    dxUnder += -correction / 2;
    680  }
    681  nscoord dxAnonymousBase = 0;
    682  dxUnder += (maxWidth - underWidth) / 2;
    683  dxAnonymousBase = (maxWidth - bmAnonymousBase.width) / 2;
    684 
    685  // adjust the offsets of the real base and overscript since their
    686  // final offsets should be relative to us...
    687  dxOver += dxAnonymousBase;
    688  dxBase += dxAnonymousBase;
    689 
    690  mBoundingMetrics.width =
    691      std::max(dxAnonymousBase + bmAnonymousBase.width,
    692               dxUnder + bmUnder.width + underMargin.LeftRight());
    693  // At this point, mBoundingMetrics.ascent = bmAnonymousBase.ascent
    694  mBoundingMetrics.descent = bmAnonymousBase.descent + underDelta1 +
    695                             bmUnder.ascent + bmUnder.descent +
    696                             underMargin.TopBottom();
    697  mBoundingMetrics.leftBearing =
    698      std::min(dxAnonymousBase + bmAnonymousBase.leftBearing,
    699               dxUnder + bmUnder.leftBearing);
    700  mBoundingMetrics.rightBearing =
    701      std::max(dxAnonymousBase + bmAnonymousBase.rightBearing,
    702               dxUnder + bmUnder.rightBearing + underMargin.LeftRight());
    703 
    704  aDesiredSize.SetBlockStartAscent(ascentAnonymousBase);
    705  aDesiredSize.Height() =
    706      aDesiredSize.BlockStartAscent() +
    707      std::max(mBoundingMetrics.descent + underDelta2,
    708               bmAnonymousBase.descent + underDelta1 + underMargin.top +
    709                   bmUnder.ascent + underSize.Height() -
    710                   underSize.BlockStartAscent() + underMargin.bottom);
    711  aDesiredSize.Height() =
    712      std::max(aDesiredSize.Height(),
    713               aDesiredSize.BlockStartAscent() + baseSize.Height() -
    714                   baseSize.BlockStartAscent() + baseMargin.bottom);
    715  aDesiredSize.Width() = mBoundingMetrics.width;
    716  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
    717 
    718  // Apply width/height to math content box.
    719  auto sizes = GetWidthAndHeightForPlaceAdjustment(aFlags);
    720  auto shiftX = ApplyAdjustmentForWidthAndHeight(aFlags, sizes, aDesiredSize,
    721                                                 mBoundingMetrics);
    722  dxOver += shiftX;
    723  dxBase += shiftX;
    724  dxUnder += shiftX;
    725 
    726  // Add padding+border.
    727  auto borderPadding = GetBorderPaddingForPlace(aFlags);
    728  InflateReflowAndBoundingMetrics(borderPadding, aDesiredSize,
    729                                  mBoundingMetrics);
    730  dxOver += borderPadding.left + overMargin.left;
    731  dxBase += borderPadding.left + baseMargin.left;
    732  dxUnder += borderPadding.left + underMargin.left;
    733 
    734  mReference.x = 0;
    735  mReference.y = aDesiredSize.BlockStartAscent();
    736 
    737  if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
    738    nscoord dy;
    739    // place overscript
    740    if (overFrame) {
    741      dy = aDesiredSize.BlockStartAscent() - mBoundingMetrics.ascent +
    742           overMargin.top + bmOver.ascent - overSize.BlockStartAscent();
    743      FinishReflowChild(overFrame, PresContext(), overSize, nullptr, dxOver, dy,
    744                        ReflowChildFlags::Default);
    745    }
    746    // place base
    747    dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
    748    FinishReflowChild(baseFrame, PresContext(), baseSize, nullptr, dxBase, dy,
    749                      ReflowChildFlags::Default);
    750    // place underscript
    751    if (underFrame) {
    752      dy = aDesiredSize.BlockStartAscent() + mBoundingMetrics.descent -
    753           bmUnder.descent - underMargin.bottom - underSize.BlockStartAscent();
    754      FinishReflowChild(underFrame, PresContext(), underSize, nullptr, dxUnder,
    755                        dy, ReflowChildFlags::Default);
    756    }
    757  }
    758 }
    759 
    760 bool nsMathMLmunderoverFrame::IsMathContentBoxHorizontallyCentered() const {
    761  bool subsupDisplay =
    762      mEmbellishData.flags.contains(MathMLEmbellishFlag::MovableLimits) &&
    763      StyleFont()->mMathStyle == StyleMathStyle::Compact;
    764  return !subsupDisplay;
    765 }