tor-browser

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

nsMathMLContainerFrame.cpp (59674B)


      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 "nsMathMLContainerFrame.h"
      8 
      9 #include "gfxContext.h"
     10 #include "gfxUtils.h"
     11 #include "mozilla/Likely.h"
     12 #include "mozilla/PresShell.h"
     13 #include "mozilla/dom/MathMLElement.h"
     14 #include "mozilla/gfx/2D.h"
     15 #include "nsContentUtils.h"
     16 #include "nsDisplayList.h"
     17 #include "nsGkAtoms.h"
     18 #include "nsIScriptError.h"
     19 #include "nsLayoutUtils.h"
     20 #include "nsNameSpaceManager.h"
     21 #include "nsPresContext.h"
     22 
     23 using namespace mozilla;
     24 using namespace mozilla::gfx;
     25 
     26 //
     27 // nsMathMLContainerFrame implementation
     28 //
     29 
     30 NS_QUERYFRAME_HEAD(nsMathMLContainerFrame)
     31  NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
     32  NS_QUERYFRAME_ENTRY(nsMathMLContainerFrame)
     33 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
     34 
     35 /* /////////////
     36 * nsIMathMLFrame - support methods for stretchy elements
     37 * =============================================================================
     38 */
     39 
     40 static bool IsForeignChild(const nsIFrame* aFrame) {
     41  // This counts nsMathMLmathBlockFrame as a foreign child, because it
     42  // uses block reflow
     43  return !aFrame->IsMathMLFrame() || aFrame->IsBlockFrame();
     44 }
     45 
     46 NS_DECLARE_FRAME_PROPERTY_DELETABLE(HTMLReflowOutputProperty, ReflowOutput)
     47 
     48 /* static */
     49 void nsMathMLContainerFrame::SaveReflowAndBoundingMetricsFor(
     50    nsIFrame* aFrame, const ReflowOutput& aReflowOutput,
     51    const nsBoundingMetrics& aBoundingMetrics) {
     52  ReflowOutput* reflowOutput = new ReflowOutput(aReflowOutput);
     53  reflowOutput->mBoundingMetrics = aBoundingMetrics;
     54  aFrame->SetProperty(HTMLReflowOutputProperty(), reflowOutput);
     55 }
     56 
     57 // helper method to facilitate getting the reflow and bounding metrics
     58 /* static */
     59 void nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(
     60    nsIFrame* aFrame, ReflowOutput& aReflowOutput,
     61    nsBoundingMetrics& aBoundingMetrics, MathMLFrameType* aMathMLFrameType) {
     62  MOZ_ASSERT(aFrame, "null arg");
     63 
     64  ReflowOutput* reflowOutput = aFrame->GetProperty(HTMLReflowOutputProperty());
     65 
     66  // IMPORTANT: This function is only meant to be called in Place() methods
     67  // where it is assumed that SaveReflowAndBoundingMetricsFor has recorded the
     68  // information.
     69  NS_ASSERTION(reflowOutput, "Didn't SaveReflowAndBoundingMetricsFor frame!");
     70  if (reflowOutput) {
     71    aReflowOutput = *reflowOutput;
     72    aBoundingMetrics = reflowOutput->mBoundingMetrics;
     73  }
     74 
     75  if (aMathMLFrameType) {
     76    if (!IsForeignChild(aFrame)) {
     77      nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
     78      if (mathMLFrame) {
     79        *aMathMLFrameType = mathMLFrame->GetMathMLFrameType();
     80        return;
     81      }
     82    }
     83    *aMathMLFrameType = MathMLFrameType::Unknown;
     84  }
     85 }
     86 
     87 void nsMathMLContainerFrame::ClearSavedChildMetrics() {
     88  nsIFrame* childFrame = mFrames.FirstChild();
     89  while (childFrame) {
     90    childFrame->RemoveProperty(HTMLReflowOutputProperty());
     91    childFrame = childFrame->GetNextSibling();
     92  }
     93 }
     94 
     95 nsMargin nsMathMLContainerFrame::GetBorderPaddingForPlace(
     96    const PlaceFlags& aFlags) {
     97  if (aFlags.contains(PlaceFlag::IgnoreBorderPadding)) {
     98    return nsMargin();
     99  }
    100 
    101  if (aFlags.contains(PlaceFlag::IntrinsicSize)) {
    102    // Bug 1910859: Should we provide separate left and right border/padding?
    103    return nsMargin(0, IntrinsicISizeOffsets().BorderPadding(), 0, 0);
    104  }
    105 
    106  return GetUsedBorderAndPadding();
    107 }
    108 
    109 /* static */
    110 nsMargin nsMathMLContainerFrame::GetMarginForPlace(const PlaceFlags& aFlags,
    111                                                   nsIFrame* aChild) {
    112  if (aFlags.contains(PlaceFlag::IntrinsicSize)) {
    113    // Bug 1910859: Should we provide separate left and right margin?
    114    return nsMargin(0, aChild->IntrinsicISizeOffsets().margin, 0, 0);
    115  }
    116 
    117  return aChild->GetUsedMargin();
    118 }
    119 
    120 void nsMathMLContainerFrame::InflateReflowAndBoundingMetrics(
    121    const nsMargin& aBorderPadding, ReflowOutput& aReflowOutput,
    122    nsBoundingMetrics& aBoundingMetrics) {
    123  // Bug 1910858: It is not really clear what is the right way to update the
    124  // ink bounding box when adding border or padding. Below, we assume that
    125  // border/padding inflate it.
    126  aBoundingMetrics.rightBearing += aBorderPadding.LeftRight();
    127  aBoundingMetrics.width += aBorderPadding.LeftRight();
    128  aReflowOutput.mBoundingMetrics = aBoundingMetrics;
    129  aReflowOutput.Width() += aBorderPadding.LeftRight();
    130  aReflowOutput.SetBlockStartAscent(aReflowOutput.BlockStartAscent() +
    131                                    aBorderPadding.top);
    132  aReflowOutput.Height() += aBorderPadding.TopBottom();
    133 }
    134 
    135 nsMathMLContainerFrame::WidthAndHeightForPlaceAdjustment
    136 nsMathMLContainerFrame::GetWidthAndHeightForPlaceAdjustment(
    137    const PlaceFlags& aFlags) {
    138  WidthAndHeightForPlaceAdjustment sizes;
    139  if (aFlags.contains(PlaceFlag::DoNotAdjustForWidthAndHeight)) {
    140    return sizes;
    141  }
    142  const nsStylePosition* stylePos = StylePosition();
    143  const auto& width = stylePos->mWidth;
    144  // TODO: Resolve percentages.
    145  // https://github.com/w3c/mathml-core/issues/76
    146  if (width.ConvertsToLength()) {
    147    sizes.width = Some(width.ToLength());
    148  }
    149  if (!aFlags.contains(PlaceFlag::IntrinsicSize)) {
    150    // TODO: Resolve percentages.
    151    // https://github.com/w3c/mathml-core/issues/77
    152    const auto& height = stylePos->mHeight;
    153    if (height.ConvertsToLength()) {
    154      sizes.height = Some(height.ToLength());
    155    }
    156  }
    157  return sizes;
    158 }
    159 
    160 nscoord nsMathMLContainerFrame::ApplyAdjustmentForWidthAndHeight(
    161    const PlaceFlags& aFlags, const WidthAndHeightForPlaceAdjustment& aSizes,
    162    ReflowOutput& aReflowOutput, nsBoundingMetrics& aBoundingMetrics) {
    163  nscoord shiftX = 0;
    164  if (aSizes.width) {
    165    MOZ_ASSERT(!aFlags.contains(PlaceFlag::DoNotAdjustForWidthAndHeight));
    166    auto width = *aSizes.width;
    167    auto oldWidth = aReflowOutput.Width();
    168    if (IsMathContentBoxHorizontallyCentered()) {
    169      shiftX = (width - oldWidth) / 2;
    170    } else if (StyleVisibility()->mDirection == StyleDirection::Rtl) {
    171      shiftX = width - oldWidth;
    172    }
    173    aBoundingMetrics.leftBearing = 0;
    174    aBoundingMetrics.rightBearing = width;
    175    aBoundingMetrics.width = width;
    176    aReflowOutput.mBoundingMetrics = aBoundingMetrics;
    177    aReflowOutput.Width() = width;
    178  }
    179  if (aSizes.height) {
    180    MOZ_ASSERT(!aFlags.contains(PlaceFlag::DoNotAdjustForWidthAndHeight));
    181    MOZ_ASSERT(!aFlags.contains(PlaceFlag::IntrinsicSize));
    182    auto height = *aSizes.height;
    183    aReflowOutput.Height() = height;
    184  }
    185  return shiftX;
    186 }
    187 
    188 // helper to get the preferred size that a container frame should use to fire
    189 // the stretch on its stretchy child frames.
    190 void nsMathMLContainerFrame::GetPreferredStretchSize(
    191    DrawTarget* aDrawTarget, uint32_t aOptions,
    192    nsStretchDirection aStretchDirection,
    193    nsBoundingMetrics& aPreferredStretchSize) {
    194  if (aOptions & STRETCH_CONSIDER_ACTUAL_SIZE) {
    195    // when our actual size is ok, just use it
    196    aPreferredStretchSize = mBoundingMetrics;
    197  } else if (aOptions & STRETCH_CONSIDER_EMBELLISHMENTS) {
    198    // compute our up-to-date size using Place(), without border/padding.
    199    ReflowOutput reflowOutput(GetWritingMode());
    200    PlaceFlags flags(PlaceFlag::MeasureOnly, PlaceFlag::IgnoreBorderPadding);
    201    Place(aDrawTarget, flags, reflowOutput);
    202    aPreferredStretchSize = reflowOutput.mBoundingMetrics;
    203  } else {
    204    // compute a size that includes embellishments iff the container stretches
    205    // in the same direction as the embellished operator.
    206    bool stretchAll = mPresentationData.flags.contains(
    207        aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL
    208            ? MathMLPresentationFlag::StretchAllChildrenVertically
    209            : MathMLPresentationFlag::StretchAllChildrenHorizontally);
    210    NS_ASSERTION(aStretchDirection == NS_STRETCH_DIRECTION_HORIZONTAL ||
    211                     aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL,
    212                 "You must specify a direction in which to stretch");
    213    NS_ASSERTION(mEmbellishData.flags.contains(
    214                     MathMLEmbellishFlag::EmbellishedOperator) ||
    215                     stretchAll,
    216                 "invalid call to GetPreferredStretchSize");
    217    bool firstTime = true;
    218    nsBoundingMetrics bm, bmChild;
    219    nsIFrame* childFrame = stretchAll ? PrincipalChildList().FirstChild()
    220                                      : mPresentationData.baseFrame;
    221    while (childFrame) {
    222      // initializations in case this child happens not to be a MathML frame
    223      nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
    224      if (mathMLFrame) {
    225        nsEmbellishData embellishData;
    226        nsPresentationData presentationData;
    227        mathMLFrame->GetEmbellishData(embellishData);
    228        mathMLFrame->GetPresentationData(presentationData);
    229        if (embellishData.flags.contains(
    230                MathMLEmbellishFlag::EmbellishedOperator) &&
    231            embellishData.direction == aStretchDirection &&
    232            presentationData.baseFrame) {
    233          // embellishements are not included, only consider the inner first
    234          // child itself
    235          // XXXkt Does that mean the core descendent frame should be used
    236          // instead of the base child?
    237          nsIMathMLFrame* mathMLchildFrame =
    238              do_QueryFrame(presentationData.baseFrame);
    239          if (mathMLchildFrame) {
    240            mathMLFrame = mathMLchildFrame;
    241          }
    242        }
    243        mathMLFrame->GetBoundingMetrics(bmChild);
    244      } else {
    245        ReflowOutput unused(GetWritingMode());
    246        GetReflowAndBoundingMetricsFor(childFrame, unused, bmChild);
    247      }
    248 
    249      if (firstTime) {
    250        firstTime = false;
    251        bm = bmChild;
    252        if (!stretchAll) {
    253          // we may get here for cases such as <msup><mo>...</mo> ... </msup>,
    254          // or <maction>...<mo>...</mo></maction>.
    255          break;
    256        }
    257      } else {
    258        if (aStretchDirection == NS_STRETCH_DIRECTION_HORIZONTAL) {
    259          // if we get here, it means this is container that will stack its
    260          // children vertically and fire an horizontal stretch on each them.
    261          // This is the case for \munder, \mover, \munderover. We just sum-up
    262          // the size vertically.
    263          bm.descent += bmChild.ascent + bmChild.descent;
    264          // Sometimes non-spacing marks (when width is zero) are positioned
    265          // to the left of the origin, but it is the distance between left
    266          // and right bearing that is important rather than the offsets from
    267          // the origin.
    268          if (bmChild.width == 0) {
    269            bmChild.rightBearing -= bmChild.leftBearing;
    270            bmChild.leftBearing = 0;
    271          }
    272          if (bm.leftBearing > bmChild.leftBearing) {
    273            bm.leftBearing = bmChild.leftBearing;
    274          }
    275          if (bm.rightBearing < bmChild.rightBearing) {
    276            bm.rightBearing = bmChild.rightBearing;
    277          }
    278        } else if (aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) {
    279          // just sum-up the sizes horizontally.
    280          bm += bmChild;
    281        } else {
    282          NS_ERROR("unexpected case in GetPreferredStretchSize");
    283          break;
    284        }
    285      }
    286      childFrame = childFrame->GetNextSibling();
    287    }
    288    aPreferredStretchSize = bm;
    289  }
    290 }
    291 
    292 NS_IMETHODIMP
    293 nsMathMLContainerFrame::Stretch(DrawTarget* aDrawTarget,
    294                                nsStretchDirection aStretchDirection,
    295                                nsBoundingMetrics& aContainerSize,
    296                                ReflowOutput& aDesiredStretchSize) {
    297  if (mEmbellishData.flags.contains(MathMLEmbellishFlag::EmbellishedOperator)) {
    298    if (mPresentationData.flags.contains(MathMLPresentationFlag::StretchDone)) {
    299      NS_WARNING("it is wrong to fire stretch more than once on a frame");
    300      return NS_OK;
    301    }
    302    mPresentationData.flags += MathMLPresentationFlag::StretchDone;
    303 
    304    // Pass the stretch to the base child ...
    305 
    306    nsIFrame* baseFrame = mPresentationData.baseFrame;
    307    if (baseFrame) {
    308      nsIMathMLFrame* mathMLFrame = do_QueryFrame(baseFrame);
    309      NS_ASSERTION(mathMLFrame, "Something is wrong somewhere");
    310      if (mathMLFrame) {
    311        // And the trick is that the child's rect.x is still holding the
    312        // descent, and rect.y is still holding the ascent ...
    313        ReflowOutput childSize(aDesiredStretchSize);
    314        GetReflowAndBoundingMetricsFor(baseFrame, childSize,
    315                                       childSize.mBoundingMetrics);
    316 
    317        // See if we should downsize and confine the stretch to us...
    318        // XXX there may be other cases where we can downsize the stretch,
    319        // e.g., the first &Sum; might appear big in the following situation
    320        // <math xmlns='http://www.w3.org/1998/Math/MathML'>
    321        //   <mstyle>
    322        //     <msub>
    323        //        <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
    324        //        <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
    325        //      </msub>
    326        //   </mstyle>
    327        // </math>
    328        nsBoundingMetrics containerSize = aContainerSize;
    329        if (aStretchDirection != mEmbellishData.direction &&
    330            mEmbellishData.direction != NS_STRETCH_DIRECTION_UNSUPPORTED) {
    331          NS_ASSERTION(
    332              mEmbellishData.direction != NS_STRETCH_DIRECTION_DEFAULT,
    333              "Stretches may have a default direction, operators can not.");
    334          if (mPresentationData.flags.contains(
    335                  mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL
    336                      ? MathMLPresentationFlag::StretchAllChildrenVertically
    337                      : MathMLPresentationFlag::
    338                            StretchAllChildrenHorizontally)) {
    339            GetPreferredStretchSize(aDrawTarget, 0, mEmbellishData.direction,
    340                                    containerSize);
    341            // Stop further recalculations
    342            aStretchDirection = mEmbellishData.direction;
    343          } else {
    344            // We aren't going to stretch the child, so just use the child
    345            // metrics.
    346            containerSize = childSize.mBoundingMetrics;
    347          }
    348        }
    349 
    350        // do the stretching...
    351        mathMLFrame->Stretch(aDrawTarget, aStretchDirection, containerSize,
    352                             childSize);
    353        // store the updated metrics
    354        SaveReflowAndBoundingMetricsFor(baseFrame, childSize,
    355                                        childSize.mBoundingMetrics);
    356 
    357        // Remember the siblings which were _deferred_.
    358        // Now that this embellished child may have changed, we need to
    359        // fire the stretch on its siblings using our updated size
    360 
    361        if (mPresentationData.flags.contains(
    362                MathMLPresentationFlag::StretchAllChildrenVertically) ||
    363            mPresentationData.flags.contains(
    364                MathMLPresentationFlag::StretchAllChildrenHorizontally)) {
    365          nsStretchDirection stretchDir =
    366              mPresentationData.flags.contains(
    367                  MathMLPresentationFlag::StretchAllChildrenVertically)
    368                  ? NS_STRETCH_DIRECTION_VERTICAL
    369                  : NS_STRETCH_DIRECTION_HORIZONTAL;
    370 
    371          GetPreferredStretchSize(aDrawTarget, STRETCH_CONSIDER_EMBELLISHMENTS,
    372                                  stretchDir, containerSize);
    373 
    374          nsIFrame* childFrame = mFrames.FirstChild();
    375          while (childFrame) {
    376            if (childFrame != mPresentationData.baseFrame) {
    377              mathMLFrame = do_QueryFrame(childFrame);
    378              if (mathMLFrame) {
    379                // retrieve the metrics that was stored at the previous pass
    380                GetReflowAndBoundingMetricsFor(childFrame, childSize,
    381                                               childSize.mBoundingMetrics);
    382                // do the stretching...
    383                mathMLFrame->Stretch(aDrawTarget, stretchDir, containerSize,
    384                                     childSize);
    385                // store the updated metrics
    386                SaveReflowAndBoundingMetricsFor(childFrame, childSize,
    387                                                childSize.mBoundingMetrics);
    388              }
    389            }
    390            childFrame = childFrame->GetNextSibling();
    391          }
    392        }
    393 
    394        // re-position all our children
    395        PlaceFlags flags;
    396        Place(aDrawTarget, flags, aDesiredStretchSize);
    397 
    398        // If our parent is not embellished, it means we are the outermost
    399        // embellished container and so we put the spacing, otherwise we don't
    400        // include the spacing, the outermost embellished container will take
    401        // care of it.
    402 
    403        nsEmbellishData parentData;
    404        GetEmbellishDataFrom(GetParent(), parentData);
    405        // ensure that we are the embellished child, not just a sibling
    406        // (need to test coreFrame since <mfrac> resets other things)
    407        if (parentData.coreFrame != mEmbellishData.coreFrame) {
    408          // (we fetch values from the core since they may use units that depend
    409          // on style data, and style changes could have occurred in the core
    410          // since our last visit there)
    411          nsEmbellishData coreData;
    412          GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
    413 
    414          mBoundingMetrics.width +=
    415              coreData.leadingSpace + coreData.trailingSpace;
    416          aDesiredStretchSize.Width() = mBoundingMetrics.width;
    417          aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
    418 
    419          nscoord dx = StyleVisibility()->mDirection == StyleDirection::Rtl
    420                           ? coreData.trailingSpace
    421                           : coreData.leadingSpace;
    422          if (dx != 0) {
    423            mBoundingMetrics.leftBearing += dx;
    424            mBoundingMetrics.rightBearing += dx;
    425            aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
    426            aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
    427 
    428            nsIFrame* childFrame = mFrames.FirstChild();
    429            while (childFrame) {
    430              childFrame->SetPosition(childFrame->GetPosition() +
    431                                      nsPoint(dx, 0));
    432              childFrame = childFrame->GetNextSibling();
    433            }
    434          }
    435        }
    436 
    437        // Finished with these:
    438        ClearSavedChildMetrics();
    439        // Set our overflow area
    440        GatherAndStoreOverflow(&aDesiredStretchSize);
    441      }
    442    }
    443  }
    444  return NS_OK;
    445 }
    446 
    447 nsresult nsMathMLContainerFrame::FinalizeReflow(DrawTarget* aDrawTarget,
    448                                                ReflowOutput& aDesiredSize) {
    449  // During reflow, we use rect.x and rect.y as placeholders for the child's
    450  // ascent and descent in expectation of a stretch command. Hence we need to
    451  // ensure that a stretch command will actually be fired later on, after
    452  // exiting from our reflow. If the stretch is not fired, the rect.x, and
    453  // rect.y will remain with inappropriate data causing children to be
    454  // improperly positioned. This helper method checks to see if our parent will
    455  // fire a stretch command targeted at us. If not, we go ahead and fire an
    456  // involutive stretch on ourselves. This will clear all the rect.x and rect.y,
    457  // and return our desired size.
    458 
    459  // First, complete the post-reflow hook.
    460  // We use the information in our children rectangles to position them.
    461  // If placeOrigin==false, then Place() will not touch rect.x, and rect.y.
    462  // They will still be holding the ascent and descent for each child.
    463 
    464  // The first clause caters for any non-embellished container.
    465  // The second clause is for a container which won't fire stretch even though
    466  // it is embellished, e.g., as in <mfrac><mo>...</mo> ... </mfrac>, the test
    467  // is convoluted because it excludes the particular case of the core
    468  // <mo>...</mo> itself.
    469  // (<mo> needs to fire stretch on its MathMLChar in any case to initialize it)
    470  bool placeOrigin =
    471      !mEmbellishData.flags.contains(
    472          MathMLEmbellishFlag::EmbellishedOperator) ||
    473      (mEmbellishData.coreFrame != this && !mPresentationData.baseFrame &&
    474       mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED);
    475  PlaceFlags flags;
    476  if (!placeOrigin) {
    477    flags += PlaceFlag::MeasureOnly;
    478  }
    479  Place(aDrawTarget, flags, aDesiredSize);
    480 
    481  bool parentWillFireStretch = false;
    482  if (!placeOrigin) {
    483    // This means the rect.x and rect.y of our children were not set!!
    484    // Don't go without checking to see if our parent will later fire a
    485    // Stretch() command targeted at us. The Stretch() will cause the rect.x and
    486    // rect.y to clear...
    487    nsIMathMLFrame* mathMLFrame = do_QueryFrame(GetParent());
    488    if (mathMLFrame) {
    489      nsEmbellishData embellishData;
    490      nsPresentationData presentationData;
    491      mathMLFrame->GetEmbellishData(embellishData);
    492      mathMLFrame->GetPresentationData(presentationData);
    493      if (presentationData.flags.contains(
    494              MathMLPresentationFlag::StretchAllChildrenVertically) ||
    495          presentationData.flags.contains(
    496              MathMLPresentationFlag::StretchAllChildrenHorizontally) ||
    497          (embellishData.flags.contains(
    498               MathMLEmbellishFlag::EmbellishedOperator) &&
    499           presentationData.baseFrame == this)) {
    500        parentWillFireStretch = true;
    501      }
    502    }
    503    if (!parentWillFireStretch) {
    504      // There is nobody who will fire the stretch for us, we do it ourselves!
    505 
    506      bool stretchAll =
    507          /* mPresentationData.flags.contains(MathMLPresentationFlag::StretchAllChildrenVertically)
    508             || */
    509          mPresentationData.flags.contains(
    510              MathMLPresentationFlag::StretchAllChildrenHorizontally);
    511 
    512      nsStretchDirection stretchDir;
    513      if (mEmbellishData.coreFrame ==
    514              this || /* case of a bare <mo>...</mo> itself */
    515          (mEmbellishData.direction == NS_STRETCH_DIRECTION_HORIZONTAL &&
    516           stretchAll) || /* or <mover><mo>...</mo>...</mover>, or friends */
    517          mEmbellishData.direction ==
    518              NS_STRETCH_DIRECTION_UNSUPPORTED) { /* Doesn't stretch */
    519        stretchDir = mEmbellishData.direction;
    520      } else {
    521        // Let the Stretch() call decide the direction.
    522        stretchDir = NS_STRETCH_DIRECTION_DEFAULT;
    523      }
    524      // Use our current size as computed earlier by Place()
    525      // The stretch call will detect if this is incorrect and recalculate the
    526      // size.
    527      nsBoundingMetrics defaultSize = aDesiredSize.mBoundingMetrics;
    528 
    529      Stretch(aDrawTarget, stretchDir, defaultSize, aDesiredSize);
    530 #ifdef DEBUG
    531      {
    532        // The Place() call above didn't request FinishReflowChild(),
    533        // so let's check that we eventually did through Stretch().
    534        for (nsIFrame* childFrame : PrincipalChildList()) {
    535          NS_ASSERTION(!childFrame->HasAnyStateBits(NS_FRAME_IN_REFLOW),
    536                       "DidReflow() was never called");
    537        }
    538      }
    539 #endif
    540    }
    541  }
    542 
    543  // Also return our bounding metrics
    544  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
    545 
    546  // see if we should fix the spacing
    547  FixInterFrameSpacing(aDesiredSize);
    548 
    549  if (!parentWillFireStretch) {
    550    // Not expecting a stretch.
    551    // Finished with these:
    552    ClearSavedChildMetrics();
    553    // Set our overflow area.
    554    GatherAndStoreOverflow(&aDesiredSize);
    555  }
    556 
    557  mPresentationData.flags -= MathMLPresentationFlag::StretchDone;
    558  return NS_OK;
    559 }
    560 
    561 /* /////////////
    562 * nsIMathMLFrame - support methods for scripting elements (nested frames
    563 * within msub, msup, msubsup, munder, mover, munderover, mmultiscripts,
    564 * mfrac, mroot, mtable).
    565 * =============================================================================
    566 */
    567 
    568 // helper to let the update of presentation data pass through
    569 // a subtree that may contain non-mathml container frames
    570 /* static */
    571 void nsMathMLContainerFrame::PropagatePresentationDataFor(
    572    nsIFrame* aFrame, MathMLPresentationFlags aFlagsValues,
    573    MathMLPresentationFlags aFlagsToUpdate) {
    574  if (!aFrame || aFlagsToUpdate.isEmpty()) {
    575    return;
    576  }
    577  nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
    578  if (mathMLFrame) {
    579    // update
    580    mathMLFrame->UpdatePresentationData(aFlagsValues, aFlagsToUpdate);
    581    // propagate using the base method to make sure that the control
    582    // is passed on to MathML frames that may be overloading the method
    583    mathMLFrame->UpdatePresentationDataFromChildAt(0, -1, aFlagsValues,
    584                                                   aFlagsToUpdate);
    585  } else {
    586    // propagate down the subtrees
    587    for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
    588      PropagatePresentationDataFor(childFrame, aFlagsValues, aFlagsToUpdate);
    589    }
    590  }
    591 }
    592 
    593 /* static */
    594 void nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(
    595    nsIFrame* aParentFrame, int32_t aFirstChildIndex, int32_t aLastChildIndex,
    596    MathMLPresentationFlags aFlagsValues,
    597    MathMLPresentationFlags aFlagsToUpdate) {
    598  if (!aParentFrame || aFlagsToUpdate.isEmpty()) {
    599    return;
    600  }
    601  int32_t index = 0;
    602  for (nsIFrame* childFrame : aParentFrame->PrincipalChildList()) {
    603    if ((index >= aFirstChildIndex) &&
    604        ((aLastChildIndex <= 0) ||
    605         ((aLastChildIndex > 0) && (index <= aLastChildIndex)))) {
    606      PropagatePresentationDataFor(childFrame, aFlagsValues, aFlagsToUpdate);
    607    }
    608    index++;
    609  }
    610 }
    611 
    612 /* //////////////////
    613 * Frame construction
    614 * =============================================================================
    615 */
    616 
    617 void nsMathMLContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
    618                                              const nsDisplayListSet& aLists) {
    619  BuildDisplayListForInline(aBuilder, aLists);
    620 }
    621 
    622 // Note that this method re-builds the automatic data in the children -- not
    623 // in aParentFrame itself (except for those particular operations that the
    624 // parent frame may do in its TransmitAutomaticData()).
    625 /* static */
    626 void nsMathMLContainerFrame::RebuildAutomaticDataForChildren(
    627    nsIFrame* aParentFrame) {
    628  // 1. As we descend the tree, make each child frame inherit data from
    629  // the parent
    630  // 2. As we ascend the tree, transmit any specific change that we want
    631  // down the subtrees
    632  for (nsIFrame* childFrame : aParentFrame->PrincipalChildList()) {
    633    nsIMathMLFrame* childMathMLFrame = do_QueryFrame(childFrame);
    634    if (childMathMLFrame) {
    635      childMathMLFrame->InheritAutomaticData(aParentFrame);
    636    }
    637    RebuildAutomaticDataForChildren(childFrame);
    638  }
    639  nsIMathMLFrame* mathMLFrame = do_QueryFrame(aParentFrame);
    640  if (mathMLFrame) {
    641    mathMLFrame->TransmitAutomaticData();
    642  }
    643 }
    644 
    645 /* static */
    646 nsresult nsMathMLContainerFrame::ReLayoutChildren(nsIFrame* aParentFrame) {
    647  if (!aParentFrame) {
    648    return NS_OK;
    649  }
    650 
    651  // walk-up to the first frame that is a MathML frame, stop if we reach <math>
    652  nsIFrame* frame = aParentFrame;
    653  while (1) {
    654    nsIFrame* parent = frame->GetParent();
    655    if (!parent || !parent->GetContent()) {
    656      break;
    657    }
    658 
    659    // stop if it is a MathML frame
    660    nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
    661    if (mathMLFrame) {
    662      break;
    663    }
    664 
    665    // stop if we reach the root <math> tag
    666    nsIContent* content = frame->GetContent();
    667    NS_ASSERTION(content, "dangling frame without a content node");
    668    if (!content) {
    669      break;
    670    }
    671    if (content->IsMathMLElement(nsGkAtoms::math)) {
    672      break;
    673    }
    674 
    675    frame = parent;
    676  }
    677 
    678  // re-sync the presentation data and embellishment data of our children
    679  RebuildAutomaticDataForChildren(frame);
    680 
    681  // Ask our parent frame to reflow us
    682  nsIFrame* parent = frame->GetParent();
    683  NS_ASSERTION(parent, "No parent to pass the reflow request up to");
    684  if (!parent) {
    685    return NS_OK;
    686  }
    687 
    688  frame->PresShell()->FrameNeedsReflow(
    689      frame, IntrinsicDirty::FrameAncestorsAndDescendants, NS_FRAME_IS_DIRTY);
    690 
    691  return NS_OK;
    692 }
    693 
    694 // There are precise rules governing children of a MathML frame,
    695 // and properties such as the scriptlevel depends on those rules.
    696 // Hence for things to work, callers must use Append/Insert/etc wisely.
    697 
    698 nsresult nsMathMLContainerFrame::ChildListChanged() {
    699  // If this is an embellished frame we need to rebuild the
    700  // embellished hierarchy by walking-up to the parent of the
    701  // outermost embellished container.
    702  nsIFrame* frame = this;
    703  if (mEmbellishData.coreFrame) {
    704    nsIFrame* parent = GetParent();
    705    nsEmbellishData embellishData;
    706    for (; parent; frame = parent, parent = parent->GetParent()) {
    707      GetEmbellishDataFrom(parent, embellishData);
    708      if (embellishData.coreFrame != mEmbellishData.coreFrame) {
    709        break;
    710      }
    711    }
    712  }
    713  return ReLayoutChildren(frame);
    714 }
    715 
    716 void nsMathMLContainerFrame::AppendFrames(ChildListID aListID,
    717                                          nsFrameList&& aFrameList) {
    718  MOZ_ASSERT(aListID == FrameChildListID::Principal);
    719  mFrames.AppendFrames(this, std::move(aFrameList));
    720  ChildListChanged();
    721 }
    722 
    723 void nsMathMLContainerFrame::InsertFrames(
    724    ChildListID aListID, nsIFrame* aPrevFrame,
    725    const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) {
    726  MOZ_ASSERT(aListID == FrameChildListID::Principal);
    727  mFrames.InsertFrames(this, aPrevFrame, std::move(aFrameList));
    728  ChildListChanged();
    729 }
    730 
    731 void nsMathMLContainerFrame::RemoveFrame(DestroyContext& aContext,
    732                                         ChildListID aListID,
    733                                         nsIFrame* aOldFrame) {
    734  MOZ_ASSERT(aListID == FrameChildListID::Principal);
    735  mFrames.DestroyFrame(aContext, aOldFrame);
    736  ChildListChanged();
    737 }
    738 
    739 void nsMathMLContainerFrame::GatherAndStoreOverflow(ReflowOutput* aMetrics) {
    740  mBlockStartAscent = aMetrics->BlockStartAscent();
    741 
    742  // nsIFrame::FinishAndStoreOverflow likes the overflow area to include the
    743  // frame rectangle.
    744  aMetrics->SetOverflowAreasToDesiredBounds();
    745 
    746  ComputeCustomOverflow(aMetrics->mOverflowAreas);
    747 
    748  // mBoundingMetrics does not necessarily include content of <mpadded>
    749  // elements whose mBoundingMetrics may not be representative of the true
    750  // bounds, and doesn't include the CSS2 outline rectangles of children, so
    751  // make such to include child overflow areas.
    752  UnionChildOverflow(aMetrics->mOverflowAreas);
    753 
    754  FinishAndStoreOverflow(aMetrics);
    755 }
    756 
    757 bool nsMathMLContainerFrame::ComputeCustomOverflow(
    758    OverflowAreas& aOverflowAreas) {
    759  // All non-child-frame content such as nsMathMLChars (and most child-frame
    760  // content) is included in mBoundingMetrics.
    761  nsRect boundingBox(
    762      mBoundingMetrics.leftBearing, mBlockStartAscent - mBoundingMetrics.ascent,
    763      mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing,
    764      mBoundingMetrics.ascent + mBoundingMetrics.descent);
    765 
    766  // REVIEW: Maybe this should contribute only to ink overflow
    767  // and not scrollable?
    768  aOverflowAreas.UnionAllWith(boundingBox);
    769  return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
    770 }
    771 
    772 void nsMathMLContainerFrame::ReflowChild(nsIFrame* aChildFrame,
    773                                         nsPresContext* aPresContext,
    774                                         ReflowOutput& aDesiredSize,
    775                                         const ReflowInput& aReflowInput,
    776                                         nsReflowStatus& aStatus) {
    777  // Having foreign/hybrid children, e.g., from html markups, is not defined by
    778  // the MathML spec. But it can happen in practice, e.g., <html:img> allows us
    779  // to do some cool demos... or we may have a child that is an nsInlineFrame
    780  // from a generated content such as :before { content: open-quote } or
    781  // :after { content: close-quote }. Unfortunately, the other frames out-there
    782  // may expect their own invariants that are not met when we mix things.
    783  // Hence we do not claim their support, but we will nevertheless attempt to
    784  // keep them in the flow, if we can get their desired size. We observed that
    785  // most frames may be reflowed generically, but nsInlineFrames need extra
    786  // care.
    787 
    788 #ifdef DEBUG
    789  nsInlineFrame* inlineFrame = do_QueryFrame(aChildFrame);
    790  NS_ASSERTION(!inlineFrame, "Inline frames should be wrapped in blocks");
    791 #endif
    792 
    793  nsContainerFrame::ReflowChild(aChildFrame, aPresContext, aDesiredSize,
    794                                aReflowInput, 0, 0,
    795                                ReflowChildFlags::NoMoveFrame, aStatus);
    796 
    797  if (aDesiredSize.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
    798    // This will be suitable for inline frames, which are wrapped in a block.
    799    nscoord ascent;
    800    WritingMode wm = aDesiredSize.GetWritingMode();
    801    if (!nsLayoutUtils::GetLastLineBaseline(wm, aChildFrame, &ascent)) {
    802      // We don't expect any other block children so just place the frame on
    803      // the baseline instead of going through DidReflow() and
    804      // GetBaseline().  This is what nsIFrame::GetBaseline() will do anyway.
    805      aDesiredSize.SetBlockStartAscent(aDesiredSize.BSize(wm));
    806    } else {
    807      aDesiredSize.SetBlockStartAscent(ascent);
    808    }
    809  }
    810  if (IsForeignChild(aChildFrame)) {
    811    // use ComputeTightBounds API as aDesiredSize.mBoundingMetrics is not set.
    812    nsRect r = aChildFrame->ComputeTightBounds(
    813        aReflowInput.mRenderingContext->GetDrawTarget());
    814    aDesiredSize.mBoundingMetrics.leftBearing = r.x;
    815    aDesiredSize.mBoundingMetrics.rightBearing = r.XMost();
    816    aDesiredSize.mBoundingMetrics.ascent =
    817        aDesiredSize.BlockStartAscent() - r.y;
    818    aDesiredSize.mBoundingMetrics.descent =
    819        r.YMost() - aDesiredSize.BlockStartAscent();
    820    aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width();
    821  }
    822 }
    823 
    824 void nsMathMLContainerFrame::Reflow(nsPresContext* aPresContext,
    825                                    ReflowOutput& aDesiredSize,
    826                                    const ReflowInput& aReflowInput,
    827                                    nsReflowStatus& aStatus) {
    828  if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
    829    return;
    830  }
    831 
    832  MarkInReflow();
    833  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
    834 
    835  aDesiredSize.Width() = aDesiredSize.Height() = 0;
    836  aDesiredSize.SetBlockStartAscent(0);
    837  aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
    838 
    839  /////////////
    840  // Reflow children
    841  // Asking each child to cache its bounding metrics
    842 
    843  nsReflowStatus childStatus;
    844  nsIFrame* childFrame = mFrames.FirstChild();
    845  while (childFrame) {
    846    ReflowOutput childDesiredSize(aReflowInput);
    847    WritingMode wm = childFrame->GetWritingMode();
    848    LogicalSize availSize = aReflowInput.ComputedSize(wm);
    849    availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
    850    ReflowInput childReflowInput(aPresContext, aReflowInput, childFrame,
    851                                 availSize);
    852    ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowInput,
    853                childStatus);
    854    // NS_ASSERTION(childStatus.IsComplete(), "bad status");
    855    SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
    856                                    childDesiredSize.mBoundingMetrics);
    857    childFrame = childFrame->GetNextSibling();
    858  }
    859 
    860  /////////////
    861  // If we are a container which is entitled to stretch its children, then we
    862  // ask our stretchy children to stretch themselves
    863 
    864  // The stretching of siblings of an embellished child is _deferred_ until
    865  // after finishing the stretching of the embellished child - bug 117652
    866 
    867  DrawTarget* drawTarget = aReflowInput.mRenderingContext->GetDrawTarget();
    868 
    869  if (!mEmbellishData.flags.contains(
    870          MathMLEmbellishFlag::EmbellishedOperator) &&
    871      (mPresentationData.flags.contains(
    872           MathMLPresentationFlag::StretchAllChildrenVertically) ||
    873       mPresentationData.flags.contains(
    874           MathMLPresentationFlag::StretchAllChildrenHorizontally))) {
    875    // get the stretchy direction
    876    nsStretchDirection stretchDir =
    877        mPresentationData.flags.contains(
    878            MathMLPresentationFlag::StretchAllChildrenVertically)
    879            ? NS_STRETCH_DIRECTION_VERTICAL
    880            : NS_STRETCH_DIRECTION_HORIZONTAL;
    881 
    882    // what size should we use to stretch our stretchy children
    883    // We don't use STRETCH_CONSIDER_ACTUAL_SIZE -- because our size is not
    884    // known yet We don't use STRETCH_CONSIDER_EMBELLISHMENTS -- because we
    885    // don't want to include them in the caculations of the size of stretchy
    886    // elements
    887    nsBoundingMetrics containerSize;
    888    GetPreferredStretchSize(drawTarget, 0, stretchDir, containerSize);
    889 
    890    // fire the stretch on each child
    891    childFrame = mFrames.FirstChild();
    892    while (childFrame) {
    893      nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
    894      if (mathMLFrame) {
    895        // retrieve the metrics that was stored at the previous pass
    896        ReflowOutput childDesiredSize(aReflowInput);
    897        GetReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
    898                                       childDesiredSize.mBoundingMetrics);
    899 
    900        mathMLFrame->Stretch(drawTarget, stretchDir, containerSize,
    901                             childDesiredSize);
    902        // store the updated metrics
    903        SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
    904                                        childDesiredSize.mBoundingMetrics);
    905      }
    906      childFrame = childFrame->GetNextSibling();
    907    }
    908  }
    909 
    910  /////////////
    911  // Place children now by re-adjusting the origins to align the baselines
    912  FinalizeReflow(drawTarget, aDesiredSize);
    913 }
    914 
    915 static nscoord AddInterFrameSpacingToSize(ReflowOutput& aDesiredSize,
    916                                          nsMathMLContainerFrame* aFrame);
    917 
    918 /* virtual */
    919 void nsMathMLContainerFrame::MarkIntrinsicISizesDirty() {
    920  mIntrinsicISize = NS_INTRINSIC_ISIZE_UNKNOWN;
    921  nsContainerFrame::MarkIntrinsicISizesDirty();
    922 }
    923 
    924 void nsMathMLContainerFrame::UpdateIntrinsicISize(
    925    gfxContext* aRenderingContext) {
    926  if (mIntrinsicISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
    927    ReflowOutput desiredSize(GetWritingMode());
    928    GetIntrinsicISizeMetrics(aRenderingContext, desiredSize);
    929 
    930    // Include the additional width added by FixInterFrameSpacing to ensure
    931    // consistent width calculations.
    932    AddInterFrameSpacingToSize(desiredSize, this);
    933 
    934    // ReflowOuput::mSize corresponds to the border box, but callers
    935    // expect padding/border are not included.
    936    mIntrinsicISize = desiredSize.ISize(GetWritingMode()) -
    937                      IntrinsicISizeOffsets().BorderPadding();
    938  }
    939 }
    940 
    941 nscoord nsMathMLContainerFrame::IntrinsicISize(const IntrinsicSizeInput& aInput,
    942                                               IntrinsicISizeType aType) {
    943  UpdateIntrinsicISize(aInput.mContext);
    944  return mIntrinsicISize;
    945 }
    946 
    947 /* virtual */
    948 void nsMathMLContainerFrame::GetIntrinsicISizeMetrics(
    949    gfxContext* aRenderingContext, ReflowOutput& aDesiredSize) {
    950  // Get child widths
    951  nsIFrame* childFrame = mFrames.FirstChild();
    952  while (childFrame) {
    953    ReflowOutput childDesiredSize(GetWritingMode());  // ???
    954 
    955    nsMathMLContainerFrame* containerFrame = do_QueryFrame(childFrame);
    956    if (containerFrame) {
    957      containerFrame->GetIntrinsicISizeMetrics(aRenderingContext,
    958                                               childDesiredSize);
    959    } else {
    960      nscoord width = nsLayoutUtils::IntrinsicForContainer(
    961          aRenderingContext, childFrame, IntrinsicISizeType::PrefISize);
    962 
    963      childDesiredSize.Width() = width;
    964      childDesiredSize.mBoundingMetrics.width = width;
    965      childDesiredSize.mBoundingMetrics.leftBearing = 0;
    966      childDesiredSize.mBoundingMetrics.rightBearing = width;
    967 
    968      nscoord x, xMost;
    969      if (NS_SUCCEEDED(childFrame->GetPrefWidthTightBounds(aRenderingContext,
    970                                                           &x, &xMost))) {
    971        childDesiredSize.mBoundingMetrics.leftBearing = x;
    972        childDesiredSize.mBoundingMetrics.rightBearing = xMost;
    973      }
    974    }
    975 
    976    SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
    977                                    childDesiredSize.mBoundingMetrics);
    978 
    979    childFrame = childFrame->GetNextSibling();
    980  }
    981 
    982  // Measure
    983  PlaceFlags flags(PlaceFlag::IntrinsicSize, PlaceFlag::MeasureOnly);
    984  Place(aRenderingContext->GetDrawTarget(), flags, aDesiredSize);
    985 
    986  ClearSavedChildMetrics();
    987 }
    988 
    989 // see spacing table in Chapter 18, TeXBook (p.170)
    990 // Our table isn't quite identical to TeX because operators have
    991 // built-in values for lspace & rspace in the Operator Dictionary.
    992 static int32_t kInterFrameSpacingTable[size_t(MathMLFrameType::Count)][size_t(
    993    MathMLFrameType::Count)] = {
    994    // in units of muspace.
    995    // upper half of the byte is set if the
    996    // spacing is not to be used for scriptlevel > 0
    997 
    998    /*           Ord  OpOrd OpInv OpUsr Inner Italic Upright */
    999    /*Ord    */ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00},
   1000    /*OpOrd  */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
   1001    /*OpInv  */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
   1002    /*OpUsr  */ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
   1003    /*Inner  */ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
   1004    /*Italic */ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01},
   1005    /*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00}};
   1006 
   1007 #define GET_INTERSPACE(scriptlevel_, frametype1_, frametype2_, space_)     \
   1008  /* no space if there is a frame that we know nothing about */            \
   1009  if (frametype1_ == MathMLFrameType::Unknown ||                           \
   1010      frametype2_ == MathMLFrameType::Unknown)                             \
   1011    space_ = 0;                                                            \
   1012  else {                                                                   \
   1013    space_ =                                                               \
   1014        kInterFrameSpacingTable[size_t(frametype1_)][size_t(frametype2_)]; \
   1015    space_ = (scriptlevel_ > 0 && (space_ & 0xF0))                         \
   1016                 ? 0 /* spacing is disabled */                             \
   1017                 : space_ & 0x0F;                                          \
   1018  }
   1019 
   1020 // This function computes the inter-space between two frames. However,
   1021 // since invisible operators need special treatment, the inter-space may
   1022 // be delayed when an invisible operator is encountered. In this case,
   1023 // the function will carry the inter-space forward until it is determined
   1024 // that it can be applied properly (i.e., until we encounter a visible
   1025 // frame where to decide whether to accept or reject the inter-space).
   1026 // aFromFrameType: remembers the frame when the carry-forward initiated.
   1027 // aCarrySpace: keeps track of the inter-space that is delayed.
   1028 // @returns: current inter-space (which is 0 when the true inter-space is
   1029 // delayed -- and thus has no effect since the frame is invisible anyway).
   1030 static nscoord GetInterFrameSpacing(int32_t aScriptLevel,
   1031                                    MathMLFrameType aFirstFrameType,
   1032                                    MathMLFrameType aSecondFrameType,
   1033                                    MathMLFrameType* aFromFrameType,  // IN/OUT
   1034                                    int32_t* aCarrySpace)             // IN/OUT
   1035 {
   1036  MathMLFrameType firstType = aFirstFrameType;
   1037  MathMLFrameType secondType = aSecondFrameType;
   1038 
   1039  int32_t space;
   1040  GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
   1041 
   1042  // feedback control to avoid the inter-space to be added when not necessary
   1043  if (secondType == MathMLFrameType::OperatorInvisible) {
   1044    // see if we should start to carry the space forward until we
   1045    // encounter a visible frame
   1046    if (*aFromFrameType == MathMLFrameType::Unknown) {
   1047      *aFromFrameType = firstType;
   1048      *aCarrySpace = space;
   1049    }
   1050    // keep carrying *aCarrySpace forward, while returning 0 for this stage
   1051    space = 0;
   1052  } else if (*aFromFrameType != MathMLFrameType::Unknown) {
   1053    // no carry-forward anymore, get the real inter-space between
   1054    // the two frames of interest
   1055 
   1056    firstType = *aFromFrameType;
   1057 
   1058    // But... the invisible operator that we encountered earlier could
   1059    // be sitting between italic and upright identifiers, e.g.,
   1060    //
   1061    // 1. <mi>sin</mi> <mo>&ApplyFunction;</mo> <mi>x</mi>
   1062    // 2. <mi>x</mi> <mo>&InvisibileTime;</mo> <mi>sin</mi>
   1063    //
   1064    // the trick to get the inter-space in either situation
   1065    // is to promote "<mi>sin</mi><mo>&ApplyFunction;</mo>" and
   1066    // "<mo>&InvisibileTime;</mo><mi>sin</mi>" to user-defined operators...
   1067    if (firstType == MathMLFrameType::UprightIdentifier) {
   1068      firstType = MathMLFrameType::OperatorUserDefined;
   1069    } else if (secondType == MathMLFrameType::UprightIdentifier) {
   1070      secondType = MathMLFrameType::OperatorUserDefined;
   1071    }
   1072 
   1073    GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
   1074 
   1075    // Now, we have two values: the computed space and the space that
   1076    // has been carried forward until now. Which value do we pick?
   1077    // If the second type is an operator (e.g., fence), it already has
   1078    // built-in lspace & rspace, so we let them win. Otherwise we pick
   1079    // the max between the two values that we have.
   1080    if (secondType != MathMLFrameType::OperatorOrdinary &&
   1081        space < *aCarrySpace) {
   1082      space = *aCarrySpace;
   1083    }
   1084 
   1085    // reset everything now that the carry-forward is done
   1086    *aFromFrameType = MathMLFrameType::Unknown;
   1087    *aCarrySpace = 0;
   1088  }
   1089 
   1090  return space;
   1091 }
   1092 
   1093 static nscoord GetThinSpace(const nsStyleFont* aStyleFont) {
   1094  return aStyleFont->mFont.size.ScaledBy(3.0f / 18.0f).ToAppUnits();
   1095 }
   1096 
   1097 class nsMathMLContainerFrame::RowChildFrameIterator {
   1098 public:
   1099  explicit RowChildFrameIterator(nsMathMLContainerFrame* aParentFrame,
   1100                                 const PlaceFlags& aFlags)
   1101      : mParentFrame(aParentFrame),
   1102        mReflowOutput(aParentFrame->GetWritingMode()),
   1103        mX(0),
   1104        mFlags(aFlags),
   1105        mChildFrameType(MathMLFrameType::Unknown),
   1106        mCarrySpace(0),
   1107        mFromFrameType(MathMLFrameType::Unknown),
   1108        mRTL(aParentFrame->StyleVisibility()->mDirection ==
   1109             StyleDirection::Rtl) {
   1110    if (!mRTL) {
   1111      mChildFrame = aParentFrame->mFrames.FirstChild();
   1112    } else {
   1113      mChildFrame = aParentFrame->mFrames.LastChild();
   1114    }
   1115 
   1116    if (!mChildFrame) {
   1117      return;
   1118    }
   1119 
   1120    InitMetricsForChild();
   1121  }
   1122 
   1123  RowChildFrameIterator& operator++() {
   1124    // add child size + italic correction
   1125    mX += mReflowOutput.mBoundingMetrics.width + mItalicCorrection;
   1126    mX += mMargin.LeftRight();
   1127 
   1128    if (!mRTL) {
   1129      mChildFrame = mChildFrame->GetNextSibling();
   1130    } else {
   1131      mChildFrame = mChildFrame->GetPrevSibling();
   1132    }
   1133 
   1134    if (!mChildFrame) {
   1135      return *this;
   1136    }
   1137 
   1138    MathMLFrameType prevFrameType = mChildFrameType;
   1139    InitMetricsForChild();
   1140 
   1141    // add inter frame spacing
   1142    const nsStyleFont* font = mParentFrame->StyleFont();
   1143    nscoord space =
   1144        GetInterFrameSpacing(font->mMathDepth, prevFrameType, mChildFrameType,
   1145                             &mFromFrameType, &mCarrySpace);
   1146    mX += space * GetThinSpace(font);
   1147    return *this;
   1148  }
   1149 
   1150  nsIFrame* Frame() const { return mChildFrame; }
   1151  nscoord X() const { return mX; }
   1152  const ReflowOutput& GetReflowOutput() const { return mReflowOutput; }
   1153  nscoord Ascent() const { return mReflowOutput.BlockStartAscent(); }
   1154  nscoord Descent() const {
   1155    return mReflowOutput.Height() - mReflowOutput.BlockStartAscent();
   1156  }
   1157  const nsMargin& Margin() const { return mMargin; }
   1158  const nsBoundingMetrics& BoundingMetrics() const {
   1159    return mReflowOutput.mBoundingMetrics;
   1160  }
   1161 
   1162 private:
   1163  const nsMathMLContainerFrame* mParentFrame;
   1164  nsIFrame* mChildFrame;
   1165  ReflowOutput mReflowOutput;
   1166  nscoord mX;
   1167  const PlaceFlags mFlags;
   1168  nsMargin mMargin;
   1169 
   1170  nscoord mItalicCorrection;
   1171  MathMLFrameType mChildFrameType;
   1172  int32_t mCarrySpace;
   1173  MathMLFrameType mFromFrameType;
   1174 
   1175  bool mRTL;
   1176 
   1177  void InitMetricsForChild() {
   1178    GetReflowAndBoundingMetricsFor(mChildFrame, mReflowOutput,
   1179                                   mReflowOutput.mBoundingMetrics,
   1180                                   &mChildFrameType);
   1181    mMargin = GetMarginForPlace(mFlags, mChildFrame);
   1182    nscoord leftCorrection, rightCorrection;
   1183    GetItalicCorrection(mReflowOutput.mBoundingMetrics, leftCorrection,
   1184                        rightCorrection);
   1185    if (!mChildFrame->GetPrevSibling() &&
   1186        mParentFrame->GetContent()->IsMathMLElement(nsGkAtoms::msqrt)) {
   1187      // Remove leading correction in <msqrt> because the sqrt glyph itself is
   1188      // there first.
   1189      if (!mRTL) {
   1190        leftCorrection = 0;
   1191      } else {
   1192        rightCorrection = 0;
   1193      }
   1194    }
   1195    // add left correction -- this fixes the problem of the italic 'f'
   1196    // e.g., <mo>q</mo> <mi>f</mi> <mo>I</mo>
   1197    mX += leftCorrection;
   1198    mItalicCorrection = rightCorrection;
   1199  }
   1200 };
   1201 
   1202 /* virtual */
   1203 void nsMathMLContainerFrame::Place(DrawTarget* aDrawTarget,
   1204                                   const PlaceFlags& aFlags,
   1205                                   ReflowOutput& aDesiredSize) {
   1206  // This is needed in case this frame is empty (i.e., no child frames)
   1207  mBoundingMetrics = nsBoundingMetrics();
   1208 
   1209  RowChildFrameIterator child(this, aFlags);
   1210  nscoord ascent = 0, descent = 0;
   1211  while (child.Frame()) {
   1212    nscoord topMargin = child.Margin().top;
   1213    nscoord bottomMargin = child.Margin().bottom;
   1214    ascent = std::max(ascent, child.Ascent() + topMargin);
   1215    descent = std::max(descent, child.Descent() + bottomMargin);
   1216 
   1217    // add the child size
   1218    mBoundingMetrics.width = child.X();
   1219    nsBoundingMetrics childBm = child.BoundingMetrics();
   1220    childBm.ascent += topMargin;
   1221    childBm.descent += bottomMargin;
   1222    childBm.rightBearing += child.Margin().LeftRight();
   1223    childBm.width += child.Margin().LeftRight();
   1224    mBoundingMetrics += childBm;
   1225 
   1226    ++child;
   1227  }
   1228 
   1229  // Add the italic correction at the end (including the last child).
   1230  // This gives a nice gap between math and non-math frames, and still
   1231  // gives the same math inter-spacing in case this frame connects to
   1232  // another math frame
   1233  mBoundingMetrics.width = child.X();
   1234 
   1235  aDesiredSize.Width() = std::max(0, mBoundingMetrics.width);
   1236  aDesiredSize.Height() = ascent + descent;
   1237  aDesiredSize.SetBlockStartAscent(ascent);
   1238  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
   1239 
   1240  // Apply inline/block sizes to math content box.
   1241  auto sizes = GetWidthAndHeightForPlaceAdjustment(aFlags);
   1242  nscoord shiftX = ApplyAdjustmentForWidthAndHeight(aFlags, sizes, aDesiredSize,
   1243                                                    mBoundingMetrics);
   1244 
   1245  // Add padding+border.
   1246  auto borderPadding = GetBorderPaddingForPlace(aFlags);
   1247  InflateReflowAndBoundingMetrics(borderPadding, aDesiredSize,
   1248                                  mBoundingMetrics);
   1249  shiftX += borderPadding.left;
   1250 
   1251  mReference.x = 0;
   1252  mReference.y = aDesiredSize.BlockStartAscent();
   1253 
   1254  //////////////////
   1255  // Place Children
   1256  if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
   1257    PositionRowChildFrames(shiftX, aDesiredSize.BlockStartAscent());
   1258  }
   1259 }
   1260 
   1261 void nsMathMLContainerFrame::PlaceAsMrow(DrawTarget* aDrawTarget,
   1262                                         const PlaceFlags& aFlags,
   1263                                         ReflowOutput& aDesiredSize) {
   1264  nsMathMLContainerFrame::Place(aDrawTarget, aFlags, aDesiredSize);
   1265 }
   1266 
   1267 void nsMathMLContainerFrame::PositionRowChildFrames(nscoord aOffsetX,
   1268                                                    nscoord aBaseline) {
   1269  PlaceFlags flags;
   1270  RowChildFrameIterator child(this, flags);
   1271  while (child.Frame()) {
   1272    nscoord dx = aOffsetX + child.X() + child.Margin().left;
   1273    nscoord dy = aBaseline - child.Ascent();
   1274    FinishReflowChild(child.Frame(), PresContext(), child.GetReflowOutput(),
   1275                      nullptr, dx, dy, ReflowChildFlags::Default);
   1276    ++child;
   1277  }
   1278 }
   1279 
   1280 // helpers to fix the inter-spacing when <math> is the only parent
   1281 // e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math>
   1282 
   1283 static nscoord GetInterFrameSpacingFor(int32_t aScriptLevel,
   1284                                       nsIFrame* aParentFrame,
   1285                                       nsIFrame* aChildFrame) {
   1286  nsIFrame* childFrame = aParentFrame->PrincipalChildList().FirstChild();
   1287  if (!childFrame || aChildFrame == childFrame) {
   1288    return 0;
   1289  }
   1290 
   1291  int32_t carrySpace = 0;
   1292  MathMLFrameType fromFrameType = MathMLFrameType::Unknown;
   1293  MathMLFrameType prevFrameType = MathMLFrameType::Unknown;
   1294  MathMLFrameType childFrameType =
   1295      nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
   1296  childFrame = childFrame->GetNextSibling();
   1297  while (childFrame) {
   1298    prevFrameType = childFrameType;
   1299    childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
   1300    nscoord space =
   1301        GetInterFrameSpacing(aScriptLevel, prevFrameType, childFrameType,
   1302                             &fromFrameType, &carrySpace);
   1303    if (aChildFrame == childFrame) {
   1304      // get thinspace
   1305      ComputedStyle* parentContext = aParentFrame->Style();
   1306      nscoord thinSpace = GetThinSpace(parentContext->StyleFont());
   1307      // we are done
   1308      return space * thinSpace;
   1309    }
   1310    childFrame = childFrame->GetNextSibling();
   1311  }
   1312 
   1313  MOZ_ASSERT_UNREACHABLE("child not in the childlist of its parent");
   1314  return 0;
   1315 }
   1316 
   1317 static nscoord AddInterFrameSpacingToSize(ReflowOutput& aDesiredSize,
   1318                                          nsMathMLContainerFrame* aFrame) {
   1319  nscoord gap = 0;
   1320  nsIFrame* parent = aFrame->GetParent();
   1321  nsIContent* parentContent = parent->GetContent();
   1322  if (MOZ_UNLIKELY(!parentContent)) {
   1323    return 0;
   1324  }
   1325  if (parentContent->IsAnyOfMathMLElements(nsGkAtoms::math, nsGkAtoms::mtd)) {
   1326    gap = GetInterFrameSpacingFor(aFrame->StyleFont()->mMathDepth, parent,
   1327                                  aFrame);
   1328    // add our own italic correction
   1329    nscoord leftCorrection = 0, italicCorrection = 0;
   1330    nsMathMLContainerFrame::GetItalicCorrection(
   1331        aDesiredSize.mBoundingMetrics, leftCorrection, italicCorrection);
   1332    gap += leftCorrection;
   1333    if (gap) {
   1334      aDesiredSize.mBoundingMetrics.leftBearing += gap;
   1335      aDesiredSize.mBoundingMetrics.rightBearing += gap;
   1336      aDesiredSize.mBoundingMetrics.width += gap;
   1337      aDesiredSize.Width() += gap;
   1338    }
   1339    aDesiredSize.mBoundingMetrics.width += italicCorrection;
   1340    aDesiredSize.Width() += italicCorrection;
   1341  }
   1342  return gap;
   1343 }
   1344 
   1345 nscoord nsMathMLContainerFrame::FixInterFrameSpacing(
   1346    ReflowOutput& aDesiredSize) {
   1347  nscoord gap = 0;
   1348  gap = AddInterFrameSpacingToSize(aDesiredSize, this);
   1349  if (gap) {
   1350    // Shift our children to account for the correction
   1351    nsIFrame* childFrame = mFrames.FirstChild();
   1352    while (childFrame) {
   1353      childFrame->SetPosition(childFrame->GetPosition() + nsPoint(gap, 0));
   1354      childFrame = childFrame->GetNextSibling();
   1355    }
   1356  }
   1357  return gap;
   1358 }
   1359 
   1360 // helper used by mstyle, mphantom, mpadded and mrow in their implementations
   1361 // of TransmitAutomaticData().
   1362 nsresult nsMathMLContainerFrame::TransmitAutomaticDataForMrowLikeElement() {
   1363  //
   1364  // One loop to check both conditions below:
   1365  //
   1366  // 1) whether all the children of the mrow-like element are space-like.
   1367  //
   1368  //   The REC defines the following elements to be "space-like":
   1369  //   * an mstyle, mphantom, or mpadded element, all of whose direct
   1370  //     sub-expressions are space-like;
   1371  //   * an mrow all of whose direct sub-expressions are space-like.
   1372  //
   1373  // 2) whether all but one child of the mrow-like element are space-like and
   1374  //    this non-space-like child is an embellished operator.
   1375  //
   1376  //   The REC defines the following elements to be embellished operators:
   1377  //   * one of the elements mstyle, mphantom, or mpadded, such that an mrow
   1378  //     containing the same arguments would be an embellished operator;
   1379  //   * an mrow whose arguments consist (in any order) of one embellished
   1380  //     operator and zero or more space-like elements.
   1381  //
   1382  nsIFrame *childFrame, *baseFrame;
   1383  bool embellishedOpFound = false;
   1384  nsEmbellishData embellishData;
   1385 
   1386  for (childFrame = PrincipalChildList().FirstChild(); childFrame;
   1387       childFrame = childFrame->GetNextSibling()) {
   1388    nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
   1389    if (!mathMLFrame) {
   1390      break;
   1391    }
   1392    if (!mathMLFrame->IsSpaceLike()) {
   1393      if (embellishedOpFound) {
   1394        break;
   1395      }
   1396      baseFrame = childFrame;
   1397      GetEmbellishDataFrom(baseFrame, embellishData);
   1398      if (!embellishData.flags.contains(
   1399              MathMLEmbellishFlag::EmbellishedOperator)) {
   1400        break;
   1401      }
   1402      embellishedOpFound = true;
   1403    }
   1404  }
   1405 
   1406  if (!childFrame) {
   1407    // we successfully went to the end of the loop. This means that one of
   1408    // condition 1) or 2) holds.
   1409    if (!embellishedOpFound) {
   1410      // the mrow-like element is space-like.
   1411      mPresentationData.flags += MathMLPresentationFlag::SpaceLike;
   1412    } else {
   1413      // the mrow-like element is an embellished operator.
   1414      // let the state of the embellished operator found bubble to us.
   1415      mPresentationData.baseFrame = baseFrame;
   1416      mEmbellishData = embellishData;
   1417    }
   1418  }
   1419 
   1420  if (childFrame || !embellishedOpFound) {
   1421    // The element is not embellished operator
   1422    mPresentationData.baseFrame = nullptr;
   1423    mEmbellishData.flags.clear();
   1424    mEmbellishData.coreFrame = nullptr;
   1425    mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
   1426    mEmbellishData.leadingSpace = 0;
   1427    mEmbellishData.trailingSpace = 0;
   1428  }
   1429 
   1430  if (childFrame || embellishedOpFound) {
   1431    // The element is not space-like
   1432    mPresentationData.flags -= MathMLPresentationFlag::SpaceLike;
   1433  }
   1434 
   1435  return NS_OK;
   1436 }
   1437 
   1438 /*static*/
   1439 void nsMathMLContainerFrame::PropagateFrameFlagFor(nsIFrame* aFrame,
   1440                                                   nsFrameState aFlags) {
   1441  if (!aFrame || !aFlags) {
   1442    return;
   1443  }
   1444 
   1445  aFrame->AddStateBits(aFlags);
   1446  for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
   1447    PropagateFrameFlagFor(childFrame, aFlags);
   1448  }
   1449 }
   1450 
   1451 nsresult nsMathMLContainerFrame::ReportErrorToConsole(
   1452    const char* errorMsgId, const nsTArray<nsString>& aParams) {
   1453  return nsContentUtils::ReportToConsole(
   1454      nsIScriptError::errorFlag, "Layout: MathML"_ns, mContent->OwnerDoc(),
   1455      nsContentUtils::eMATHML_PROPERTIES, errorMsgId, aParams);
   1456 }
   1457 
   1458 nsresult nsMathMLContainerFrame::ReportParseError(const char16_t* aAttribute,
   1459                                                  const char16_t* aValue) {
   1460  AutoTArray<nsString, 3> argv;
   1461  argv.AppendElement(aValue);
   1462  argv.AppendElement(aAttribute);
   1463  argv.AppendElement(nsDependentAtomString(mContent->NodeInfo()->NameAtom()));
   1464  return ReportErrorToConsole("AttributeParsingError", argv);
   1465 }
   1466 
   1467 nsresult nsMathMLContainerFrame::ReportChildCountError() {
   1468  AutoTArray<nsString, 1> arg = {
   1469      nsDependentAtomString(mContent->NodeInfo()->NameAtom())};
   1470  return ReportErrorToConsole("ChildCountIncorrect", arg);
   1471 }
   1472 
   1473 nsresult nsMathMLContainerFrame::ReportInvalidChildError(nsAtom* aChildTag) {
   1474  AutoTArray<nsString, 2> argv = {
   1475      nsDependentAtomString(aChildTag),
   1476      nsDependentAtomString(mContent->NodeInfo()->NameAtom())};
   1477  return ReportErrorToConsole("InvalidChild", argv);
   1478 }
   1479 
   1480 //==========================
   1481 
   1482 nsContainerFrame* NS_NewMathMLmathBlockFrame(PresShell* aPresShell,
   1483                                             ComputedStyle* aStyle) {
   1484  auto newFrame = new (aPresShell)
   1485      nsMathMLmathBlockFrame(aStyle, aPresShell->GetPresContext());
   1486  return newFrame;
   1487 }
   1488 
   1489 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathBlockFrame)
   1490 
   1491 NS_QUERYFRAME_HEAD(nsMathMLmathBlockFrame)
   1492  NS_QUERYFRAME_ENTRY(nsMathMLmathBlockFrame)
   1493 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
   1494 
   1495 nsContainerFrame* NS_NewMathMLmathInlineFrame(PresShell* aPresShell,
   1496                                              ComputedStyle* aStyle) {
   1497  return new (aPresShell)
   1498      nsMathMLmathInlineFrame(aStyle, aPresShell->GetPresContext());
   1499 }
   1500 
   1501 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathInlineFrame)
   1502 
   1503 NS_QUERYFRAME_HEAD(nsMathMLmathInlineFrame)
   1504  NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
   1505 NS_QUERYFRAME_TAIL_INHERITING(nsInlineFrame)