tor-browser

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

nsMathMLmoFrame.cpp (44133B)


      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 "nsMathMLmoFrame.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "gfxContext.h"
     12 #include "mozilla/PresShell.h"
     13 #include "mozilla/StaticPrefs_mathml.h"
     14 #include "mozilla/dom/Document.h"
     15 #include "mozilla/dom/MathMLElement.h"
     16 #include "nsCSSValue.h"
     17 #include "nsContentUtils.h"
     18 #include "nsFrameSelection.h"
     19 #include "nsGkAtoms.h"
     20 #include "nsLayoutUtils.h"
     21 #include "nsPresContext.h"
     22 
     23 using namespace mozilla;
     24 
     25 //
     26 // <mo> -- operator, fence, or separator - implementation
     27 //
     28 
     29 nsIFrame* NS_NewMathMLmoFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
     30  return new (aPresShell) nsMathMLmoFrame(aStyle, aPresShell->GetPresContext());
     31 }
     32 
     33 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmoFrame)
     34 
     35 nsMathMLmoFrame::~nsMathMLmoFrame() = default;
     36 
     37 static const char16_t kApplyFunction = char16_t(0x2061);
     38 static const char16_t kInvisibleTimes = char16_t(0x2062);
     39 static const char16_t kInvisibleSeparator = char16_t(0x2063);
     40 static const char16_t kInvisiblePlus = char16_t(0x2064);
     41 
     42 MathMLFrameType nsMathMLmoFrame::GetMathMLFrameType() {
     43  return NS_MATHML_OPERATOR_IS_INVISIBLE(mFlags)
     44             ? MathMLFrameType::OperatorInvisible
     45             : MathMLFrameType::OperatorOrdinary;
     46 }
     47 
     48 // since a mouse click implies selection, we cannot just rely on the
     49 // frame's state bit in our child text frame. So we will first check
     50 // its selected state bit, and use this little helper to double check.
     51 bool nsMathMLmoFrame::IsFrameInSelection(nsIFrame* aFrame) {
     52  NS_ASSERTION(aFrame, "null arg");
     53  if (!aFrame || !aFrame->IsSelected()) {
     54    return false;
     55  }
     56 
     57  const nsFrameSelection* frameSelection = aFrame->GetConstFrameSelection();
     58  UniquePtr<SelectionDetails> details = frameSelection->LookUpSelection(
     59      aFrame->GetContent(), 0, 1,
     60      aFrame->ShouldPaintNormalSelection()
     61          ? nsFrameSelection::IgnoreNormalSelection::No
     62          : nsFrameSelection::IgnoreNormalSelection::Yes);
     63 
     64  return details != nullptr;
     65 }
     66 
     67 bool nsMathMLmoFrame::UseMathMLChar() {
     68  return (NS_MATHML_OPERATOR_GET_FORM(mFlags) &&
     69          NS_MATHML_OPERATOR_IS_MUTABLE(mFlags)) ||
     70         NS_MATHML_OPERATOR_FORCES_MATHML_CHAR(mFlags);
     71 }
     72 
     73 void nsMathMLmoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
     74                                       const nsDisplayListSet& aLists) {
     75  bool useMathMLChar = UseMathMLChar();
     76 
     77  if (!useMathMLChar) {
     78    // let the base class do everything
     79    nsMathMLTokenFrame::BuildDisplayList(aBuilder, aLists);
     80  } else {
     81    DisplayBorderBackgroundOutline(aBuilder, aLists);
     82 
     83    // make our char selected if our inner child text frame is selected
     84    bool isSelected = false;
     85    nsRect selectedRect;
     86    nsIFrame* firstChild = mFrames.FirstChild();
     87    if (IsFrameInSelection(firstChild)) {
     88      mMathMLChar.GetRect(selectedRect);
     89      // add a one pixel border (it renders better for operators like minus)
     90      selectedRect.Inflate(nsPresContext::CSSPixelsToAppUnits(1));
     91      isSelected = true;
     92    }
     93    mMathMLChar.Display(aBuilder, this, aLists, 0,
     94                        isSelected ? &selectedRect : nullptr);
     95  }
     96 }
     97 
     98 // get the text that we enclose and setup our nsMathMLChar
     99 void nsMathMLmoFrame::ProcessTextData() {
    100  mFlags = 0;
    101 
    102  nsAutoString data;
    103  nsContentUtils::GetNodeTextContent(mContent, false, data);
    104 
    105  data.CompressWhitespace();
    106  int32_t length = data.Length();
    107  char16_t ch = (length == 0) ? char16_t('\0') : data[0];
    108 
    109  if ((length == 1) && (ch == kApplyFunction || ch == kInvisibleSeparator ||
    110                        ch == kInvisiblePlus || ch == kInvisibleTimes)) {
    111    mFlags |= NS_MATHML_OPERATOR_INVISIBLE;
    112  }
    113 
    114  // don't bother doing anything special if we don't have a single child
    115  if (mFrames.GetLength() != 1) {
    116    data.Truncate();  // empty data to reset the char
    117    mMathMLChar.SetData(data);
    118    mMathMLChar.SetComputedStyle(Style());
    119    return;
    120  }
    121 
    122  // special... in math mode, the usual minus sign '-' looks too short, so
    123  // what we do here is to remap <mo>-</mo> to the official Unicode minus
    124  // sign (U+2212) which looks much better. For background on this, see
    125  // http://groups.google.com/groups?hl=en&th=66488daf1ade7635&rnum=1
    126  if (1 == length && ch == '-') {
    127    ch = 0x2212;
    128    data = ch;
    129    mFlags |= NS_MATHML_OPERATOR_FORCE_MATHML_CHAR;
    130  }
    131 
    132  // cache the special bits: mutable, accent, movablelimits.
    133  // we need to do this in anticipation of other requirements, and these
    134  // bits don't change. Do not reset these bits unless the text gets changed.
    135 
    136  // lookup all the forms under which the operator is listed in the dictionary,
    137  // and record whether the operator has accent="true" or movablelimits="true"
    138  nsOperatorFlags allFlags = 0;
    139  for (const auto& form :
    140       {NS_MATHML_OPERATOR_FORM_INFIX, NS_MATHML_OPERATOR_FORM_POSTFIX,
    141        NS_MATHML_OPERATOR_FORM_PREFIX}) {
    142    nsOperatorFlags flags = 0;
    143    float dummy;
    144    if (nsMathMLOperators::LookupOperator(data, form, &flags, &dummy, &dummy)) {
    145      allFlags |= flags;
    146    }
    147  }
    148 
    149  mFlags |= allFlags & NS_MATHML_OPERATOR_ACCENT;
    150  mFlags |= allFlags & NS_MATHML_OPERATOR_MOVABLELIMITS;
    151 
    152  // cache the operator
    153  mMathMLChar.SetData(data);
    154 
    155  // cache the native direction -- beware of bug 133429...
    156  // mEmbellishData.direction must always retain our native direction, whereas
    157  // mMathMLChar.GetStretchDirection() may change later, when Stretch() is
    158  // called
    159  mEmbellishData.direction = mMathMLChar.GetStretchDirection();
    160 
    161  bool isMutable =
    162      NS_MATHML_OPERATOR_IS_LARGEOP(allFlags) ||
    163      (mEmbellishData.direction != NS_STRETCH_DIRECTION_UNSUPPORTED);
    164  if (isMutable) {
    165    mFlags |= NS_MATHML_OPERATOR_MUTABLE;
    166  }
    167 
    168  mMathMLChar.SetComputedStyle(Style());
    169 }
    170 
    171 // get our 'form' and lookup in the Operator Dictionary to fetch
    172 // our default data that may come from there. Then complete our setup
    173 // using attributes that we may have. To stay in sync, this function is
    174 // called very often. We depend on many things that may change around us.
    175 // However, we re-use unchanged values.
    176 void nsMathMLmoFrame::ProcessOperatorData() {
    177  // if we have been here before, we will just use our cached form
    178  uint8_t form = NS_MATHML_OPERATOR_GET_FORM(mFlags);
    179  nsAutoString value;
    180  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
    181 
    182  // special bits are always kept in mFlags.
    183  // remember the mutable bit from ProcessTextData().
    184  // Some chars are listed under different forms in the dictionary,
    185  // and there could be a form under which the char is mutable.
    186  // If the char is the core of an embellished container, we will keep
    187  // it mutable irrespective of the form of the embellished container.
    188  // Also remember the other special bits that we want to carry forward.
    189  mFlags &= NS_MATHML_OPERATOR_MUTABLE | NS_MATHML_OPERATOR_ACCENT |
    190            NS_MATHML_OPERATOR_MOVABLELIMITS | NS_MATHML_OPERATOR_INVISIBLE |
    191            NS_MATHML_OPERATOR_FORCE_MATHML_CHAR;
    192 
    193  if (!mEmbellishData.coreFrame) {
    194    // i.e., we haven't been here before, the default form is infix
    195    form = NS_MATHML_OPERATOR_FORM_INFIX;
    196 
    197    // reset everything so that we don't keep outdated values around
    198    // in case of dynamic changes
    199    mEmbellishData.flags.clear();
    200    mEmbellishData.coreFrame = nullptr;
    201    mEmbellishData.leadingSpace = 0;
    202    mEmbellishData.trailingSpace = 0;
    203    if (mMathMLChar.Length() != 1) {
    204      mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
    205    }
    206    // else... retain the native direction obtained in ProcessTextData()
    207 
    208    if (!mFrames.FirstChild()) {
    209      return;
    210    }
    211 
    212    mEmbellishData.flags += MathMLEmbellishFlag::EmbellishedOperator;
    213    mEmbellishData.coreFrame = this;
    214 
    215    // there are two particular things that we also need to record so that if
    216    // our parent is <mover>, <munder>, or <munderover>, they will treat us
    217    // properly: 1) do we have accent="true" 2) do we have movablelimits="true"
    218 
    219    // they need the extra information to decide how to treat their
    220    // scripts/limits (note: <mover>, <munder>, or <munderover> need not
    221    // necessarily be our direct parent -- case of embellished operators)
    222 
    223    // default values from the Operator Dictionary were obtained in
    224    // ProcessTextData() and these special bits are always kept in mFlags
    225    if (NS_MATHML_OPERATOR_IS_ACCENT(mFlags)) {
    226      mEmbellishData.flags += MathMLEmbellishFlag::Accent;
    227    }
    228    if (NS_MATHML_OPERATOR_IS_MOVABLELIMITS(mFlags)) {
    229      mEmbellishData.flags += MathMLEmbellishFlag::MovableLimits;
    230    }
    231 
    232    // see if the accent attribute is there
    233    if (mContent->AsElement()->GetAttr(nsGkAtoms::accent, value)) {
    234      [&]() {
    235        AutoTArray<nsString, 2> params;
    236        auto parentName = GetParent()->GetContent()->NodeInfo()->NameAtom();
    237        if (parentName == nsGkAtoms::mover) {
    238          params.AppendElement(u"accent");
    239          params.AppendElement(u"mover");
    240        } else if (parentName == nsGkAtoms::munder) {
    241          params.AppendElement(u"accentunder");
    242          params.AppendElement(u"munder");
    243        } else if (parentName == nsGkAtoms::munderover) {
    244          params.AppendElement(u"accent/accentunder");
    245          params.AppendElement(u"munderover");
    246        } else {
    247          return;
    248        }
    249        PresContext()->Document()->WarnOnceAbout(
    250            dom::DeprecatedOperations::eMathML_DeprecatedMoExplicitAccent,
    251            false, params);
    252      }();
    253      if (value.LowerCaseEqualsLiteral("true")) {
    254        mEmbellishData.flags += MathMLEmbellishFlag::Accent;
    255      } else if (value.LowerCaseEqualsLiteral("false")) {
    256        mEmbellishData.flags -= MathMLEmbellishFlag::Accent;
    257      }
    258    }
    259 
    260    // see if the movablelimits attribute is there
    261    mContent->AsElement()->GetAttr(nsGkAtoms::movablelimits, value);
    262    if (value.LowerCaseEqualsLiteral("true")) {
    263      mEmbellishData.flags += MathMLEmbellishFlag::MovableLimits;
    264    } else if (value.LowerCaseEqualsLiteral("false")) {
    265      mEmbellishData.flags -= MathMLEmbellishFlag::MovableLimits;
    266    }
    267 
    268    // ---------------------------------------------------------------------
    269    // we will be called again to re-sync the rest of our state next time...
    270    // (nobody needs the other values below at this stage)
    271    mFlags |= form;
    272    return;
    273  }
    274 
    275  // beware of bug 133814 - there is a two-way dependency in the
    276  // embellished hierarchy: our embellished ancestors need to set
    277  // their flags based on some of our state (set above), and here we
    278  // need to re-sync our 'form' depending on our outermost embellished
    279  // container. A null form here means that an earlier attempt to stretch
    280  // our mMathMLChar failed, in which case we don't bother re-stretching again
    281  if (form) {
    282    // get our outermost embellished container and its parent.
    283    // (we ensure that we are the core, not just a sibling of the core)
    284    nsIFrame* embellishAncestor = this;
    285    nsEmbellishData embellishData;
    286    nsIFrame* parentAncestor = this;
    287    do {
    288      embellishAncestor = parentAncestor;
    289      parentAncestor = embellishAncestor->GetParent();
    290      GetEmbellishDataFrom(parentAncestor, embellishData);
    291    } while (embellishData.coreFrame == this);
    292 
    293    // flag if we have an embellished ancestor
    294    if (embellishAncestor != this) {
    295      mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
    296    } else {
    297      mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
    298    }
    299 
    300    // find the position of our outermost embellished container w.r.t
    301    // its siblings.
    302 
    303    nsIFrame* nextSibling = embellishAncestor->GetNextSibling();
    304    nsIFrame* prevSibling = embellishAncestor->GetPrevSibling();
    305 
    306    // flag to distinguish from a real infix.  Set for (embellished) operators
    307    // that live in (inferred) mrows.
    308    nsIMathMLFrame* mathAncestor = do_QueryFrame(parentAncestor);
    309    bool zeroSpacing = false;
    310    if (mathAncestor) {
    311      zeroSpacing = !mathAncestor->IsMrowLike();
    312    } else {
    313      nsMathMLmathBlockFrame* blockFrame = do_QueryFrame(parentAncestor);
    314      if (blockFrame) {
    315        zeroSpacing = !blockFrame->IsMrowLike();
    316      }
    317    }
    318    if (zeroSpacing) {
    319      mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
    320    } else {
    321      mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
    322    }
    323 
    324    // find our form
    325    form = NS_MATHML_OPERATOR_FORM_INFIX;
    326    mContent->AsElement()->GetAttr(nsGkAtoms::form, value);
    327    if (!value.IsEmpty()) {
    328      if (value.EqualsLiteral("prefix")) {
    329        form = NS_MATHML_OPERATOR_FORM_PREFIX;
    330      } else if (value.EqualsLiteral("postfix")) {
    331        form = NS_MATHML_OPERATOR_FORM_POSTFIX;
    332      }
    333    } else {
    334      // set our form flag depending on the position
    335      if (!prevSibling && nextSibling) {
    336        form = NS_MATHML_OPERATOR_FORM_PREFIX;
    337      } else if (prevSibling && !nextSibling) {
    338        form = NS_MATHML_OPERATOR_FORM_POSTFIX;
    339      }
    340    }
    341    mFlags &= ~NS_MATHML_OPERATOR_FORM;  // clear the old form bits
    342    mFlags |= form;
    343 
    344    // Use the default value suggested by the MathML REC.
    345    // http://www.w3.org/TR/MathML/chapter3.html#presm.mo.attrs
    346    // thickmathspace = 5/18em
    347    float lspace = 5.0f / 18.0f;
    348    float rspace = 5.0f / 18.0f;
    349    // lookup the operator dictionary
    350    nsAutoString data;
    351    mMathMLChar.GetData(data);
    352    nsOperatorFlags flags = 0;
    353    if (nsMathMLOperators::LookupOperatorWithFallback(data, form, &flags,
    354                                                      &lspace, &rspace)) {
    355      mFlags &= ~NS_MATHML_OPERATOR_FORM;  // clear the form bits
    356      mFlags |= flags;                     // just add bits without overwriting
    357    }
    358 
    359    // Spacing is zero if our outermost embellished operator is not in an
    360    // inferred mrow.
    361    if (!NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags) &&
    362        (lspace || rspace)) {
    363      // Cache the default values of lspace and rspace.
    364      // since these values are relative to the 'em' unit, convert to twips now
    365      nscoord em;
    366      RefPtr<nsFontMetrics> fm =
    367          nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
    368      GetEmHeight(fm, em);
    369 
    370      mEmbellishData.leadingSpace = NSToCoordRound(lspace * em);
    371      mEmbellishData.trailingSpace = NSToCoordRound(rspace * em);
    372 
    373      // tuning if we don't want too much extra space when we are a script.
    374      // (with its fonts, TeX sets lspace=0 & rspace=0 as soon as scriptlevel>0.
    375      // Our fonts can be anything, so...)
    376      if (StyleFont()->mMathDepth > 0 &&
    377          !NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
    378        mEmbellishData.leadingSpace /= 2;
    379        mEmbellishData.trailingSpace /= 2;
    380      }
    381    }
    382  }
    383 
    384  // If we are an accent without explicit lspace="." or rspace=".",
    385  // we will ignore our default leading/trailing space
    386 
    387  // lspace
    388  //
    389  // "Specifies the leading space appearing before the operator"
    390  //
    391  // values: length
    392  // default: set by dictionary (thickmathspace)
    393  //
    394  // XXXfredw Support for negative and relative values is not implemented
    395  // (bug 805926).
    396  // Relative values will give a multiple of the current leading space,
    397  // which is not necessarily the default one.
    398  //
    399  nscoord leadingSpace = mEmbellishData.leadingSpace;
    400  mContent->AsElement()->GetAttr(nsGkAtoms::lspace, value);
    401  if (!value.IsEmpty()) {
    402    nsCSSValue cssValue;
    403    if (dom::MathMLElement::ParseNumericValue(value, cssValue, 0,
    404                                              mContent->OwnerDoc())) {
    405      if ((eCSSUnit_Number == cssValue.GetUnit()) &&
    406          !cssValue.GetFloatValue()) {
    407        leadingSpace = 0;
    408      } else if (cssValue.IsLengthUnit()) {
    409        leadingSpace = CalcLength(cssValue, fontSizeInflation, this);
    410      }
    411      mFlags |= NS_MATHML_OPERATOR_LSPACE_ATTR;
    412    }
    413  }
    414 
    415  // rspace
    416  //
    417  // "Specifies the trailing space appearing after the operator"
    418  //
    419  // values: length
    420  // default: set by dictionary (thickmathspace)
    421  //
    422  // XXXfredw Support for negative and relative values is not implemented
    423  // (bug 805926).
    424  // Relative values will give a multiple of the current leading space,
    425  // which is not necessarily the default one.
    426  //
    427  nscoord trailingSpace = mEmbellishData.trailingSpace;
    428  mContent->AsElement()->GetAttr(nsGkAtoms::rspace, value);
    429  if (!value.IsEmpty()) {
    430    nsCSSValue cssValue;
    431    if (dom::MathMLElement::ParseNumericValue(value, cssValue, 0,
    432                                              mContent->OwnerDoc())) {
    433      if ((eCSSUnit_Number == cssValue.GetUnit()) &&
    434          !cssValue.GetFloatValue()) {
    435        trailingSpace = 0;
    436      } else if (cssValue.IsLengthUnit()) {
    437        trailingSpace = CalcLength(cssValue, fontSizeInflation, this);
    438      }
    439      mFlags |= NS_MATHML_OPERATOR_RSPACE_ATTR;
    440    }
    441  }
    442 
    443  // little extra tuning to round lspace & rspace to at least a pixel so that
    444  // operators don't look as if they are colliding with their operands
    445  if (leadingSpace || trailingSpace) {
    446    nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
    447    if (leadingSpace && leadingSpace < onePixel) {
    448      leadingSpace = onePixel;
    449    }
    450    if (trailingSpace && trailingSpace < onePixel) {
    451      trailingSpace = onePixel;
    452    }
    453  }
    454 
    455  // the values that we get from our attributes override the dictionary
    456  mEmbellishData.leadingSpace = leadingSpace;
    457  mEmbellishData.trailingSpace = trailingSpace;
    458 
    459  // Now see if there are user-defined attributes that override the dictionary.
    460  // XXX Bug 1197771 - forcing an attribute to true when it is false in the
    461  // dictionary can cause conflicts in the rest of the stretching algorithms
    462  // (e.g. all largeops are assumed to have a vertical direction)
    463 
    464  // For each attribute overriden by the user, turn off its bit flag.
    465  // symmetric|movablelimits|separator|largeop|accent|fence|stretchy|form
    466  // special: accent and movablelimits are handled above,
    467  // don't process them here
    468 
    469  mContent->AsElement()->GetAttr(nsGkAtoms::stretchy, value);
    470  if (value.LowerCaseEqualsLiteral("false")) {
    471    mFlags &= ~NS_MATHML_OPERATOR_STRETCHY;
    472  } else if (value.LowerCaseEqualsLiteral("true")) {
    473    mFlags |= NS_MATHML_OPERATOR_STRETCHY;
    474  }
    475  if (NS_MATHML_OPERATOR_IS_FENCE(mFlags)) {
    476    mContent->AsElement()->GetAttr(nsGkAtoms::fence, value);
    477    if (value.LowerCaseEqualsLiteral("false")) {
    478      mFlags &= ~NS_MATHML_OPERATOR_FENCE;
    479    } else {
    480      mEmbellishData.flags += MathMLEmbellishFlag::Fence;
    481    }
    482  }
    483  mContent->AsElement()->GetAttr(nsGkAtoms::largeop, value);
    484  if (value.LowerCaseEqualsLiteral("false")) {
    485    mFlags &= ~NS_MATHML_OPERATOR_LARGEOP;
    486  } else if (value.LowerCaseEqualsLiteral("true")) {
    487    mFlags |= NS_MATHML_OPERATOR_LARGEOP;
    488  }
    489  if (NS_MATHML_OPERATOR_IS_SEPARATOR(mFlags)) {
    490    mContent->AsElement()->GetAttr(nsGkAtoms::separator, value);
    491    if (value.LowerCaseEqualsLiteral("false")) {
    492      mFlags &= ~NS_MATHML_OPERATOR_SEPARATOR;
    493    } else {
    494      mEmbellishData.flags += MathMLEmbellishFlag::Separator;
    495    }
    496  }
    497  mContent->AsElement()->GetAttr(nsGkAtoms::symmetric, value);
    498  if (value.LowerCaseEqualsLiteral("false")) {
    499    mFlags &= ~NS_MATHML_OPERATOR_SYMMETRIC;
    500  } else if (value.LowerCaseEqualsLiteral("true")) {
    501    mFlags |= NS_MATHML_OPERATOR_SYMMETRIC;
    502  }
    503 
    504  // minsize
    505  //
    506  // "Specifies the minimum size of the operator when stretchy"
    507  //
    508  // values: length
    509  // default: set by dictionary (1em)
    510  //
    511  // We don't allow negative values.
    512  // Note: Contrary to other "length" values, unitless and percentage do not
    513  // give a multiple of the defaut value but a multiple of the operator at
    514  // normal size.
    515  //
    516  mMinSize = 0;
    517  mContent->AsElement()->GetAttr(nsGkAtoms::minsize, value);
    518  if (!value.IsEmpty()) {
    519    nsCSSValue cssValue;
    520    if (dom::MathMLElement::ParseNumericValue(value, cssValue, 0,
    521                                              mContent->OwnerDoc())) {
    522      nsCSSUnit unit = cssValue.GetUnit();
    523      if (eCSSUnit_Number == unit) {
    524        mMinSize = cssValue.GetFloatValue();
    525      } else if (eCSSUnit_Percent == unit) {
    526        mMinSize = cssValue.GetPercentValue();
    527      } else if (eCSSUnit_Null != unit) {
    528        mMinSize = float(CalcLength(cssValue, fontSizeInflation, this));
    529        mFlags |= NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE;
    530      }
    531    }
    532  }
    533 
    534  // maxsize
    535  //
    536  // "Specifies the maximum size of the operator when stretchy"
    537  //
    538  // values: length | "infinity"
    539  // default: set by dictionary (infinity)
    540  //
    541  // We don't allow negative values.
    542  // Note: Contrary to other "length" values, unitless and percentage do not
    543  // give a multiple of the defaut value but a multiple of the operator at
    544  // normal size.
    545  //
    546  mMaxSize = NS_MATHML_OPERATOR_SIZE_INFINITY;
    547  mContent->AsElement()->GetAttr(nsGkAtoms::maxsize, value);
    548  if (!value.IsEmpty()) {
    549    nsCSSValue cssValue;
    550    if (dom::MathMLElement::ParseNumericValue(value, cssValue, 0,
    551                                              mContent->OwnerDoc())) {
    552      nsCSSUnit unit = cssValue.GetUnit();
    553      if (eCSSUnit_Number == unit) {
    554        mMaxSize = cssValue.GetFloatValue();
    555      } else if (eCSSUnit_Percent == unit) {
    556        mMaxSize = cssValue.GetPercentValue();
    557      } else if (eCSSUnit_Null != unit) {
    558        mMaxSize = float(CalcLength(cssValue, fontSizeInflation, this));
    559        mFlags |= NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE;
    560      }
    561    }
    562  }
    563 }
    564 
    565 static uint32_t GetStretchHint(nsOperatorFlags aFlags,
    566                               nsPresentationData aPresentationData,
    567                               bool aIsVertical,
    568                               const nsStyleFont* aStyleFont) {
    569  uint32_t stretchHint = NS_STRETCH_NONE;
    570  // See if it is okay to stretch,
    571  // starting from what the Operator Dictionary said
    572  if (NS_MATHML_OPERATOR_IS_MUTABLE(aFlags)) {
    573    // set the largeop or largeopOnly flags to suitably cover all the
    574    // 8 possible cases depending on whether displaystyle, largeop,
    575    // stretchy are true or false (see bug 69325).
    576    // . largeopOnly is taken if largeop=true and stretchy=false
    577    // . largeop is taken if largeop=true and stretchy=true
    578    if (aStyleFont->mMathStyle == StyleMathStyle::Normal &&
    579        NS_MATHML_OPERATOR_IS_LARGEOP(aFlags)) {
    580      stretchHint = NS_STRETCH_LARGEOP;  // (largeopOnly, not mask!)
    581      if (NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
    582        stretchHint |= NS_STRETCH_NEARER | NS_STRETCH_LARGER;
    583      }
    584    } else if (NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
    585      if (aIsVertical) {
    586        // TeX hint. Can impact some sloppy markups missing <mrow></mrow>
    587        stretchHint = NS_STRETCH_NEARER;
    588      } else {
    589        stretchHint = NS_STRETCH_NORMAL;
    590      }
    591    }
    592    // else if the stretchy and largeop attributes have been disabled,
    593    // the operator is not mutable
    594  }
    595  return stretchHint;
    596 }
    597 
    598 // NOTE: aDesiredStretchSize is an IN/OUT parameter
    599 //       On input  - it contains our current size
    600 //       On output - the same size or the new size that we want
    601 NS_IMETHODIMP
    602 nsMathMLmoFrame::Stretch(DrawTarget* aDrawTarget,
    603                         nsStretchDirection aStretchDirection,
    604                         nsBoundingMetrics& aContainerSize,
    605                         ReflowOutput& aDesiredStretchSize) {
    606  if (mPresentationData.flags.contains(MathMLPresentationFlag::StretchDone)) {
    607    NS_WARNING("it is wrong to fire stretch more than once on a frame");
    608    return NS_OK;
    609  }
    610  mPresentationData.flags += MathMLPresentationFlag::StretchDone;
    611 
    612  nsIFrame* firstChild = mFrames.FirstChild();
    613 
    614  // get the axis height;
    615  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
    616  RefPtr<nsFontMetrics> fm =
    617      nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
    618  nscoord axisHeight, height;
    619  GetAxisHeight(aDrawTarget, fm, axisHeight);
    620 
    621  // Operators that are stretchy are handled by the MathMLChar
    622  // ('form' is reset if stretch fails -- i.e., we don't bother to stretch next
    623  // time)
    624  bool useMathMLChar = UseMathMLChar();
    625 
    626  nsBoundingMetrics charSize;
    627  nsBoundingMetrics container = aDesiredStretchSize.mBoundingMetrics;
    628  bool isVertical = false;
    629 
    630  if (((aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) ||
    631       (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT)) &&
    632      (mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL)) {
    633    isVertical = true;
    634  }
    635 
    636  uint32_t stretchHint =
    637      GetStretchHint(mFlags, mPresentationData, isVertical, StyleFont());
    638 
    639  if (useMathMLChar) {
    640    nsBoundingMetrics initialSize = aDesiredStretchSize.mBoundingMetrics;
    641 
    642    if (stretchHint != NS_STRETCH_NONE) {
    643      container = aContainerSize;
    644 
    645      // some adjustments if the operator is symmetric and vertical
    646 
    647      if (isVertical && NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
    648        // we need to center about the axis
    649        nscoord delta = std::max(container.ascent - axisHeight,
    650                                 container.descent + axisHeight);
    651        container.ascent = delta + axisHeight;
    652        container.descent = delta - axisHeight;
    653 
    654        // get ready in case we encounter user-desired min-max size
    655        delta = std::max(initialSize.ascent - axisHeight,
    656                         initialSize.descent + axisHeight);
    657        initialSize.ascent = delta + axisHeight;
    658        initialSize.descent = delta - axisHeight;
    659      }
    660 
    661      // check for user-desired min-max size
    662 
    663      if (mMaxSize != NS_MATHML_OPERATOR_SIZE_INFINITY && mMaxSize > 0.0f) {
    664        // if we are here, there is a user defined maxsize ...
    665        // XXX Set stretchHint = NS_STRETCH_NORMAL? to honor the maxsize as
    666        // close as possible?
    667        if (NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags)) {
    668          // there is an explicit value like maxsize="20pt"
    669          // try to maintain the aspect ratio of the char
    670          float aspect =
    671              mMaxSize / float(initialSize.ascent + initialSize.descent);
    672          container.ascent =
    673              std::min(container.ascent, nscoord(initialSize.ascent * aspect));
    674          container.descent = std::min(container.descent,
    675                                       nscoord(initialSize.descent * aspect));
    676          // below we use a type cast instead of a conversion to avoid a VC++
    677          // bug see
    678          // http://support.microsoft.com/support/kb/articles/Q115/7/05.ASP
    679          container.width = std::min(container.width, (nscoord)mMaxSize);
    680        } else {  // multiplicative value
    681          container.ascent = std::min(container.ascent,
    682                                      nscoord(initialSize.ascent * mMaxSize));
    683          container.descent = std::min(container.descent,
    684                                       nscoord(initialSize.descent * mMaxSize));
    685          container.width =
    686              std::min(container.width, nscoord(initialSize.width * mMaxSize));
    687        }
    688 
    689        if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
    690          // re-adjust to align the char with the bottom of the initial
    691          // container
    692          height = container.ascent + container.descent;
    693          container.descent = aContainerSize.descent;
    694          container.ascent = height - container.descent;
    695        }
    696      }
    697 
    698      if (mMinSize > 0.0f) {
    699        // if we are here, there is a user defined minsize ...
    700        // always allow the char to stretch in its natural direction,
    701        // even if it is different from the caller's direction
    702        if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT &&
    703            aStretchDirection != mEmbellishData.direction) {
    704          aStretchDirection = NS_STRETCH_DIRECTION_DEFAULT;
    705          // but when we are not honoring the requested direction
    706          // we should not use the caller's container size either
    707          container = initialSize;
    708        }
    709        if (NS_MATHML_OPERATOR_MINSIZE_IS_ABSOLUTE(mFlags)) {
    710          // there is an explicit value like minsize="20pt"
    711          // try to maintain the aspect ratio of the char
    712          float aspect =
    713              mMinSize / float(initialSize.ascent + initialSize.descent);
    714          container.ascent =
    715              std::max(container.ascent, nscoord(initialSize.ascent * aspect));
    716          container.descent = std::max(container.descent,
    717                                       nscoord(initialSize.descent * aspect));
    718          container.width = std::max(container.width, (nscoord)mMinSize);
    719        } else {  // multiplicative value
    720          container.ascent = std::max(container.ascent,
    721                                      nscoord(initialSize.ascent * mMinSize));
    722          container.descent = std::max(container.descent,
    723                                       nscoord(initialSize.descent * mMinSize));
    724          container.width =
    725              std::max(container.width, nscoord(initialSize.width * mMinSize));
    726        }
    727 
    728        if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
    729          // re-adjust to align the char with the bottom of the initial
    730          // container
    731          height = container.ascent + container.descent;
    732          container.descent = aContainerSize.descent;
    733          container.ascent = height - container.descent;
    734        }
    735      }
    736    }
    737 
    738    // let the MathMLChar stretch itself...
    739    nsresult res = mMathMLChar.Stretch(
    740        this, aDrawTarget, fontSizeInflation, aStretchDirection, container,
    741        charSize, stretchHint,
    742        StyleVisibility()->mDirection == StyleDirection::Rtl);
    743    if (NS_FAILED(res)) {
    744      // gracefully handle cases where stretching the char failed (i.e.,
    745      // GetBoundingMetrics failed) clear our 'form' to behave as if the
    746      // operator wasn't in the dictionary
    747      mFlags &= ~NS_MATHML_OPERATOR_FORM;
    748      useMathMLChar = false;
    749    }
    750  }
    751 
    752  // Place our children using the default method and no border/padding.
    753  // This will allow our child text frame to get its DidReflow()
    754  PlaceFlags flags(PlaceFlag::IgnoreBorderPadding,
    755                   PlaceFlag::DoNotAdjustForWidthAndHeight);
    756  Place(aDrawTarget, flags, aDesiredStretchSize);
    757 
    758  if (useMathMLChar) {
    759    // update our bounding metrics... it becomes that of our MathML char
    760    mBoundingMetrics = charSize;
    761 
    762    // if the returned direction is 'unsupported', the char didn't actually
    763    // change. So we do the centering only if necessary
    764    if (mMathMLChar.GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED) {
    765      bool largeopOnly = (NS_STRETCH_LARGEOP & stretchHint) != 0 &&
    766                         (NS_STRETCH_VARIABLE_MASK & stretchHint) == 0;
    767 
    768      if (isVertical) {
    769        // the desired size returned by mMathMLChar maybe different
    770        // from the size of the container.
    771        // the mMathMLChar.mRect.y calculation is subtle, watch out!!!
    772 
    773        height = mBoundingMetrics.ascent + mBoundingMetrics.descent;
    774        if (NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
    775          // For symmetric and vertical operators, we want to center about the
    776          // axis of the container
    777          mBoundingMetrics.descent = height / 2 - axisHeight;
    778        } else if (!largeopOnly) {
    779          // Align the center of the char with the center of the container
    780          mBoundingMetrics.descent =
    781              height / 2 + (container.ascent + container.descent) / 2 -
    782              container.ascent;
    783        }  // else align the baselines
    784        mBoundingMetrics.ascent = height - mBoundingMetrics.descent;
    785      }
    786    }
    787  }
    788 
    789  // Fixup for the final height.
    790  // On one hand, our stretchy height can sometimes be shorter than surrounding
    791  // ASCII chars, e.g., arrow symbols have |mBoundingMetrics.ascent|
    792  // that is smaller than the ASCII's ascent, hence when painting the background
    793  // later, it won't look uniform along the line.
    794  // On the other hand, sometimes we may leave too much gap when our glyph
    795  // happens to come from a font with tall glyphs. For example, since CMEX10 has
    796  // very tall glyphs, its natural font metrics are large, even if we pick a
    797  // small glyph whose size is comparable to the size of a normal ASCII glyph.
    798  // So to avoid uneven spacing in either of these two cases, we use the height
    799  // of the ASCII font as a reference and try to match it if possible.
    800 
    801  // special case for accents... keep them short to improve mouse operations...
    802  // an accent can only be the non-first child of <mover>, <munder>,
    803  // <munderover>
    804  bool isAccent = mEmbellishData.flags.contains(MathMLEmbellishFlag::Accent);
    805  if (isAccent) {
    806    nsEmbellishData parentData;
    807    GetEmbellishDataFrom(GetParent(), parentData);
    808    isAccent = (parentData.flags.contains(MathMLEmbellishFlag::AccentOver) ||
    809                parentData.flags.contains(MathMLEmbellishFlag::AccentUnder)) &&
    810               parentData.coreFrame != this;
    811  }
    812  if (isAccent && firstChild) {
    813    // see bug 188467 for what is going on here
    814    nscoord dy =
    815        aDesiredStretchSize.BlockStartAscent() - (mBoundingMetrics.ascent);
    816    aDesiredStretchSize.SetBlockStartAscent(mBoundingMetrics.ascent);
    817    aDesiredStretchSize.Height() =
    818        aDesiredStretchSize.BlockStartAscent() + mBoundingMetrics.descent;
    819 
    820    firstChild->SetPosition(firstChild->GetPosition() - nsPoint(0, dy));
    821  } else if (useMathMLChar) {
    822    nscoord ascent = fm->MaxAscent();
    823    nscoord descent = fm->MaxDescent();
    824    aDesiredStretchSize.SetBlockStartAscent(
    825        std::max(mBoundingMetrics.ascent, ascent));
    826    aDesiredStretchSize.Height() = aDesiredStretchSize.BlockStartAscent() +
    827                                   std::max(mBoundingMetrics.descent, descent);
    828  }
    829  aDesiredStretchSize.Width() = mBoundingMetrics.width;
    830  aDesiredStretchSize.mBoundingMetrics = mBoundingMetrics;
    831  mReference.x = 0;
    832  mReference.y = aDesiredStretchSize.BlockStartAscent();
    833  // Place our mMathMLChar, its origin is in our coordinate system
    834  if (useMathMLChar) {
    835    nscoord dy =
    836        aDesiredStretchSize.BlockStartAscent() - mBoundingMetrics.ascent;
    837    mMathMLChar.SetRect(
    838        nsRect(0, dy, charSize.width, charSize.ascent + charSize.descent));
    839  }
    840 
    841  // Before we leave... there is a last item in the check-list:
    842  // If our parent is not embellished, it means we are the outermost embellished
    843  // container and so we put the spacing, otherwise we don't include the
    844  // spacing, the outermost embellished container will take care of it.
    845 
    846  nscoord leadingSpace = 0, trailingSpace = 0;
    847  if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
    848    // Account the spacing if we are not an accent with explicit attributes
    849    if (!isAccent || NS_MATHML_OPERATOR_HAS_LSPACE_ATTR(mFlags)) {
    850      leadingSpace = mEmbellishData.leadingSpace;
    851    }
    852    if (!isAccent || NS_MATHML_OPERATOR_HAS_RSPACE_ATTR(mFlags)) {
    853      trailingSpace = mEmbellishData.trailingSpace;
    854    }
    855  }
    856 
    857  flags = PlaceFlags();
    858  auto sizes = GetWidthAndHeightForPlaceAdjustment(flags);
    859  auto borderPadding = GetBorderPaddingForPlace(flags);
    860  if (leadingSpace || trailingSpace || !borderPadding.IsAllZero() ||
    861      sizes.width || sizes.height) {
    862    mBoundingMetrics.width += leadingSpace + trailingSpace;
    863    aDesiredStretchSize.Width() = mBoundingMetrics.width;
    864    aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
    865 
    866    nscoord dx = StyleVisibility()->mDirection == StyleDirection::Rtl
    867                     ? trailingSpace
    868                     : leadingSpace;
    869    mBoundingMetrics.leftBearing += dx;
    870    mBoundingMetrics.rightBearing += dx;
    871    aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
    872    aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
    873 
    874    // Apply inline/block sizes to math content box.
    875    dx += ApplyAdjustmentForWidthAndHeight(flags, sizes, aDesiredStretchSize,
    876                                           mBoundingMetrics);
    877 
    878    // Add border/padding.
    879    InflateReflowAndBoundingMetrics(borderPadding, aDesiredStretchSize,
    880                                    mBoundingMetrics);
    881    dx += borderPadding.left;
    882    nscoord dy = borderPadding.top;
    883 
    884    if (dx || dy) {
    885      // adjust the offsets
    886 
    887      if (useMathMLChar) {
    888        nsRect rect;
    889        mMathMLChar.GetRect(rect);
    890        mMathMLChar.SetRect(
    891            nsRect(rect.x + dx, rect.y + dy, rect.width, rect.height));
    892      } else {
    893        nsIFrame* childFrame = firstChild;
    894        while (childFrame) {
    895          childFrame->SetPosition(childFrame->GetPosition() + nsPoint(dx, dy));
    896          childFrame = childFrame->GetNextSibling();
    897        }
    898      }
    899    }
    900  }
    901 
    902  // Finished with these:
    903  ClearSavedChildMetrics();
    904  // Set our overflow area
    905  GatherAndStoreOverflow(&aDesiredStretchSize);
    906 
    907  // There used to be code here to change the height of the child frame to
    908  // change the caret height, but the text frame that manages the caret is now
    909  // not a direct child but wrapped in a block frame.  See also bug 412033.
    910 
    911  return NS_OK;
    912 }
    913 
    914 NS_IMETHODIMP
    915 nsMathMLmoFrame::InheritAutomaticData(nsIFrame* aParent) {
    916  // retain our native direction, it only changes if our text content changes
    917  nsStretchDirection direction = mEmbellishData.direction;
    918  nsMathMLTokenFrame::InheritAutomaticData(aParent);
    919  ProcessTextData();
    920  mEmbellishData.direction = direction;
    921  return NS_OK;
    922 }
    923 
    924 NS_IMETHODIMP
    925 nsMathMLmoFrame::TransmitAutomaticData() {
    926  // this will cause us to re-sync our flags from scratch
    927  // but our returned 'form' is still not final (bug 133429), it will
    928  // be recomputed to its final value during the next call in Reflow()
    929  mEmbellishData.coreFrame = nullptr;
    930  ProcessOperatorData();
    931  return NS_OK;
    932 }
    933 
    934 void nsMathMLmoFrame::SetInitialChildList(ChildListID aListID,
    935                                          nsFrameList&& aChildList) {
    936  // First, let the parent class do its work
    937  nsMathMLTokenFrame::SetInitialChildList(aListID, std::move(aChildList));
    938  ProcessTextData();
    939 }
    940 
    941 void nsMathMLmoFrame::Reflow(nsPresContext* aPresContext,
    942                             ReflowOutput& aDesiredSize,
    943                             const ReflowInput& aReflowInput,
    944                             nsReflowStatus& aStatus) {
    945  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
    946 
    947  // certain values use units that depend on our ComputedStyle, so
    948  // it is safer to just process the whole lot here
    949  ProcessOperatorData();
    950 
    951  nsMathMLTokenFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
    952 }
    953 
    954 void nsMathMLmoFrame::Place(DrawTarget* aDrawTarget, const PlaceFlags& aFlags,
    955                            ReflowOutput& aDesiredSize) {
    956  nsMathMLTokenFrame::Place(aDrawTarget, aFlags, aDesiredSize);
    957 
    958  /* Special behaviour for largeops.
    959     In MathML "stretchy" and displaystyle "largeop" are different notions,
    960     even if we use the same technique to draw them (picking size variants).
    961     So largeop display operators should be considered "non-stretchy" and
    962     thus their sizes should be taken into account for the stretch size of
    963     other elements.
    964 
    965     This is a preliminary stretch - exact sizing/placement is handled by the
    966     Stretch() method.
    967  */
    968 
    969  if (aFlags.contains(PlaceFlag::MeasureOnly) &&
    970      StyleFont()->mMathStyle == StyleMathStyle::Normal &&
    971      NS_MATHML_OPERATOR_IS_LARGEOP(mFlags) && UseMathMLChar()) {
    972    nsBoundingMetrics newMetrics;
    973    nsresult rv = mMathMLChar.Stretch(
    974        this, aDrawTarget, nsLayoutUtils::FontSizeInflationFor(this),
    975        NS_STRETCH_DIRECTION_VERTICAL, aDesiredSize.mBoundingMetrics,
    976        newMetrics, NS_STRETCH_LARGEOP,
    977        StyleVisibility()->mDirection == StyleDirection::Rtl);
    978 
    979    if (NS_FAILED(rv)) {
    980      // Just use the initial size
    981      return;
    982    }
    983 
    984    aDesiredSize.mBoundingMetrics = newMetrics;
    985    /* Treat the ascent/descent values calculated in the TokenFrame place
    986      calculations as the minimum for aDesiredSize calculations, rather
    987      than fetching them from font metrics again.
    988   */
    989    aDesiredSize.SetBlockStartAscent(
    990        std::max(mBoundingMetrics.ascent, newMetrics.ascent));
    991    aDesiredSize.Height() =
    992        aDesiredSize.BlockStartAscent() +
    993        std::max(mBoundingMetrics.descent, newMetrics.descent);
    994    aDesiredSize.Width() = newMetrics.width;
    995    mBoundingMetrics = newMetrics;
    996  }
    997 }
    998 
    999 /* virtual */
   1000 void nsMathMLmoFrame::MarkIntrinsicISizesDirty() {
   1001  // if we get this, it may mean that something changed in the text
   1002  // content. So blow away everything an re-build the automatic data
   1003  // from the parent of our outermost embellished container (we ensure
   1004  // that we are the core, not just a sibling of the core)
   1005 
   1006  ProcessTextData();
   1007 
   1008  nsIFrame* target = this;
   1009  nsEmbellishData embellishData;
   1010  do {
   1011    target = target->GetParent();
   1012    GetEmbellishDataFrom(target, embellishData);
   1013  } while (embellishData.coreFrame == this);
   1014 
   1015  // we have automatic data to update in the children of the target frame
   1016  // XXXldb This should really be marking dirty rather than rebuilding
   1017  // so that we don't rebuild multiple times for the same change.
   1018  RebuildAutomaticDataForChildren(target);
   1019 
   1020  nsMathMLContainerFrame::MarkIntrinsicISizesDirty();
   1021 }
   1022 
   1023 /* virtual */
   1024 void nsMathMLmoFrame::GetIntrinsicISizeMetrics(gfxContext* aRenderingContext,
   1025                                               ReflowOutput& aDesiredSize) {
   1026  ProcessOperatorData();
   1027  if (UseMathMLChar()) {
   1028    uint32_t stretchHint =
   1029        GetStretchHint(mFlags, mPresentationData, true, StyleFont());
   1030    aDesiredSize.Width() = mMathMLChar.GetMaxWidth(
   1031        this, aRenderingContext->GetDrawTarget(),
   1032        nsLayoutUtils::FontSizeInflationFor(this), stretchHint);
   1033    aDesiredSize.Width() += IntrinsicISizeOffsets().BorderPadding();
   1034  } else {
   1035    nsMathMLTokenFrame::GetIntrinsicISizeMetrics(aRenderingContext,
   1036                                                 aDesiredSize);
   1037  }
   1038 
   1039  // leadingSpace and trailingSpace are actually applied to the outermost
   1040  // embellished container but for determining total intrinsic width it should
   1041  // be safe to include it for the core here instead.
   1042  bool isRTL = StyleVisibility()->mDirection == StyleDirection::Rtl;
   1043  aDesiredSize.Width() +=
   1044      mEmbellishData.leadingSpace + mEmbellishData.trailingSpace;
   1045  aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width();
   1046  if (isRTL) {
   1047    aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.trailingSpace;
   1048    aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.trailingSpace;
   1049  } else {
   1050    aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.leadingSpace;
   1051    aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.leadingSpace;
   1052  }
   1053 }
   1054 
   1055 nsresult nsMathMLmoFrame::AttributeChanged(int32_t aNameSpaceID,
   1056                                           nsAtom* aAttribute,
   1057                                           AttrModType aModType) {
   1058  // check if this is an attribute that can affect the embellished hierarchy
   1059  // in a significant way and re-layout the entire hierarchy.
   1060  // This is not needed for the fence and separator
   1061  // attributes, since they have no visual effect.
   1062  if (aAttribute == nsGkAtoms::accent || aAttribute == nsGkAtoms::form ||
   1063      aAttribute == nsGkAtoms::largeop || aAttribute == nsGkAtoms::maxsize ||
   1064      aAttribute == nsGkAtoms::minsize ||
   1065      aAttribute == nsGkAtoms::movablelimits ||
   1066      aAttribute == nsGkAtoms::rspace || aAttribute == nsGkAtoms::stretchy ||
   1067      aAttribute == nsGkAtoms::symmetric || aAttribute == nsGkAtoms::lspace) {
   1068    // set the target as the parent of our outermost embellished container
   1069    // (we ensure that we are the core, not just a sibling of the core)
   1070    nsIFrame* target = this;
   1071    nsEmbellishData embellishData;
   1072    do {
   1073      target = target->GetParent();
   1074      GetEmbellishDataFrom(target, embellishData);
   1075    } while (embellishData.coreFrame == this);
   1076 
   1077    // we have automatic data to update in the children of the target frame
   1078    return ReLayoutChildren(target);
   1079  }
   1080 
   1081  return nsMathMLTokenFrame::AttributeChanged(aNameSpaceID, aAttribute,
   1082                                              aModType);
   1083 }
   1084 
   1085 void nsMathMLmoFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
   1086  nsMathMLTokenFrame::DidSetComputedStyle(aOldStyle);
   1087  mMathMLChar.SetComputedStyle(Style());
   1088 }