tor-browser

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

nsMathMLmencloseFrame.cpp (26269B)


      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 "nsMathMLmencloseFrame.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "gfx2DGlue.h"
     12 #include "gfxContext.h"
     13 #include "gfxUtils.h"
     14 #include "mozilla/PresShell.h"
     15 #include "mozilla/StaticPrefs_mathml.h"
     16 #include "mozilla/dom/Document.h"
     17 #include "mozilla/dom/Element.h"
     18 #include "mozilla/gfx/2D.h"
     19 #include "mozilla/gfx/PathHelpers.h"
     20 #include "nsDisplayList.h"
     21 #include "nsLayoutUtils.h"
     22 #include "nsMathMLChar.h"
     23 #include "nsPresContext.h"
     24 #include "nsWhitespaceTokenizer.h"
     25 
     26 using namespace mozilla;
     27 using namespace mozilla::gfx;
     28 
     29 //
     30 // <menclose> -- enclose content with a stretching symbol such
     31 // as a long division sign. - implementation
     32 
     33 // longdiv:
     34 // Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis
     35 // renders better with current font support.
     36 static const char16_t kLongDivChar = ')';
     37 
     38 // updiagonalstrike
     39 static const uint8_t kArrowHeadSize = 10;
     40 
     41 // phasorangle
     42 static const uint8_t kPhasorangleWidth = 8;
     43 
     44 nsIFrame* NS_NewMathMLmencloseFrame(PresShell* aPresShell,
     45                                    ComputedStyle* aStyle) {
     46  return new (aPresShell)
     47      nsMathMLmencloseFrame(aStyle, aPresShell->GetPresContext());
     48 }
     49 
     50 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame)
     51 
     52 nsMathMLmencloseFrame::nsMathMLmencloseFrame(ComputedStyle* aStyle,
     53                                             nsPresContext* aPresContext)
     54    : nsMathMLContainerFrame(aStyle, aPresContext, kClassID),
     55      mRuleThickness(0),
     56      mLongDivCharIndex(-1),
     57      mContentWidth(0) {}
     58 
     59 nsMathMLmencloseFrame::~nsMathMLmencloseFrame() = default;
     60 
     61 nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask) {
     62  // Is the char already allocated?
     63  if (mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) {
     64    return NS_OK;
     65  }
     66 
     67  // No need to track the ComputedStyle given to our MathML chars.
     68  // The Style System will use Get/SetAdditionalComputedStyle() to keep it
     69  // up-to-date if dynamic changes arise.
     70  uint32_t i = mMathMLChar.Length();
     71  nsAutoString Char;
     72 
     73  // XXX(Bug 1631371) Check if this should use a fallible operation as it
     74  // pretended earlier, or change the return type to void.
     75  mMathMLChar.AppendElement();
     76 
     77  if (mask == NOTATION_LONGDIV) {
     78    Char.Assign(kLongDivChar);
     79    mLongDivCharIndex = i;
     80  }
     81 
     82  mMathMLChar[i].SetData(Char);
     83  mMathMLChar[i].SetComputedStyle(Style());
     84 
     85  return NS_OK;
     86 }
     87 
     88 /*
     89 * Add a notation to draw, if the argument is the name of a known notation.
     90 * @param aNotation string name of a notation
     91 */
     92 nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation) {
     93  nsresult rv;
     94 
     95  if (aNotation.EqualsLiteral("longdiv")) {
     96    rv = AllocateMathMLChar(NOTATION_LONGDIV);
     97    NS_ENSURE_SUCCESS(rv, rv);
     98    mNotationsToDraw += NOTATION_LONGDIV;
     99  } else if (aNotation.EqualsLiteral("actuarial")) {
    100    mNotationsToDraw += NOTATION_RIGHT;
    101    mNotationsToDraw += NOTATION_TOP;
    102  } else if (aNotation.EqualsLiteral("box")) {
    103    mNotationsToDraw += NOTATION_LEFT;
    104    mNotationsToDraw += NOTATION_RIGHT;
    105    mNotationsToDraw += NOTATION_TOP;
    106    mNotationsToDraw += NOTATION_BOTTOM;
    107  } else if (aNotation.EqualsLiteral("roundedbox")) {
    108    mNotationsToDraw += NOTATION_ROUNDEDBOX;
    109  } else if (aNotation.EqualsLiteral("circle")) {
    110    mNotationsToDraw += NOTATION_CIRCLE;
    111  } else if (aNotation.EqualsLiteral("left")) {
    112    mNotationsToDraw += NOTATION_LEFT;
    113  } else if (aNotation.EqualsLiteral("right")) {
    114    mNotationsToDraw += NOTATION_RIGHT;
    115  } else if (aNotation.EqualsLiteral("top")) {
    116    mNotationsToDraw += NOTATION_TOP;
    117  } else if (aNotation.EqualsLiteral("bottom")) {
    118    mNotationsToDraw += NOTATION_BOTTOM;
    119  } else if (aNotation.EqualsLiteral("updiagonalstrike")) {
    120    mNotationsToDraw += NOTATION_UPDIAGONALSTRIKE;
    121  } else if (aNotation.EqualsLiteral("updiagonalarrow")) {
    122    mNotationsToDraw += NOTATION_UPDIAGONALARROW;
    123  } else if (aNotation.EqualsLiteral("downdiagonalstrike")) {
    124    mNotationsToDraw += NOTATION_DOWNDIAGONALSTRIKE;
    125  } else if (aNotation.EqualsLiteral("verticalstrike")) {
    126    mNotationsToDraw += NOTATION_VERTICALSTRIKE;
    127  } else if (aNotation.EqualsLiteral("horizontalstrike")) {
    128    mNotationsToDraw += NOTATION_HORIZONTALSTRIKE;
    129  } else if (aNotation.EqualsLiteral("madruwb")) {
    130    mNotationsToDraw += NOTATION_RIGHT;
    131    mNotationsToDraw += NOTATION_BOTTOM;
    132  } else if (aNotation.EqualsLiteral("phasorangle")) {
    133    mNotationsToDraw += NOTATION_BOTTOM;
    134    mNotationsToDraw += NOTATION_PHASORANGLE;
    135  }
    136 
    137  return NS_OK;
    138 }
    139 
    140 /*
    141 * Initialize the list of notations to draw
    142 */
    143 void nsMathMLmencloseFrame::InitNotations() {
    144  MarkNeedsDisplayItemRebuild();
    145  mNotationsToDraw.clear();
    146  mLongDivCharIndex = -1;
    147  mMathMLChar.Clear();
    148 
    149  nsAutoString value;
    150 
    151  if (mContent->AsElement()->GetAttr(nsGkAtoms::notation, value)) {
    152    // parse the notation attribute
    153    nsWhitespaceTokenizer tokenizer(value);
    154 
    155    while (tokenizer.hasMoreTokens()) {
    156      AddNotation(tokenizer.nextToken());
    157    }
    158 
    159    if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
    160      // For <menclose notation="updiagonalstrike updiagonalarrow">, if
    161      // the two notations are drawn then the strike line may cause the point of
    162      // the arrow to be too wide. Hence we will only draw the updiagonalarrow
    163      // and the arrow shaft may be thought to be the updiagonalstrike.
    164      mNotationsToDraw -= NOTATION_UPDIAGONALSTRIKE;
    165    }
    166  } else {
    167    // default: longdiv
    168    if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV))) {
    169      return;
    170    }
    171    mNotationsToDraw += NOTATION_LONGDIV;
    172  }
    173 }
    174 
    175 NS_IMETHODIMP
    176 nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent) {
    177  // let the base class get the default from our parent
    178  nsMathMLContainerFrame::InheritAutomaticData(aParent);
    179 
    180  mPresentationData.flags +=
    181      MathMLPresentationFlag::StretchAllChildrenVertically;
    182 
    183  InitNotations();
    184 
    185  return NS_OK;
    186 }
    187 
    188 void nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
    189                                             const nsDisplayListSet& aLists) {
    190  /////////////
    191  // paint the menclosed content
    192  nsMathMLContainerFrame::BuildDisplayList(aBuilder, aLists);
    193 
    194  nsRect mencloseRect = nsIFrame::GetContentRectRelativeToSelf();
    195 
    196  if (IsToDraw(NOTATION_PHASORANGLE)) {
    197    DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness,
    198                    NOTATION_PHASORANGLE);
    199  }
    200 
    201  if (IsToDraw(NOTATION_LONGDIV)) {
    202    mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1);
    203 
    204    nsRect rect;
    205    mMathMLChar[mLongDivCharIndex].GetRect(rect);
    206    rect.SizeTo(rect.width + mContentWidth, mRuleThickness);
    207    DisplayBar(aBuilder, this, rect, aLists, NOTATION_LONGDIV);
    208  }
    209 
    210  if (IsToDraw(NOTATION_TOP)) {
    211    nsRect rect(0, 0, mencloseRect.width, mRuleThickness);
    212    DisplayBar(aBuilder, this, rect, aLists, NOTATION_TOP);
    213  }
    214 
    215  if (IsToDraw(NOTATION_BOTTOM)) {
    216    nsRect rect(0, mencloseRect.height - mRuleThickness, mencloseRect.width,
    217                mRuleThickness);
    218    DisplayBar(aBuilder, this, rect, aLists, NOTATION_BOTTOM);
    219  }
    220 
    221  if (IsToDraw(NOTATION_LEFT)) {
    222    nsRect rect(0, 0, mRuleThickness, mencloseRect.height);
    223    DisplayBar(aBuilder, this, rect, aLists, NOTATION_LEFT);
    224  }
    225 
    226  if (IsToDraw(NOTATION_RIGHT)) {
    227    nsRect rect(mencloseRect.width - mRuleThickness, 0, mRuleThickness,
    228                mencloseRect.height);
    229    DisplayBar(aBuilder, this, rect, aLists, NOTATION_RIGHT);
    230  }
    231 
    232  if (IsToDraw(NOTATION_ROUNDEDBOX)) {
    233    DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness,
    234                    NOTATION_ROUNDEDBOX);
    235  }
    236 
    237  if (IsToDraw(NOTATION_CIRCLE)) {
    238    DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness,
    239                    NOTATION_CIRCLE);
    240  }
    241 
    242  if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) {
    243    DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness,
    244                    NOTATION_UPDIAGONALSTRIKE);
    245  }
    246 
    247  if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
    248    DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness,
    249                    NOTATION_UPDIAGONALARROW);
    250  }
    251 
    252  if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) {
    253    DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness,
    254                    NOTATION_DOWNDIAGONALSTRIKE);
    255  }
    256 
    257  if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) {
    258    nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2,
    259                mencloseRect.width, mRuleThickness);
    260    DisplayBar(aBuilder, this, rect, aLists, NOTATION_HORIZONTALSTRIKE);
    261  }
    262 
    263  if (IsToDraw(NOTATION_VERTICALSTRIKE)) {
    264    nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0, mRuleThickness,
    265                mencloseRect.height);
    266    DisplayBar(aBuilder, this, rect, aLists, NOTATION_VERTICALSTRIKE);
    267  }
    268 }
    269 
    270 /* virtual */
    271 void nsMathMLmencloseFrame::Place(DrawTarget* aDrawTarget,
    272                                  const PlaceFlags& aFlags,
    273                                  ReflowOutput& aDesiredSize) {
    274  ///////////////
    275  // Measure the size of our content using the base class to format like an
    276  // inferred mrow, without border/padding.
    277  ReflowOutput baseSize(aDesiredSize.GetWritingMode());
    278  PlaceFlags flags = aFlags + PlaceFlag::MeasureOnly +
    279                     PlaceFlag::IgnoreBorderPadding +
    280                     PlaceFlag::DoNotAdjustForWidthAndHeight;
    281  nsMathMLContainerFrame::Place(aDrawTarget, flags, baseSize);
    282 
    283  nsBoundingMetrics bmBase = baseSize.mBoundingMetrics;
    284  nscoord dx_left = 0, dx_right = 0;
    285  nsBoundingMetrics bmLongdivChar;
    286  nscoord longdivAscent = 0, longdivDescent = 0;
    287  nscoord psi = 0;
    288  nscoord leading = 0;
    289 
    290  ///////////////
    291  // Thickness of bars and font metrics
    292  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
    293 
    294  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
    295  RefPtr<nsFontMetrics> fm =
    296      nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
    297  GetRuleThickness(aDrawTarget, fm, mRuleThickness);
    298  if (mRuleThickness < onePixel) {
    299    mRuleThickness = onePixel;
    300  }
    301 
    302  char16_t one = '1';
    303  nsBoundingMetrics bmOne =
    304      nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, aDrawTarget);
    305 
    306  ///////////////
    307  // General rules: the menclose element takes the size of the enclosed content.
    308  // We add a padding when needed.
    309 
    310  // determine padding & psi
    311  nscoord padding = 3 * mRuleThickness;
    312  nscoord delta = padding % onePixel;
    313  if (delta) {
    314    padding += onePixel - delta;  // round up
    315  }
    316 
    317  if (IsToDraw(NOTATION_LONGDIV)) {
    318    // The MathML spec does not define precise layout rules for menclose. Here
    319    // we draw longdiv using the same parameter as for radicals.
    320    // See https://github.com/w3c/mathml-core/issues/245
    321    nscoord dummy;
    322    GetRadicalParameters(fm, StyleFont()->mMathStyle == StyleMathStyle::Normal,
    323                         dummy, leading, psi);
    324 
    325    // adjust clearance psi to get an exact number of pixels -- this
    326    // gives a nicer & uniform look on stacked radicals (bug 130282)
    327    delta = psi % onePixel;
    328    if (delta) {
    329      psi += onePixel - delta;  // round up
    330    }
    331  }
    332 
    333  // Set horizontal parameters
    334  if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) ||
    335      IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_BOTTOM) ||
    336      IsToDraw(NOTATION_CIRCLE)) {
    337    dx_left = padding;
    338  }
    339 
    340  if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) ||
    341      IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_BOTTOM) ||
    342      IsToDraw(NOTATION_CIRCLE)) {
    343    dx_right = padding;
    344  }
    345 
    346  // Set vertical parameters
    347  if (IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) ||
    348      IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
    349      IsToDraw(NOTATION_UPDIAGONALARROW) ||
    350      IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
    351      IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) ||
    352      IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_LONGDIV) ||
    353      IsToDraw(NOTATION_PHASORANGLE)) {
    354    // set a minimal value for the base height
    355    bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent);
    356    bmBase.descent = std::max(0, bmBase.descent);
    357  }
    358 
    359  mBoundingMetrics.ascent = bmBase.ascent;
    360  mBoundingMetrics.descent = bmBase.descent;
    361 
    362  if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) ||
    363      IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_RIGHT) ||
    364      IsToDraw(NOTATION_CIRCLE)) {
    365    mBoundingMetrics.ascent += padding;
    366  }
    367 
    368  if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_LEFT) ||
    369      IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_BOTTOM) ||
    370      IsToDraw(NOTATION_CIRCLE)) {
    371    mBoundingMetrics.descent += padding;
    372  }
    373 
    374  ///////////////
    375  // phasorangle notation
    376  if (IsToDraw(NOTATION_PHASORANGLE)) {
    377    nscoord phasorangleWidth = kPhasorangleWidth * mRuleThickness;
    378    // Update horizontal parameters
    379    dx_left = std::max(dx_left, phasorangleWidth);
    380  }
    381 
    382  ///////////////
    383  // updiagonal arrow notation. We need enough space at the top right corner to
    384  // draw the arrow head.
    385  if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
    386    // This is an estimate, see nsDisplayNotation::Paint for the exact head size
    387    nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness;
    388 
    389    // We want that the arrow shaft strikes the menclose content and that the
    390    // arrow head does not overlap with that content. Hence we add some space
    391    // on the right. We don't add space on the top but only ensure that the
    392    // ascent is large enough.
    393    dx_right = std::max(dx_right, arrowHeadSize);
    394    mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize);
    395  }
    396 
    397  ///////////////
    398  // circle notation: we don't want the ellipse to overlap the enclosed
    399  // content. Hence, we need to increase the size of the bounding box by a
    400  // factor of at least sqrt(2).
    401  if (IsToDraw(NOTATION_CIRCLE)) {
    402    double ratio = (sqrt(2.0) - 1.0) / 2.0;
    403    nscoord padding2;
    404 
    405    // Update horizontal parameters
    406    padding2 = ratio * bmBase.width;
    407 
    408    dx_left = std::max(dx_left, padding2);
    409    dx_right = std::max(dx_right, padding2);
    410 
    411    // Update vertical parameters
    412    padding2 = ratio * (bmBase.ascent + bmBase.descent);
    413 
    414    mBoundingMetrics.ascent =
    415        std::max(mBoundingMetrics.ascent, bmBase.ascent + padding2);
    416    mBoundingMetrics.descent =
    417        std::max(mBoundingMetrics.descent, bmBase.descent + padding2);
    418  }
    419 
    420  ///////////////
    421  // longdiv notation:
    422  if (IsToDraw(NOTATION_LONGDIV)) {
    423    if (aFlags.contains(PlaceFlag::IntrinsicSize)) {
    424      nscoord longdiv_width = mMathMLChar[mLongDivCharIndex].GetMaxWidth(
    425          this, aDrawTarget, fontSizeInflation);
    426 
    427      // Update horizontal parameters
    428      dx_left = std::max(dx_left, longdiv_width);
    429    } else {
    430      // Stretch the parenthesis to the appropriate height if it is not
    431      // big enough.
    432      nsBoundingMetrics contSize = bmBase;
    433      contSize.ascent = mRuleThickness;
    434      contSize.descent = bmBase.ascent + bmBase.descent + psi;
    435 
    436      // height(longdiv) should be >= height(base) + psi + mRuleThickness
    437      mMathMLChar[mLongDivCharIndex].Stretch(
    438          this, aDrawTarget, fontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL,
    439          contSize, bmLongdivChar, NS_STRETCH_LARGER, false);
    440      mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar);
    441 
    442      // Update horizontal parameters
    443      dx_left = std::max(dx_left, bmLongdivChar.width);
    444 
    445      // Update vertical parameters
    446      longdivAscent = bmBase.ascent + psi + mRuleThickness;
    447      longdivDescent = std::max(
    448          bmBase.descent,
    449          (bmLongdivChar.ascent + bmLongdivChar.descent - longdivAscent));
    450 
    451      mBoundingMetrics.ascent =
    452          std::max(mBoundingMetrics.ascent, longdivAscent);
    453      mBoundingMetrics.descent =
    454          std::max(mBoundingMetrics.descent, longdivDescent);
    455    }
    456  }
    457 
    458  ///////////////
    459  //
    460  if (IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) ||
    461      (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) {
    462    // center the menclose around the content (horizontally)
    463    dx_left = dx_right = std::max(dx_left, dx_right);
    464  }
    465 
    466  ///////////////
    467  // The maximum size is now computed: set the remaining parameters
    468  mBoundingMetrics.width = dx_left + bmBase.width + dx_right;
    469 
    470  mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing);
    471  mBoundingMetrics.rightBearing =
    472      std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing);
    473 
    474  aDesiredSize.Width() = mBoundingMetrics.width;
    475 
    476  aDesiredSize.SetBlockStartAscent(
    477      std::max(mBoundingMetrics.ascent, baseSize.BlockStartAscent()));
    478  aDesiredSize.Height() =
    479      aDesiredSize.BlockStartAscent() +
    480      std::max(mBoundingMetrics.descent,
    481               baseSize.Height() - baseSize.BlockStartAscent());
    482 
    483  if (IsToDraw(NOTATION_LONGDIV)) {
    484    nscoord desiredSizeAscent = aDesiredSize.BlockStartAscent();
    485    nscoord desiredSizeDescent =
    486        aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
    487 
    488    if (IsToDraw(NOTATION_LONGDIV)) {
    489      desiredSizeAscent = std::max(desiredSizeAscent, longdivAscent + leading);
    490      desiredSizeDescent =
    491          std::max(desiredSizeDescent, longdivDescent + mRuleThickness);
    492    }
    493 
    494    aDesiredSize.SetBlockStartAscent(desiredSizeAscent);
    495    aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent;
    496  }
    497 
    498  if (IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) ||
    499      (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) {
    500    // center the menclose around the content (vertically)
    501    nscoord dy = std::max(aDesiredSize.BlockStartAscent() - bmBase.ascent,
    502                          aDesiredSize.Height() -
    503                              aDesiredSize.BlockStartAscent() - bmBase.descent);
    504 
    505    aDesiredSize.SetBlockStartAscent(bmBase.ascent + dy);
    506    aDesiredSize.Height() =
    507        aDesiredSize.BlockStartAscent() + bmBase.descent + dy;
    508  }
    509 
    510  // Update mBoundingMetrics ascent/descent
    511  if (IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_RIGHT) ||
    512      IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
    513      IsToDraw(NOTATION_UPDIAGONALARROW) ||
    514      IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
    515      IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) ||
    516      IsToDraw(NOTATION_ROUNDEDBOX)) {
    517    mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent();
    518  }
    519 
    520  if (IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_RIGHT) ||
    521      IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
    522      IsToDraw(NOTATION_UPDIAGONALARROW) ||
    523      IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
    524      IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) ||
    525      IsToDraw(NOTATION_ROUNDEDBOX)) {
    526    mBoundingMetrics.descent =
    527        aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
    528  }
    529 
    530  // phasorangle notation:
    531  // move up from the bottom by the angled line height
    532  if (IsToDraw(NOTATION_PHASORANGLE)) {
    533    mBoundingMetrics.ascent = std::max(
    534        mBoundingMetrics.ascent,
    535        2 * kPhasorangleWidth * mRuleThickness - mBoundingMetrics.descent);
    536  }
    537 
    538  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
    539 
    540  // Apply width/height to math content box.
    541  auto sizes = GetWidthAndHeightForPlaceAdjustment(aFlags);
    542  dx_left += ApplyAdjustmentForWidthAndHeight(aFlags, sizes, aDesiredSize,
    543                                              mBoundingMetrics);
    544 
    545  // Add padding+border.
    546  auto borderPadding = GetBorderPaddingForPlace(aFlags);
    547  InflateReflowAndBoundingMetrics(borderPadding, aDesiredSize,
    548                                  mBoundingMetrics);
    549 
    550  mReference.x = 0;
    551  mReference.y = aDesiredSize.BlockStartAscent();
    552 
    553  if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
    554    //////////////////
    555    // Set position and size of MathMLChars
    556    if (IsToDraw(NOTATION_LONGDIV)) {
    557      mMathMLChar[mLongDivCharIndex].SetRect(nsRect(
    558          dx_left - bmLongdivChar.width + borderPadding.left,
    559          aDesiredSize.BlockStartAscent() - longdivAscent, bmLongdivChar.width,
    560          bmLongdivChar.ascent + bmLongdivChar.descent));
    561    }
    562 
    563    mContentWidth = bmBase.width;
    564 
    565    //////////////////
    566    // Finish reflowing child frames
    567    PositionRowChildFrames(dx_left + borderPadding.left,
    568                           aDesiredSize.BlockStartAscent());
    569  }
    570 }
    571 
    572 nscoord nsMathMLmencloseFrame::FixInterFrameSpacing(
    573    ReflowOutput& aDesiredSize) {
    574  nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
    575  if (!gap) {
    576    return 0;
    577  }
    578 
    579  // Move the MathML characters
    580  nsRect rect;
    581  for (uint32_t i = 0; i < mMathMLChar.Length(); i++) {
    582    mMathMLChar[i].GetRect(rect);
    583    rect.MoveBy(gap, 0);
    584    mMathMLChar[i].SetRect(rect);
    585  }
    586 
    587  return gap;
    588 }
    589 
    590 nsresult nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID,
    591                                                 nsAtom* aAttribute,
    592                                                 AttrModType aModType) {
    593  if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::notation) {
    594    InitNotations();
    595    PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
    596                                  NS_FRAME_IS_DIRTY);
    597    return NS_OK;
    598  }
    599 
    600  return nsMathMLContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
    601                                                  aModType);
    602 }
    603 
    604 void nsMathMLmencloseFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
    605  nsMathMLContainerFrame::DidSetComputedStyle(aOldStyle);
    606  for (auto& ch : mMathMLChar) {
    607    ch.SetComputedStyle(Style());
    608  }
    609 }
    610 
    611 //////////////////
    612 
    613 namespace mozilla {
    614 
    615 class nsDisplayNotation final : public nsPaintedDisplayItem {
    616 public:
    617  nsDisplayNotation(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
    618                    const nsRect& aRect, nscoord aThickness,
    619                    nsMencloseNotation aType)
    620      : nsPaintedDisplayItem(aBuilder, aFrame),
    621        mRect(aRect),
    622        mThickness(aThickness),
    623        mType(aType) {
    624    MOZ_COUNT_CTOR(nsDisplayNotation);
    625  }
    626 
    627  MOZ_COUNTED_DTOR_FINAL(nsDisplayNotation)
    628 
    629  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
    630  NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION)
    631 
    632 private:
    633  nsRect mRect;
    634  nscoord mThickness;
    635  nsMencloseNotation mType;
    636 };
    637 
    638 void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder,
    639                              gfxContext* aCtx) {
    640  DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
    641  nsPresContext* presContext = mFrame->PresContext();
    642 
    643  Float strokeWidth = presContext->AppUnitsToGfxUnits(mThickness);
    644 
    645  Rect rect = NSRectToRect(mRect + ToReferenceFrame(),
    646                           presContext->AppUnitsPerDevPixel());
    647  rect.Deflate(strokeWidth / 2.f);
    648 
    649  ColorPattern color(ToDeviceColor(
    650      mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
    651 
    652  StrokeOptions strokeOptions(strokeWidth);
    653 
    654  switch (mType) {
    655    case NOTATION_CIRCLE: {
    656      RefPtr<Path> ellipse =
    657          MakePathForEllipse(aDrawTarget, rect.Center(), rect.Size());
    658      aDrawTarget.Stroke(ellipse, color, strokeOptions);
    659      return;
    660    }
    661    case NOTATION_ROUNDEDBOX: {
    662      Float radius = 3 * strokeWidth;
    663      RectCornerRadii radii(radius, radius);
    664      RefPtr<Path> roundedRect =
    665          MakePathForRoundedRect(aDrawTarget, rect, radii, true);
    666      aDrawTarget.Stroke(roundedRect, color, strokeOptions);
    667      return;
    668    }
    669    case NOTATION_UPDIAGONALSTRIKE: {
    670      aDrawTarget.StrokeLine(rect.BottomLeft(), rect.TopRight(), color,
    671                             strokeOptions);
    672      return;
    673    }
    674    case NOTATION_DOWNDIAGONALSTRIKE: {
    675      aDrawTarget.StrokeLine(rect.TopLeft(), rect.BottomRight(), color,
    676                             strokeOptions);
    677      return;
    678    }
    679    case NOTATION_UPDIAGONALARROW: {
    680      // Compute some parameters to draw the updiagonalarrow. The values below
    681      // are taken from MathJax's HTML-CSS output.
    682      Float W = rect.Width();
    683      gfxFloat H = rect.Height();
    684      Float l = sqrt(W * W + H * H);
    685      Float f = Float(kArrowHeadSize) * strokeWidth / l;
    686      Float w = W * f;
    687      gfxFloat h = H * f;
    688 
    689      // Draw the arrow shaft
    690      aDrawTarget.StrokeLine(rect.BottomLeft(),
    691                             rect.TopRight() + Point(-.7 * w, .7 * h), color,
    692                             strokeOptions);
    693 
    694      // Draw the arrow head
    695      RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
    696      builder->MoveTo(rect.TopRight());
    697      builder->LineTo(
    698          rect.TopRight() +
    699          Point(-w - .4 * h, std::max(-strokeWidth / 2.0, h - .4 * w)));
    700      builder->LineTo(rect.TopRight() + Point(-.7 * w, .7 * h));
    701      builder->LineTo(
    702          rect.TopRight() +
    703          Point(std::min(strokeWidth / 2.0, -w + .4 * h), h + .4 * w));
    704      builder->Close();
    705      RefPtr<Path> path = builder->Finish();
    706      aDrawTarget.Fill(path, color);
    707      return;
    708    }
    709    case NOTATION_PHASORANGLE: {
    710      // Compute some parameters to draw the angled line,
    711      // that uses a slope of 2 (angle = tan^-1(2)).
    712      // H = w * tan(angle) = w * 2
    713      Float w = Float(kPhasorangleWidth) * strokeWidth;
    714      Float H = 2 * w;
    715 
    716      // Draw the angled line
    717      aDrawTarget.StrokeLine(rect.BottomLeft(),
    718                             rect.BottomLeft() + Point(w, -H), color,
    719                             strokeOptions);
    720      return;
    721    }
    722    default:
    723      MOZ_ASSERT_UNREACHABLE(
    724          "This notation can not be drawn using "
    725          "nsDisplayNotation");
    726  }
    727 }
    728 
    729 }  // namespace mozilla
    730 
    731 void nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder,
    732                                            nsIFrame* aFrame,
    733                                            const nsRect& aRect,
    734                                            const nsDisplayListSet& aLists,
    735                                            nscoord aThickness,
    736                                            nsMencloseNotation aType) {
    737  if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() ||
    738      aThickness <= 0) {
    739    return;
    740  }
    741 
    742  const uint16_t index = aType;
    743  aLists.Content()->AppendNewToTopWithIndex<nsDisplayNotation>(
    744      aBuilder, aFrame, index, aRect, aThickness, aType);
    745 }