Blame layout/mathml/nsMathMLmfencedFrame.cpp

Packit f0b94e
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
Packit f0b94e
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
#include "gfxContext.h"
Packit f0b94e
#include "nsMathMLmfencedFrame.h"
Packit f0b94e
#include "nsMathMLChar.h"
Packit f0b94e
#include <algorithm>
Packit f0b94e
Packit f0b94e
using namespace mozilla;
Packit f0b94e
Packit f0b94e
//
Packit f0b94e
// <mfenced> -- surround content with a pair of fences
Packit f0b94e
//
Packit f0b94e
Packit f0b94e
nsIFrame* NS_NewMathMLmfencedFrame(nsIPresShell* aPresShell,
Packit f0b94e
                                   nsStyleContext* aContext) {
Packit f0b94e
  return new (aPresShell) nsMathMLmfencedFrame(aContext);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmfencedFrame)
Packit f0b94e
Packit f0b94e
void nsMathMLmfencedFrame::DestroyFrom(nsIFrame* aDestructRoot,
Packit f0b94e
                                       PostDestroyData& aPostDestroyData) {
Packit f0b94e
  RemoveFencesAndSeparators();
Packit f0b94e
  nsMathMLContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
nsMathMLmfencedFrame::InheritAutomaticData(nsIFrame* aParent) {
Packit f0b94e
  // let the base class get the default from our parent
Packit f0b94e
  nsMathMLContainerFrame::InheritAutomaticData(aParent);
Packit f0b94e
Packit f0b94e
  mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
Packit f0b94e
Packit f0b94e
  RemoveFencesAndSeparators();
Packit f0b94e
  CreateFencesAndSeparators(PresContext());
Packit f0b94e
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsMathMLmfencedFrame::SetInitialChildList(ChildListID aListID,
Packit f0b94e
                                               nsFrameList& aChildList) {
Packit f0b94e
  // First, let the base class do its work
Packit f0b94e
  nsMathMLContainerFrame::SetInitialChildList(aListID, aChildList);
Packit f0b94e
Packit f0b94e
  // InheritAutomaticData will not get called if our parent is not a mathml
Packit f0b94e
  // frame, so initialize NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY for
Packit f0b94e
  // GetPreferredStretchSize() from Reflow().
Packit f0b94e
  mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
Packit f0b94e
  // No need to track the style contexts given to our MathML chars.
Packit f0b94e
  // The Style System will use Get/SetAdditionalStyleContext() to keep them
Packit f0b94e
  // up-to-date if dynamic changes arise.
Packit f0b94e
  CreateFencesAndSeparators(PresContext());
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsresult nsMathMLmfencedFrame::AttributeChanged(int32_t aNameSpaceID,
Packit f0b94e
                                                nsAtom* aAttribute,
Packit f0b94e
                                                int32_t aModType) {
Packit f0b94e
  RemoveFencesAndSeparators();
Packit f0b94e
  CreateFencesAndSeparators(PresContext());
Packit f0b94e
Packit f0b94e
  return nsMathMLContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
Packit f0b94e
                                                  aModType);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsresult nsMathMLmfencedFrame::ChildListChanged(int32_t aModType) {
Packit f0b94e
  RemoveFencesAndSeparators();
Packit f0b94e
  CreateFencesAndSeparators(PresContext());
Packit f0b94e
Packit f0b94e
  return nsMathMLContainerFrame::ChildListChanged(aModType);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsMathMLmfencedFrame::RemoveFencesAndSeparators() {
Packit f0b94e
  MarkNeedsDisplayItemRebuild();
Packit f0b94e
  delete mOpenChar;
Packit f0b94e
  delete mCloseChar;
Packit f0b94e
  if (mSeparatorsChar) delete[] mSeparatorsChar;
Packit f0b94e
Packit f0b94e
  mOpenChar = nullptr;
Packit f0b94e
  mCloseChar = nullptr;
Packit f0b94e
  mSeparatorsChar = nullptr;
Packit f0b94e
  mSeparatorsCount = 0;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsMathMLmfencedFrame::CreateFencesAndSeparators(
Packit f0b94e
    nsPresContext* aPresContext) {
Packit f0b94e
  nsAutoString value;
Packit f0b94e
Packit f0b94e
  //////////////
Packit f0b94e
  // see if the opening fence is there ...
Packit f0b94e
  if (!mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::open,
Packit f0b94e
                                      value)) {
Packit f0b94e
    value = char16_t('(');  // default as per the MathML REC
Packit f0b94e
  } else {
Packit f0b94e
    value.CompressWhitespace();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (!value.IsEmpty()) {
Packit f0b94e
    mOpenChar = new nsMathMLChar;
Packit f0b94e
    mOpenChar->SetData(value);
Packit f0b94e
    ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext, mOpenChar);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  //////////////
Packit f0b94e
  // see if the closing fence is there ...
Packit f0b94e
  if (!mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::close,
Packit f0b94e
                                      value)) {
Packit f0b94e
    value = char16_t(')');  // default as per the MathML REC
Packit f0b94e
  } else {
Packit f0b94e
    value.CompressWhitespace();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (!value.IsEmpty()) {
Packit f0b94e
    mCloseChar = new nsMathMLChar;
Packit f0b94e
    mCloseChar->SetData(value);
Packit f0b94e
    ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext, mCloseChar);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  //////////////
Packit f0b94e
  // see if separators are there ...
Packit f0b94e
  if (!mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::separators_,
Packit f0b94e
                                      value)) {
Packit f0b94e
    value = char16_t(',');  // default as per the MathML REC
Packit f0b94e
  } else {
Packit f0b94e
    value.StripWhitespace();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  mSeparatorsCount = value.Length();
Packit f0b94e
  if (0 < mSeparatorsCount) {
Packit f0b94e
    int32_t sepCount = mFrames.GetLength() - 1;
Packit f0b94e
    if (0 < sepCount) {
Packit f0b94e
      mSeparatorsChar = new nsMathMLChar[sepCount];
Packit f0b94e
      nsAutoString sepChar;
Packit f0b94e
      for (int32_t i = 0; i < sepCount; i++) {
Packit f0b94e
        if (i < mSeparatorsCount) {
Packit f0b94e
          sepChar = value[i];
Packit f0b94e
        } else {
Packit f0b94e
          sepChar = value[mSeparatorsCount - 1];
Packit f0b94e
        }
Packit f0b94e
        mSeparatorsChar[i].SetData(sepChar);
Packit f0b94e
        ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext,
Packit f0b94e
                               &mSeparatorsChar[i]);
Packit f0b94e
      }
Packit f0b94e
      mSeparatorsCount = sepCount;
Packit f0b94e
    } else {
Packit f0b94e
      // No separators.  Note that sepCount can be -1 here, so don't
Packit f0b94e
      // set mSeparatorsCount to it.
Packit f0b94e
      mSeparatorsCount = 0;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsMathMLmfencedFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
Packit f0b94e
                                            const nsDisplayListSet& aLists) {
Packit f0b94e
  /////////////
Packit f0b94e
  // display the content
Packit f0b94e
  nsMathMLContainerFrame::BuildDisplayList(aBuilder, aLists);
Packit f0b94e
Packit f0b94e
  ////////////
Packit f0b94e
  // display fences and separators
Packit f0b94e
  uint32_t count = 0;
Packit f0b94e
  if (mOpenChar) {
Packit f0b94e
    mOpenChar->Display(aBuilder, this, aLists, count++);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (mCloseChar) {
Packit f0b94e
    mCloseChar->Display(aBuilder, this, aLists, count++);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  for (int32_t i = 0; i < mSeparatorsCount; i++) {
Packit f0b94e
    mSeparatorsChar[i].Display(aBuilder, this, aLists, count++);
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/* @param aMetrics is an IN/OUT.  Provide the current metrics for the mFenced
Packit f0b94e
          frame and it will be enlarged as necessary.
Packit f0b94e
For simplicity the width of the container is always incremented by the width
Packit f0b94e
of the nsMathMLChar.  As we only stretch fences and separators in the vertical
Packit f0b94e
direction, this has no impact on overall appearance.
Packit f0b94e
*/
Packit f0b94e
static void ApplyUnstretchedMetrics(nsIFrame* aFrame, DrawTarget* aDrawTarget,
Packit f0b94e
                                    float aFontSizeInflation,
Packit f0b94e
                                    nsMathMLChar* aMathMLChar,
Packit f0b94e
                                    nsBoundingMetrics& aMetrics, bool aIsRTL) {
Packit f0b94e
  if (aMathMLChar && 0 < aMathMLChar->Length()) {
Packit f0b94e
    nsBoundingMetrics charSize;
Packit f0b94e
    aMathMLChar->Stretch(
Packit f0b94e
        aFrame, aDrawTarget, aFontSizeInflation, NS_STRETCH_DIRECTION_DEFAULT,
Packit f0b94e
        aMetrics,  // size is unimportant as we aren't stretching
Packit f0b94e
        charSize, NS_STRETCH_NONE, aIsRTL);
Packit f0b94e
    aMetrics += charSize;
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsMathMLmfencedFrame::Reflow(nsPresContext* aPresContext,
Packit f0b94e
                                  ReflowOutput& aDesiredSize,
Packit f0b94e
                                  const ReflowInput& aReflowInput,
Packit f0b94e
                                  nsReflowStatus& aStatus) {
Packit f0b94e
  MarkInReflow();
Packit f0b94e
  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
Packit f0b94e
Packit f0b94e
  mPresentationData.flags &= ~NS_MATHML_ERROR;
Packit f0b94e
  aDesiredSize.ClearSize();
Packit f0b94e
  aDesiredSize.SetBlockStartAscent(0);
Packit f0b94e
  aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
Packit f0b94e
Packit f0b94e
  int32_t i;
Packit f0b94e
  const nsStyleFont* font = StyleFont();
Packit f0b94e
  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
Packit f0b94e
  RefPtr<nsFontMetrics> fm =
Packit f0b94e
      nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
Packit f0b94e
  nscoord axisHeight, em;
Packit f0b94e
  GetAxisHeight(aReflowInput.mRenderingContext->GetDrawTarget(), fm,
Packit f0b94e
                axisHeight);
Packit f0b94e
  GetEmHeight(fm, em);
Packit f0b94e
  // leading to be left at the top and the bottom of stretched chars
Packit f0b94e
  nscoord leading = NSToCoordRound(0.2f * em);
Packit f0b94e
Packit f0b94e
  /////////////
Packit f0b94e
  // Reflow children
Packit f0b94e
  // Asking each child to cache its bounding metrics
Packit f0b94e
Packit f0b94e
  // Note that we don't use the base method nsMathMLContainerFrame::Reflow()
Packit f0b94e
  // because we want to stretch our fences, separators and stretchy frames using
Packit f0b94e
  // the *same* initial aDesiredSize.mBoundingMetrics. If we were to use the
Packit f0b94e
  // base method here, our stretchy frames will be stretched and placed, and we
Packit f0b94e
  // may end up stretching our fences/separators with a different aDesiredSize.
Packit f0b94e
  // XXX The above decision was revisited in bug 121748 and this code can be
Packit f0b94e
  // refactored to use nsMathMLContainerFrame::Reflow() at some stage.
Packit f0b94e
Packit f0b94e
  nsReflowStatus childStatus;
Packit f0b94e
  nsIFrame* firstChild = PrincipalChildList().FirstChild();
Packit f0b94e
  nsIFrame* childFrame = firstChild;
Packit f0b94e
  nscoord ascent = 0, descent = 0;
Packit f0b94e
  if (firstChild || mOpenChar || mCloseChar || mSeparatorsCount > 0) {
Packit f0b94e
    // We use the ASCII metrics to get our minimum height. This way,
Packit f0b94e
    // if we have borders or a background, they will fit better with
Packit f0b94e
    // other elements on the line.
Packit f0b94e
    ascent = fm->MaxAscent();
Packit f0b94e
    descent = fm->MaxDescent();
Packit f0b94e
  }
Packit f0b94e
  while (childFrame) {
Packit f0b94e
    ReflowOutput childDesiredSize(
Packit f0b94e
        aReflowInput, aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS);
Packit f0b94e
    WritingMode wm = childFrame->GetWritingMode();
Packit f0b94e
    LogicalSize availSize = aReflowInput.ComputedSize(wm);
Packit f0b94e
    availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
Packit f0b94e
    ReflowInput childReflowInput(aPresContext, aReflowInput, childFrame,
Packit f0b94e
                                 availSize);
Packit f0b94e
    ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowInput,
Packit f0b94e
                childStatus);
Packit f0b94e
    // NS_ASSERTION(childStatus.IsComplete(), "bad status");
Packit f0b94e
    SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
Packit f0b94e
                                    childDesiredSize.mBoundingMetrics);
Packit f0b94e
Packit f0b94e
    mozilla::WritingMode outerWM = aReflowInput.GetWritingMode();
Packit f0b94e
    nscoord childDescent =
Packit f0b94e
        childDesiredSize.BSize(outerWM) - childDesiredSize.BlockStartAscent();
Packit f0b94e
    if (descent < childDescent) descent = childDescent;
Packit f0b94e
    if (ascent < childDesiredSize.BlockStartAscent())
Packit f0b94e
      ascent = childDesiredSize.BlockStartAscent();
Packit f0b94e
Packit f0b94e
    childFrame = childFrame->GetNextSibling();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  /////////////
Packit f0b94e
  // Ask stretchy children to stretch themselves
Packit f0b94e
Packit f0b94e
  nsBoundingMetrics containerSize;
Packit f0b94e
  nsStretchDirection stretchDir = NS_STRETCH_DIRECTION_VERTICAL;
Packit f0b94e
Packit f0b94e
  DrawTarget* drawTarget = aReflowInput.mRenderingContext->GetDrawTarget();
Packit f0b94e
Packit f0b94e
  GetPreferredStretchSize(drawTarget, 0, /* i.e., without embellishments */
Packit f0b94e
                          stretchDir, containerSize);
Packit f0b94e
  childFrame = firstChild;
Packit f0b94e
  while (childFrame) {
Packit f0b94e
    nsIMathMLFrame* mathmlChild = do_QueryFrame(childFrame);
Packit f0b94e
    if (mathmlChild) {
Packit f0b94e
      ReflowOutput childDesiredSize(aReflowInput);
Packit f0b94e
      // retrieve the metrics that was stored at the previous pass
Packit f0b94e
      GetReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
Packit f0b94e
                                     childDesiredSize.mBoundingMetrics);
Packit f0b94e
Packit f0b94e
      mathmlChild->Stretch(drawTarget, stretchDir, containerSize,
Packit f0b94e
                           childDesiredSize);
Packit f0b94e
      // store the updated metrics
Packit f0b94e
      SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
Packit f0b94e
                                      childDesiredSize.mBoundingMetrics);
Packit f0b94e
Packit f0b94e
      nscoord childDescent =
Packit f0b94e
          childDesiredSize.Height() - childDesiredSize.BlockStartAscent();
Packit f0b94e
      if (descent < childDescent) descent = childDescent;
Packit f0b94e
      if (ascent < childDesiredSize.BlockStartAscent())
Packit f0b94e
        ascent = childDesiredSize.BlockStartAscent();
Packit f0b94e
    }
Packit f0b94e
    childFrame = childFrame->GetNextSibling();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // bug 121748: for surrounding fences & separators, use a size that covers
Packit f0b94e
  // everything
Packit f0b94e
  GetPreferredStretchSize(drawTarget, STRETCH_CONSIDER_EMBELLISHMENTS,
Packit f0b94e
                          stretchDir, containerSize);
Packit f0b94e
Packit f0b94e
  bool isRTL = StyleVisibility()->mDirection;
Packit f0b94e
Packit f0b94e
  // To achieve a minimum size of "1", the container should be enlarged by the
Packit f0b94e
  // unstretched metrics of the fences and separators.
Packit f0b94e
  ApplyUnstretchedMetrics(this, drawTarget, fontSizeInflation, mOpenChar,
Packit f0b94e
                          containerSize, isRTL);
Packit f0b94e
  for (i = 0; i < mSeparatorsCount; i++) {
Packit f0b94e
    ApplyUnstretchedMetrics(this, drawTarget, fontSizeInflation,
Packit f0b94e
                            &mSeparatorsChar[i], containerSize, isRTL);
Packit f0b94e
  }
Packit f0b94e
  ApplyUnstretchedMetrics(this, drawTarget, fontSizeInflation, mCloseChar,
Packit f0b94e
                          containerSize, isRTL);
Packit f0b94e
Packit f0b94e
  //////////////////////////////////////////
Packit f0b94e
  // Prepare the opening fence, separators, and closing fence, and
Packit f0b94e
  // adjust the origin of children.
Packit f0b94e
Packit f0b94e
  // we need to center around the axis
Packit f0b94e
  nscoord delta = std::max(containerSize.ascent - axisHeight,
Packit f0b94e
                           containerSize.descent + axisHeight);
Packit f0b94e
  containerSize.ascent = delta + axisHeight;
Packit f0b94e
  containerSize.descent = delta - axisHeight;
Packit f0b94e
Packit f0b94e
  /////////////////
Packit f0b94e
  // opening fence ...
Packit f0b94e
  ReflowChar(drawTarget, *fm, fontSizeInflation, mOpenChar,
Packit f0b94e
             NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel, axisHeight,
Packit f0b94e
             leading, em, containerSize, ascent, descent, isRTL);
Packit f0b94e
  /////////////////
Packit f0b94e
  // separators ...
Packit f0b94e
  for (i = 0; i < mSeparatorsCount; i++) {
Packit f0b94e
    ReflowChar(drawTarget, *fm, fontSizeInflation, &mSeparatorsChar[i],
Packit f0b94e
               NS_MATHML_OPERATOR_FORM_INFIX, font->mScriptLevel, axisHeight,
Packit f0b94e
               leading, em, containerSize, ascent, descent, isRTL);
Packit f0b94e
  }
Packit f0b94e
  /////////////////
Packit f0b94e
  // closing fence ...
Packit f0b94e
  ReflowChar(drawTarget, *fm, fontSizeInflation, mCloseChar,
Packit f0b94e
             NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel, axisHeight,
Packit f0b94e
             leading, em, containerSize, ascent, descent, isRTL);
Packit f0b94e
Packit f0b94e
  //////////////////
Packit f0b94e
  // Adjust the origins of each child.
Packit f0b94e
  // and update our bounding metrics
Packit f0b94e
Packit f0b94e
  i = 0;
Packit f0b94e
  nscoord dx = 0;
Packit f0b94e
  nsBoundingMetrics bm;
Packit f0b94e
  bool firstTime = true;
Packit f0b94e
  nsMathMLChar *leftChar, *rightChar;
Packit f0b94e
  if (isRTL) {
Packit f0b94e
    leftChar = mCloseChar;
Packit f0b94e
    rightChar = mOpenChar;
Packit f0b94e
  } else {
Packit f0b94e
    leftChar = mOpenChar;
Packit f0b94e
    rightChar = mCloseChar;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (leftChar) {
Packit f0b94e
    PlaceChar(leftChar, ascent, bm, dx);
Packit f0b94e
    aDesiredSize.mBoundingMetrics = bm;
Packit f0b94e
    firstTime = false;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (isRTL) {
Packit f0b94e
    childFrame = this->GetChildList(nsIFrame::kPrincipalList).LastChild();
Packit f0b94e
  } else {
Packit f0b94e
    childFrame = firstChild;
Packit f0b94e
  }
Packit f0b94e
  while (childFrame) {
Packit f0b94e
    ReflowOutput childSize(aReflowInput);
Packit f0b94e
    GetReflowAndBoundingMetricsFor(childFrame, childSize, bm);
Packit f0b94e
    if (firstTime) {
Packit f0b94e
      firstTime = false;
Packit f0b94e
      aDesiredSize.mBoundingMetrics = bm;
Packit f0b94e
    } else
Packit f0b94e
      aDesiredSize.mBoundingMetrics += bm;
Packit f0b94e
Packit f0b94e
    FinishReflowChild(childFrame, aPresContext, childSize, nullptr, dx,
Packit f0b94e
                      ascent - childSize.BlockStartAscent(), 0);
Packit f0b94e
    dx += childSize.Width();
Packit f0b94e
Packit f0b94e
    if (i < mSeparatorsCount) {
Packit f0b94e
      PlaceChar(&mSeparatorsChar[isRTL ? mSeparatorsCount - 1 - i : i], ascent,
Packit f0b94e
                bm, dx);
Packit f0b94e
      aDesiredSize.mBoundingMetrics += bm;
Packit f0b94e
    }
Packit f0b94e
    i++;
Packit f0b94e
Packit f0b94e
    if (isRTL) {
Packit f0b94e
      childFrame = childFrame->GetPrevSibling();
Packit f0b94e
    } else {
Packit f0b94e
      childFrame = childFrame->GetNextSibling();
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (rightChar) {
Packit f0b94e
    PlaceChar(rightChar, ascent, bm, dx);
Packit f0b94e
    if (firstTime)
Packit f0b94e
      aDesiredSize.mBoundingMetrics = bm;
Packit f0b94e
    else
Packit f0b94e
      aDesiredSize.mBoundingMetrics += bm;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  aDesiredSize.Width() = aDesiredSize.mBoundingMetrics.width;
Packit f0b94e
  aDesiredSize.Height() = ascent + descent;
Packit f0b94e
  aDesiredSize.SetBlockStartAscent(ascent);
Packit f0b94e
Packit f0b94e
  SetBoundingMetrics(aDesiredSize.mBoundingMetrics);
Packit f0b94e
  SetReference(nsPoint(0, aDesiredSize.BlockStartAscent()));
Packit f0b94e
Packit f0b94e
  // see if we should fix the spacing
Packit f0b94e
  FixInterFrameSpacing(aDesiredSize);
Packit f0b94e
Packit f0b94e
  // Finished with these:
Packit f0b94e
  ClearSavedChildMetrics();
Packit f0b94e
Packit f0b94e
  // Set our overflow area
Packit f0b94e
  GatherAndStoreOverflow(&aDesiredSize);
Packit f0b94e
Packit f0b94e
  MOZ_ASSERT(aStatus.IsEmpty(), "This type of frame can't be split.");
Packit f0b94e
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
static void GetCharSpacing(nsMathMLChar* aMathMLChar, nsOperatorFlags aForm,
Packit f0b94e
                           int32_t aScriptLevel, nscoord em,
Packit f0b94e
                           nscoord& aLeftSpace, nscoord& aRightSpace) {
Packit f0b94e
  nsAutoString data;
Packit f0b94e
  aMathMLChar->GetData(data);
Packit f0b94e
  nsOperatorFlags flags = 0;
Packit f0b94e
  float lspace = 0.0f;
Packit f0b94e
  float rspace = 0.0f;
Packit f0b94e
  bool found =
Packit f0b94e
      nsMathMLOperators::LookupOperator(data, aForm, &flags, &lspace, &rspace);
Packit f0b94e
Packit f0b94e
  // We don't want extra space when we are a script
Packit f0b94e
  if (found && aScriptLevel > 0) {
Packit f0b94e
    lspace /= 2.0f;
Packit f0b94e
    rspace /= 2.0f;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  aLeftSpace = NSToCoordRound(lspace * em);
Packit f0b94e
  aRightSpace = NSToCoordRound(rspace * em);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// helper functions to perform the common task of formatting our chars
Packit f0b94e
nsresult nsMathMLmfencedFrame::ReflowChar(
Packit f0b94e
    DrawTarget* aDrawTarget, nsFontMetrics& aFontMetrics,
Packit f0b94e
    float aFontSizeInflation, nsMathMLChar* aMathMLChar, nsOperatorFlags aForm,
Packit f0b94e
    int32_t aScriptLevel, nscoord axisHeight, nscoord leading, nscoord em,
Packit f0b94e
    nsBoundingMetrics& aContainerSize, nscoord& aAscent, nscoord& aDescent,
Packit f0b94e
    bool aRTL) {
Packit f0b94e
  if (aMathMLChar && 0 < aMathMLChar->Length()) {
Packit f0b94e
    nscoord leftSpace;
Packit f0b94e
    nscoord rightSpace;
Packit f0b94e
    GetCharSpacing(aMathMLChar, aForm, aScriptLevel, em, leftSpace, rightSpace);
Packit f0b94e
Packit f0b94e
    // stretch the char to the appropriate height if it is not big enough.
Packit f0b94e
    nsBoundingMetrics charSize;
Packit f0b94e
    nsresult res = aMathMLChar->Stretch(
Packit f0b94e
        this, aDrawTarget, aFontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL,
Packit f0b94e
        aContainerSize, charSize, NS_STRETCH_NORMAL, aRTL);
Packit f0b94e
Packit f0b94e
    if (NS_STRETCH_DIRECTION_UNSUPPORTED !=
Packit f0b94e
        aMathMLChar->GetStretchDirection()) {
Packit f0b94e
      // has changed... so center the char around the axis
Packit f0b94e
      nscoord height = charSize.ascent + charSize.descent;
Packit f0b94e
      charSize.ascent = height / 2 + axisHeight;
Packit f0b94e
      charSize.descent = height - charSize.ascent;
Packit f0b94e
    } else {
Packit f0b94e
      // either it hasn't changed or stretching the char failed (i.e.,
Packit f0b94e
      // nsLayoutUtils::AppUnitBoundsOfString failed)
Packit f0b94e
      leading = 0;
Packit f0b94e
      if (NS_FAILED(res)) {
Packit f0b94e
        nsAutoString data;
Packit f0b94e
        aMathMLChar->GetData(data);
Packit f0b94e
        nsBoundingMetrics metrics = nsLayoutUtils::AppUnitBoundsOfString(
Packit f0b94e
            data.get(), data.Length(), aFontMetrics, aDrawTarget);
Packit f0b94e
        charSize.ascent = metrics.ascent;
Packit f0b94e
        charSize.descent = metrics.descent;
Packit f0b94e
        charSize.width = metrics.width;
Packit f0b94e
        // Set this as the bounding metrics of the MathMLChar to leave
Packit f0b94e
        // the necessary room to paint the char.
Packit f0b94e
        aMathMLChar->SetBoundingMetrics(charSize);
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    if (aAscent < charSize.ascent + leading)
Packit f0b94e
      aAscent = charSize.ascent + leading;
Packit f0b94e
    if (aDescent < charSize.descent + leading)
Packit f0b94e
      aDescent = charSize.descent + leading;
Packit f0b94e
Packit f0b94e
    // account the spacing
Packit f0b94e
    charSize.width += leftSpace + rightSpace;
Packit f0b94e
Packit f0b94e
    // x-origin is used to store lspace ...
Packit f0b94e
    // y-origin is used to stored the ascent ...
Packit f0b94e
    aMathMLChar->SetRect(nsRect(leftSpace, charSize.ascent, charSize.width,
Packit f0b94e
                                charSize.ascent + charSize.descent));
Packit f0b94e
  }
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/*static*/ void nsMathMLmfencedFrame::PlaceChar(nsMathMLChar* aMathMLChar,
Packit f0b94e
                                                nscoord aDesiredAscent,
Packit f0b94e
                                                nsBoundingMetrics& bm,
Packit f0b94e
                                                nscoord& dx) {
Packit f0b94e
  aMathMLChar->GetBoundingMetrics(bm);
Packit f0b94e
Packit f0b94e
  // the char's x-origin was used to store lspace ...
Packit f0b94e
  // the char's y-origin was used to store the ascent ...
Packit f0b94e
  // the char's width was used to store the advance with (with spacing) ...
Packit f0b94e
  nsRect rect;
Packit f0b94e
  aMathMLChar->GetRect(rect);
Packit f0b94e
Packit f0b94e
  nscoord dy = aDesiredAscent - rect.y;
Packit f0b94e
  if (aMathMLChar->GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED) {
Packit f0b94e
    // the stretchy char will be centered around the axis
Packit f0b94e
    // so we adjust the returned bounding metrics accordingly
Packit f0b94e
    bm.descent = (bm.ascent + bm.descent) - rect.y;
Packit f0b94e
    bm.ascent = rect.y;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  aMathMLChar->SetRect(nsRect(dx + rect.x, dy, bm.width, rect.height));
Packit f0b94e
Packit f0b94e
  bm.leftBearing += rect.x;
Packit f0b94e
  bm.rightBearing += rect.x;
Packit f0b94e
Packit f0b94e
  // return rect.width since it includes lspace and rspace
Packit f0b94e
  bm.width = rect.width;
Packit f0b94e
  dx += rect.width;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
static nscoord GetMaxCharWidth(nsIFrame* aFrame, DrawTarget* aDrawTarget,
Packit f0b94e
                               float aFontSizeInflation,
Packit f0b94e
                               nsMathMLChar* aMathMLChar, nsOperatorFlags aForm,
Packit f0b94e
                               int32_t aScriptLevel, nscoord em) {
Packit f0b94e
  nscoord width =
Packit f0b94e
      aMathMLChar->GetMaxWidth(aFrame, aDrawTarget, aFontSizeInflation);
Packit f0b94e
Packit f0b94e
  if (0 < aMathMLChar->Length()) {
Packit f0b94e
    nscoord leftSpace;
Packit f0b94e
    nscoord rightSpace;
Packit f0b94e
    GetCharSpacing(aMathMLChar, aForm, aScriptLevel, em, leftSpace, rightSpace);
Packit f0b94e
Packit f0b94e
    width += leftSpace + rightSpace;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return width;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/* virtual */ void nsMathMLmfencedFrame::GetIntrinsicISizeMetrics(
Packit f0b94e
    gfxContext* aRenderingContext, ReflowOutput& aDesiredSize) {
Packit f0b94e
  nscoord width = 0;
Packit f0b94e
Packit f0b94e
  const nsStyleFont* font = StyleFont();
Packit f0b94e
  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
Packit f0b94e
  RefPtr<nsFontMetrics> fm =
Packit f0b94e
      nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
Packit f0b94e
  nscoord em;
Packit f0b94e
  GetEmHeight(fm, em);
Packit f0b94e
Packit f0b94e
  if (mOpenChar) {
Packit f0b94e
    width += GetMaxCharWidth(
Packit f0b94e
        this, aRenderingContext->GetDrawTarget(), fontSizeInflation, mOpenChar,
Packit f0b94e
        NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel, em);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  int32_t i = 0;
Packit f0b94e
  for (nsIFrame* childFrame : PrincipalChildList()) {
Packit f0b94e
    // XXX This includes margin while Reflow currently doesn't consider
Packit f0b94e
    // margin, so we may end up with too much space, but, with stretchy
Packit f0b94e
    // characters, this is an approximation anyway.
Packit f0b94e
    width += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
Packit f0b94e
                                                  nsLayoutUtils::PREF_ISIZE);
Packit f0b94e
Packit f0b94e
    if (i < mSeparatorsCount) {
Packit f0b94e
      width += GetMaxCharWidth(this, aRenderingContext->GetDrawTarget(),
Packit f0b94e
                               fontSizeInflation, &mSeparatorsChar[i],
Packit f0b94e
                               NS_MATHML_OPERATOR_FORM_INFIX,
Packit f0b94e
                               font->mScriptLevel, em);
Packit f0b94e
    }
Packit f0b94e
    i++;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (mCloseChar) {
Packit f0b94e
    width += GetMaxCharWidth(
Packit f0b94e
        this, aRenderingContext->GetDrawTarget(), fontSizeInflation, mCloseChar,
Packit f0b94e
        NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel, em);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  aDesiredSize.Width() = width;
Packit f0b94e
  aDesiredSize.mBoundingMetrics.width = width;
Packit f0b94e
  aDesiredSize.mBoundingMetrics.leftBearing = 0;
Packit f0b94e
  aDesiredSize.mBoundingMetrics.rightBearing = width;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nscoord nsMathMLmfencedFrame::FixInterFrameSpacing(ReflowOutput& aDesiredSize) {
Packit f0b94e
  nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
Packit f0b94e
  if (!gap) return 0;
Packit f0b94e
Packit f0b94e
  nsRect rect;
Packit f0b94e
  if (mOpenChar) {
Packit f0b94e
    mOpenChar->GetRect(rect);
Packit f0b94e
    rect.MoveBy(gap, 0);
Packit f0b94e
    mOpenChar->SetRect(rect);
Packit f0b94e
  }
Packit f0b94e
  if (mCloseChar) {
Packit f0b94e
    mCloseChar->GetRect(rect);
Packit f0b94e
    rect.MoveBy(gap, 0);
Packit f0b94e
    mCloseChar->SetRect(rect);
Packit f0b94e
  }
Packit f0b94e
  for (int32_t i = 0; i < mSeparatorsCount; i++) {
Packit f0b94e
    mSeparatorsChar[i].GetRect(rect);
Packit f0b94e
    rect.MoveBy(gap, 0);
Packit f0b94e
    mSeparatorsChar[i].SetRect(rect);
Packit f0b94e
  }
Packit f0b94e
  return gap;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// ----------------------
Packit f0b94e
// the Style System will use these to pass the proper style context to our
Packit f0b94e
// MathMLChar
Packit f0b94e
nsStyleContext* nsMathMLmfencedFrame::GetAdditionalStyleContext(
Packit f0b94e
    int32_t aIndex) const {
Packit f0b94e
  int32_t openIndex = -1;
Packit f0b94e
  int32_t closeIndex = -1;
Packit f0b94e
  int32_t lastIndex = mSeparatorsCount - 1;
Packit f0b94e
Packit f0b94e
  if (mOpenChar) {
Packit f0b94e
    lastIndex++;
Packit f0b94e
    openIndex = lastIndex;
Packit f0b94e
  }
Packit f0b94e
  if (mCloseChar) {
Packit f0b94e
    lastIndex++;
Packit f0b94e
    closeIndex = lastIndex;
Packit f0b94e
  }
Packit f0b94e
  if (aIndex < 0 || aIndex > lastIndex) {
Packit f0b94e
    return nullptr;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (aIndex < mSeparatorsCount) {
Packit f0b94e
    return mSeparatorsChar[aIndex].GetStyleContext();
Packit f0b94e
  } else if (aIndex == openIndex) {
Packit f0b94e
    return mOpenChar->GetStyleContext();
Packit f0b94e
  } else if (aIndex == closeIndex) {
Packit f0b94e
    return mCloseChar->GetStyleContext();
Packit f0b94e
  }
Packit f0b94e
  return nullptr;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsMathMLmfencedFrame::SetAdditionalStyleContext(
Packit f0b94e
    int32_t aIndex, nsStyleContext* aStyleContext) {
Packit f0b94e
  int32_t openIndex = -1;
Packit f0b94e
  int32_t closeIndex = -1;
Packit f0b94e
  int32_t lastIndex = mSeparatorsCount - 1;
Packit f0b94e
Packit f0b94e
  if (mOpenChar) {
Packit f0b94e
    lastIndex++;
Packit f0b94e
    openIndex = lastIndex;
Packit f0b94e
  }
Packit f0b94e
  if (mCloseChar) {
Packit f0b94e
    lastIndex++;
Packit f0b94e
    closeIndex = lastIndex;
Packit f0b94e
  }
Packit f0b94e
  if (aIndex < 0 || aIndex > lastIndex) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (aIndex < mSeparatorsCount) {
Packit f0b94e
    mSeparatorsChar[aIndex].SetStyleContext(aStyleContext);
Packit f0b94e
  } else if (aIndex == openIndex) {
Packit f0b94e
    mOpenChar->SetStyleContext(aStyleContext);
Packit f0b94e
  } else if (aIndex == closeIndex) {
Packit f0b94e
    mCloseChar->SetStyleContext(aStyleContext);
Packit f0b94e
  }
Packit f0b94e
}