Ejemplo n.º 1
0
// Only place the selected child ...
/* virtual */ nsresult
nsMathMLSelectedFrame::Place(nsRenderingContext& aRenderingContext,
                             bool                 aPlaceOrigin,
                             nsHTMLReflowMetrics& aDesiredSize)
{
  nsIFrame* childFrame = GetSelectedFrame();

  if (mInvalidMarkup) {
    return ReflowError(aRenderingContext, aDesiredSize);
  }

  aDesiredSize.ClearSize();
  aDesiredSize.SetBlockStartAscent(0);
  mBoundingMetrics = nsBoundingMetrics();
  if (childFrame) {
    GetReflowAndBoundingMetricsFor(childFrame, aDesiredSize, mBoundingMetrics);
    if (aPlaceOrigin) {
      FinishReflowChild(childFrame, PresContext(), aDesiredSize, nullptr, 0, 0, 0);
    }
    mReference.x = 0;
    mReference.y = aDesiredSize.BlockStartAscent();
  }
  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
  return NS_OK;
}
Ejemplo n.º 2
0
void
nsMathMLmspaceFrame::Reflow(nsPresContext*          aPresContext,
                            nsHTMLReflowMetrics&     aDesiredSize,
                            const nsHTMLReflowState& aReflowState,
                            nsReflowStatus&          aStatus)
{
  MarkInReflow();
  mPresentationData.flags &= ~NS_MATHML_ERROR;
  ProcessAttributes(aPresContext);

  mBoundingMetrics = nsBoundingMetrics();
  mBoundingMetrics.width = mWidth;
  mBoundingMetrics.ascent = mHeight;
  mBoundingMetrics.descent = mDepth;
  mBoundingMetrics.leftBearing = 0;
  mBoundingMetrics.rightBearing = mBoundingMetrics.width;

  aDesiredSize.SetBlockStartAscent(mHeight);
  aDesiredSize.Width() = std::max(0, mBoundingMetrics.width);
  aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + mDepth;
  // Also return our bounding metrics
  aDesiredSize.mBoundingMetrics = mBoundingMetrics;

  aStatus = NS_FRAME_COMPLETE;
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
}
Ejemplo n.º 3
0
// Only reflow the selected child ...
void
nsMathMLSelectedFrame::Reflow(nsPresContext*          aPresContext,
                              nsHTMLReflowMetrics&     aDesiredSize,
                              const nsHTMLReflowState& aReflowState,
                              nsReflowStatus&          aStatus)
{
  aStatus = NS_FRAME_COMPLETE;
  aDesiredSize.ClearSize();
  aDesiredSize.SetBlockStartAscent(0);
  mBoundingMetrics = nsBoundingMetrics();
  nsIFrame* childFrame = GetSelectedFrame();
  if (childFrame) {
    WritingMode wm = childFrame->GetWritingMode();
    LogicalSize availSize = aReflowState.ComputedSize(wm);
    availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
    nsHTMLReflowState childReflowState(aPresContext, aReflowState,
                                       childFrame, availSize);
    ReflowChild(childFrame, aPresContext, aDesiredSize,
                childReflowState, aStatus);
    SaveReflowAndBoundingMetricsFor(childFrame, aDesiredSize,
                                    aDesiredSize.mBoundingMetrics);
    mBoundingMetrics = aDesiredSize.mBoundingMetrics;
  }
  FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
}
Ejemplo n.º 4
0
void
nsTextControlFrame::Reflow(nsPresContext*   aPresContext,
                           nsHTMLReflowMetrics&     aDesiredSize,
                           const nsHTMLReflowState& aReflowState,
                           nsReflowStatus&          aStatus)
{
  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

  // make sure that the form registers itself on the initial/first reflow
  if (mState & NS_FRAME_FIRST_REFLOW) {
    nsFormControlFrame::RegUnRegAccessKey(this, true);
  }

  // set values of reflow's out parameters
  WritingMode wm = aReflowState.GetWritingMode();
  LogicalSize
    finalSize(wm,
              aReflowState.ComputedISize() +
              aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm),
              aReflowState.ComputedBSize() +
              aReflowState.ComputedLogicalBorderPadding().BStartEnd(wm));
  aDesiredSize.SetSize(wm, finalSize);

  // computation of the ascent wrt the input height
  nscoord lineHeight = aReflowState.ComputedBSize();
  float inflation = nsLayoutUtils::FontSizeInflationFor(this);
  if (!IsSingleLineTextControl()) {
    lineHeight = nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(),
                                                   NS_AUTOHEIGHT, inflation);
  }
  RefPtr<nsFontMetrics> fontMet;
  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet),
                                        inflation);
  // now adjust for our borders and padding
  aDesiredSize.SetBlockStartAscent(
    nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight,
                                           wm.IsLineInverted()) +
    aReflowState.ComputedLogicalBorderPadding().BStart(wm));

  // overflow handling
  aDesiredSize.SetOverflowAreasToDesiredBounds();
  // perform reflow on all kids
  nsIFrame* kid = mFrames.FirstChild();
  while (kid) {
    ReflowTextControlChild(kid, aPresContext, aReflowState, aStatus, aDesiredSize);
    kid = kid->GetNextSibling();
  }

  // take into account css properties that affect overflow handling
  FinishAndStoreOverflow(&aDesiredSize);

  aStatus = NS_FRAME_COMPLETE;
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
}
Ejemplo n.º 5
0
nsresult
nsMathMLmoFrame::Place(nsRenderingContext&  aRenderingContext,
                       bool                 aPlaceOrigin,
                       nsHTMLReflowMetrics& aDesiredSize)
{
  nsresult rv = nsMathMLTokenFrame::Place(aRenderingContext, aPlaceOrigin,
                                          aDesiredSize);

  if (NS_FAILED(rv)) {
    return rv;
  }

  /* Special behaviour for largeops.
     In MathML "stretchy" and displaystyle "largeop" are different notions,
     even if we use the same technique to draw them (picking size variants).
     So largeop display operators should be considered "non-stretchy" and
     thus their sizes should be taken into account for the stretch size of
     other elements.

     This is a preliminary stretch - exact sizing/placement is handled by the
     Stretch() method.
  */

  if (!aPlaceOrigin &&
      StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK &&
      NS_MATHML_OPERATOR_IS_LARGEOP(mFlags) && UseMathMLChar()) {
    nsBoundingMetrics newMetrics;
    rv = mMathMLChar.Stretch(PresContext(), aRenderingContext,
                                      nsLayoutUtils::FontSizeInflationFor(this),
                                      NS_STRETCH_DIRECTION_VERTICAL,
                                      aDesiredSize.mBoundingMetrics, newMetrics,
                                      NS_STRETCH_LARGEOP, StyleVisibility()->mDirection);

    if (NS_FAILED(rv)) {
      // Just use the initial size
      return NS_OK;
    }

    aDesiredSize.mBoundingMetrics = newMetrics;
     /* Treat the ascent/descent values calculated in the TokenFrame place
       calculations as the minimum for aDesiredSize calculations, rather
       than fetching them from font metrics again.
    */
    aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent,
                                              newMetrics.ascent));
    aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
                            std::max(mBoundingMetrics.descent,
                                     newMetrics.descent);
    aDesiredSize.Width() = newMetrics.width;
    mBoundingMetrics = newMetrics;
  }
  return NS_OK;
}
Ejemplo n.º 6
0
// For token elements, mBoundingMetrics is computed at the ReflowToken
// pass, it is not computed here because our children may be text frames
// that do not implement the GetBoundingMetrics() interface.
/* virtual */ nsresult
nsMathMLTokenFrame::Place(DrawTarget*          aDrawTarget,
                          bool                 aPlaceOrigin,
                          nsHTMLReflowMetrics& aDesiredSize)
{
  mBoundingMetrics = nsBoundingMetrics();
  for (nsIFrame* childFrame = GetFirstPrincipalChild(); childFrame;
       childFrame = childFrame->GetNextSibling()) {
    nsHTMLReflowMetrics childSize(aDesiredSize.GetWritingMode());
    GetReflowAndBoundingMetricsFor(childFrame, childSize,
                                   childSize.mBoundingMetrics, nullptr);
    // compute and cache the bounding metrics
    mBoundingMetrics += childSize.mBoundingMetrics;
  }

  RefPtr<nsFontMetrics> fm;
  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
                                        nsLayoutUtils::
                                        FontSizeInflationFor(this));
  nscoord ascent = fm->MaxAscent();
  nscoord descent = fm->MaxDescent();

  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
  aDesiredSize.Width() = mBoundingMetrics.width;
  aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent, ascent));
  aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
                        std::max(mBoundingMetrics.descent, descent);

  if (aPlaceOrigin) {
    nscoord dy, dx = 0;
    for (nsIFrame* childFrame = GetFirstPrincipalChild(); childFrame;
         childFrame = childFrame->GetNextSibling()) {
      nsHTMLReflowMetrics childSize(aDesiredSize.GetWritingMode());
      GetReflowAndBoundingMetricsFor(childFrame, childSize,
                                     childSize.mBoundingMetrics);

      // place and size the child; (dx,0) makes the caret happy - bug 188146
      dy = childSize.Height() == 0 ? 0 : aDesiredSize.BlockStartAscent() - childSize.BlockStartAscent();
      FinishReflowChild(childFrame, PresContext(), childSize, nullptr, dx, dy, 0);
      dx += childSize.Width();
    }
  }

  SetReference(nsPoint(0, aDesiredSize.BlockStartAscent()));

  return NS_OK;
}
Ejemplo n.º 7
0
void
nsMathMLTokenFrame::Reflow(nsPresContext*          aPresContext,
                           nsHTMLReflowMetrics&     aDesiredSize,
                           const nsHTMLReflowState& aReflowState,
                           nsReflowStatus&          aStatus)
{
  MarkInReflow();
  mPresentationData.flags &= ~NS_MATHML_ERROR;

  // initializations needed for empty markup like <mtag></mtag>
  aDesiredSize.ClearSize();
  aDesiredSize.SetBlockStartAscent(0);
  aDesiredSize.mBoundingMetrics = nsBoundingMetrics();

  nsIFrame* childFrame = GetFirstPrincipalChild();
  while (childFrame) {
    // ask our children to compute their bounding metrics
    nsHTMLReflowMetrics childDesiredSize(aReflowState.GetWritingMode(),
                                         aDesiredSize.mFlags
                                         | NS_REFLOW_CALC_BOUNDING_METRICS);
    WritingMode wm = childFrame->GetWritingMode();
    LogicalSize availSize = aReflowState.ComputedSize(wm);
    availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
    nsHTMLReflowState childReflowState(aPresContext, aReflowState,
                                       childFrame, availSize);
    ReflowChild(childFrame, aPresContext, childDesiredSize,
                childReflowState, aStatus);
    //NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
    SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
                                    childDesiredSize.mBoundingMetrics);

    childFrame = childFrame->GetNextSibling();
  }

  // place and size children
  FinalizeReflow(aReflowState.rendContext->GetDrawTarget(), aDesiredSize);

  aStatus = NS_FRAME_COMPLETE;
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
}
void
nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext,
                                               nsHTMLReflowMetrics& aButtonDesiredSize,
                                               const nsHTMLReflowState& aButtonReflowState,
                                               nsIFrame* aFirstKid)
{
  WritingMode wm = GetWritingMode();
  LogicalSize availSize = aButtonReflowState.ComputedSize(wm);
  availSize.BSize(wm) = NS_INTRINSICSIZE;

  // Buttons have some bonus renderer-determined border/padding,
  // which occupies part of the button's content-box area:
  LogicalMargin focusPadding =
    LogicalMargin(wm, mRenderer.GetAddedButtonBorderAndPadding());

  // See whether out availSize's inline-size is big enough.  If it's
  // smaller than our intrinsic min iSize, that means that the kid
  // wouldn't really fit.  In that case, we overflow into our internal
  // focuspadding (which other browsers don't have) so that there's a
  // little more space for it.
  // Note that GetMinISize includes the focusPadding.
  nscoord IOverflow = GetMinISize(aButtonReflowState.rendContext) -
                      aButtonReflowState.ComputedISize();
  nscoord IFocusPadding = focusPadding.IStartEnd(wm);
  nscoord focusPaddingReduction = std::min(IFocusPadding,
                                           std::max(IOverflow, 0));
  if (focusPaddingReduction > 0) {
    nscoord startReduction = focusPadding.IStart(wm);
    if (focusPaddingReduction != IFocusPadding) {
      startReduction = NSToCoordRound(startReduction *
                                      (float(focusPaddingReduction) /
                                       float(IFocusPadding)));
    }
    focusPadding.IStart(wm) -= startReduction;
    focusPadding.IEnd(wm) -= focusPaddingReduction - startReduction;
  }

  // shorthand for a value we need to use in a bunch of places
  const LogicalMargin& clbp = aButtonReflowState.ComputedLogicalBorderPadding();

  // Indent the child inside us by the focus border. We must do this separate
  // from the regular border.
  availSize.ISize(wm) -= focusPadding.IStartEnd(wm);

  LogicalPoint childPos(wm);
  childPos.I(wm) = focusPadding.IStart(wm) + clbp.IStart(wm);
  availSize.ISize(wm) = std::max(availSize.ISize(wm), 0);

  // Give child a clone of the button's reflow state, with height/width reduced
  // by focusPadding, so that descendants with height:100% don't protrude.
  nsHTMLReflowState adjustedButtonReflowState =
    CloneReflowStateWithReducedContentBox(aButtonReflowState,
                                          focusPadding.GetPhysicalMargin(wm));

  nsHTMLReflowState contentsReflowState(aPresContext,
                                        adjustedButtonReflowState,
                                        aFirstKid, availSize);

  nsReflowStatus contentsReflowStatus;
  nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState);
  childPos.B(wm) = 0; // This will be set properly later, after reflowing the
                      // child to determine its size.

  // We just pass a dummy containerSize here, as the child will be
  // repositioned later by FinishReflowChild.
  nsSize dummyContainerSize;
  ReflowChild(aFirstKid, aPresContext,
              contentsDesiredSize, contentsReflowState,
              wm, childPos, dummyContainerSize, 0, contentsReflowStatus);
  MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus),
             "We gave button-contents frame unconstrained available height, "
             "so it should be complete");

  // Compute the button's content-box size:
  LogicalSize buttonContentBox(wm);
  if (aButtonReflowState.ComputedBSize() != NS_INTRINSICSIZE) {
    // Button has a fixed block-size -- that's its content-box bSize.
    buttonContentBox.BSize(wm) = aButtonReflowState.ComputedBSize();
  } else {
    // Button is intrinsically sized -- it should shrinkwrap the
    // button-contents' bSize, plus any focus-padding space:
    buttonContentBox.BSize(wm) =
      contentsDesiredSize.BSize(wm) + focusPadding.BStartEnd(wm);

    // Make sure we obey min/max-bSize in the case when we're doing intrinsic
    // sizing (we get it for free when we have a non-intrinsic
    // aButtonReflowState.ComputedBSize()).  Note that we do this before
    // adjusting for borderpadding, since mComputedMaxBSize and
    // mComputedMinBSize are content bSizes.
    buttonContentBox.BSize(wm) =
      NS_CSS_MINMAX(buttonContentBox.BSize(wm),
                    aButtonReflowState.ComputedMinBSize(),
                    aButtonReflowState.ComputedMaxBSize());
  }
  if (aButtonReflowState.ComputedISize() != NS_INTRINSICSIZE) {
    buttonContentBox.ISize(wm) = aButtonReflowState.ComputedISize();
  } else {
    buttonContentBox.ISize(wm) =
      contentsDesiredSize.ISize(wm) + focusPadding.IStartEnd(wm);
    buttonContentBox.ISize(wm) =
      NS_CSS_MINMAX(buttonContentBox.ISize(wm),
                    aButtonReflowState.ComputedMinISize(),
                    aButtonReflowState.ComputedMaxISize());
  }

  // Center child in the block-direction in the button
  // (technically, inside of the button's focus-padding area)
  nscoord extraSpace =
    buttonContentBox.BSize(wm) - focusPadding.BStartEnd(wm) -
    contentsDesiredSize.BSize(wm);

  childPos.B(wm) = std::max(0, extraSpace / 2);

  // Adjust childPos.B() to be in terms of the button's frame-rect, instead of
  // its focus-padding rect:
  childPos.B(wm) += focusPadding.BStart(wm) + clbp.BStart(wm);

  nsSize containerSize =
    (buttonContentBox + clbp.Size(wm)).GetPhysicalSize(wm);

  // Place the child
  FinishReflowChild(aFirstKid, aPresContext,
                    contentsDesiredSize, &contentsReflowState,
                    wm, childPos, containerSize, 0);

  // Make sure we have a useful 'ascent' value for the child
  if (contentsDesiredSize.BlockStartAscent() ==
      nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
    WritingMode wm = aButtonReflowState.GetWritingMode();
    contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm));
  }

  // OK, we're done with the child frame.
  // Use what we learned to populate the button frame's reflow metrics.
  //  * Button's height & width are content-box size + border-box contribution:
  aButtonDesiredSize.SetSize(wm,
    LogicalSize(wm, aButtonReflowState.ComputedISize() + clbp.IStartEnd(wm),
                    buttonContentBox.BSize(wm) + clbp.BStartEnd(wm)));

  //  * Button's ascent is its child's ascent, plus the child's block-offset
  // within our frame... unless it's orthogonal, in which case we'll use the
  // contents inline-size as an approximation for now.
  // XXX is there a better strategy? should we include border-padding?
  if (aButtonDesiredSize.GetWritingMode().IsOrthogonalTo(wm)) {
    aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.ISize(wm));
  } else {
    aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.BlockStartAscent() +
                                           childPos.B(wm));
  }

  aButtonDesiredSize.SetOverflowAreasToDesiredBounds();
}
Ejemplo n.º 9
0
void
BRFrame::Reflow(nsPresContext* aPresContext,
                nsHTMLReflowMetrics& aMetrics,
                const nsHTMLReflowState& aReflowState,
                nsReflowStatus& aStatus)
{
  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("BRFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
  WritingMode wm = aReflowState.GetWritingMode();
  LogicalSize finalSize(wm);
  finalSize.BSize(wm) = 0; // BR frames with block size 0 are ignored in quirks
                           // mode by nsLineLayout::VerticalAlignFrames .
                           // However, it's not always 0.  See below.
  finalSize.ISize(wm) = 0;
  aMetrics.SetBlockStartAscent(0);

  // Only when the BR is operating in a line-layout situation will it
  // behave like a BR. Additionally, we suppress breaks from BR inside
  // of ruby frames. To determine if we're inside ruby, we have to rely
  // on the *parent's* ShouldSuppressLineBreak() method, instead of our
  // own, because we may have custom "display" value that makes our
  // ShouldSuppressLineBreak() return false.
  nsLineLayout* ll = aReflowState.mLineLayout;
  if (ll && !GetParent()->StyleContext()->ShouldSuppressLineBreak()) {
    // Note that the compatibility mode check excludes AlmostStandards
    // mode, since this is the inline box model.  See bug 161691.
    if ( ll->LineIsEmpty() ||
         aPresContext->CompatibilityMode() == eCompatibility_FullStandards ) {
      // The line is logically empty; any whitespace is trimmed away.
      //
      // If this frame is going to terminate the line we know
      // that nothing else will go on the line. Therefore, in this
      // case, we provide some height for the BR frame so that it
      // creates some vertical whitespace.  It's necessary to use the
      // line-height rather than the font size because the
      // quirks-mode fix that doesn't apply the block's min
      // line-height makes this necessary to make BR cause a line
      // of the full line-height

      // We also do this in strict mode because BR should act like a
      // normal inline frame.  That line-height is used is important
      // here for cases where the line-height is less than 1.
      RefPtr<nsFontMetrics> fm =
        nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
      if (fm) {
        nscoord logicalHeight = aReflowState.CalcLineHeight();
        finalSize.BSize(wm) = logicalHeight;
        aMetrics.SetBlockStartAscent(nsLayoutUtils::GetCenteredFontBaseline(
                                       fm, logicalHeight, wm.IsLineInverted()));
      }
      else {
        aMetrics.SetBlockStartAscent(aMetrics.BSize(wm) = 0);
      }

      // XXX temporary until I figure out a better solution; see the
      // code in nsLineLayout::VerticalAlignFrames that zaps minY/maxY
      // if the width is zero.
      // XXX This also fixes bug 10036!
      // Warning: nsTextControlFrame::CalculateSizeStandard depends on
      // the following line, see bug 228752.
      // The code below in AddInlinePrefISize also adds 1 appunit to width
      finalSize.ISize(wm) = 1;
    }

    // Return our reflow status
    uint32_t breakType = aReflowState.mStyleDisplay->PhysicalBreakType(wm);
    if (NS_STYLE_CLEAR_NONE == breakType) {
      breakType = NS_STYLE_CLEAR_LINE;
    }

    aStatus = NS_INLINE_BREAK | NS_INLINE_BREAK_AFTER |
      NS_INLINE_MAKE_BREAK_TYPE(breakType);
    ll->SetLineEndsInBR(true);
  }
  else {
    aStatus = NS_FRAME_COMPLETE;
  }

  aMetrics.SetSize(wm, finalSize);
  aMetrics.SetOverflowAreasToDesiredBounds();

  mAscent = aMetrics.BlockStartAscent();

  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
}
Ejemplo n.º 10
0
// NOTE: aDesiredStretchSize is an IN/OUT parameter
//       On input  - it contains our current size
//       On output - the same size or the new size that we want
NS_IMETHODIMP
nsMathMLmoFrame::Stretch(nsRenderingContext& aRenderingContext,
                         nsStretchDirection   aStretchDirection,
                         nsBoundingMetrics&   aContainerSize,
                         nsHTMLReflowMetrics& aDesiredStretchSize)
{
  if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) {
    NS_WARNING("it is wrong to fire stretch more than once on a frame");
    return NS_OK;
  }
  mPresentationData.flags |= NS_MATHML_STRETCH_DONE;

  nsIFrame* firstChild = mFrames.FirstChild();

  // get the axis height;
  nsRefPtr<nsFontMetrics> fm;
  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
  aRenderingContext.SetFont(fm);
  nscoord axisHeight, height;
  GetAxisHeight(aRenderingContext, fm, axisHeight);

  // get the leading to be left at the top and the bottom of the stretched char
  // this seems more reliable than using fm->GetLeading() on suspicious fonts
  nscoord em;
  GetEmHeight(fm, em);
  nscoord leading = NSToCoordRound(0.2f * em);

  // Operators that are stretchy, or those that are to be centered
  // to cater for fonts that are not math-aware, are handled by the MathMLChar
  // ('form' is reset if stretch fails -- i.e., we don't bother to stretch next time)
  bool useMathMLChar = UseMathMLChar();

  nsBoundingMetrics charSize;
  nsBoundingMetrics container = aDesiredStretchSize.mBoundingMetrics;
  bool isVertical = false;

  if (((aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) ||
       (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT))  &&
      (mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL)) {
    isVertical = true;
  }

  uint32_t stretchHint =
    GetStretchHint(mFlags, mPresentationData, isVertical, StyleFont());

  if (useMathMLChar) {
    nsBoundingMetrics initialSize = aDesiredStretchSize.mBoundingMetrics;

    if (stretchHint != NS_STRETCH_NONE) {

      container = aContainerSize;

      // some adjustments if the operator is symmetric and vertical

      if (isVertical && NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
        // we need to center about the axis
        nscoord delta = std::max(container.ascent - axisHeight,
                               container.descent + axisHeight);
        container.ascent = delta + axisHeight;
        container.descent = delta - axisHeight;

        // get ready in case we encounter user-desired min-max size
        delta = std::max(initialSize.ascent - axisHeight,
                       initialSize.descent + axisHeight);
        initialSize.ascent = delta + axisHeight;
        initialSize.descent = delta - axisHeight;
      }

      // check for user-desired min-max size

      if (mMaxSize != NS_MATHML_OPERATOR_SIZE_INFINITY && mMaxSize > 0.0f) {
        // if we are here, there is a user defined maxsize ...
        //XXX Set stretchHint = NS_STRETCH_NORMAL? to honor the maxsize as close as possible?
        if (NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags)) {
          // there is an explicit value like maxsize="20pt"
          // try to maintain the aspect ratio of the char
          float aspect = mMaxSize / float(initialSize.ascent + initialSize.descent);
          container.ascent =
            std::min(container.ascent, nscoord(initialSize.ascent * aspect));
          container.descent =
            std::min(container.descent, nscoord(initialSize.descent * aspect));
          // below we use a type cast instead of a conversion to avoid a VC++ bug
          // see http://support.microsoft.com/support/kb/articles/Q115/7/05.ASP
          container.width =
            std::min(container.width, (nscoord)mMaxSize);
        }
        else { // multiplicative value
          container.ascent =
            std::min(container.ascent, nscoord(initialSize.ascent * mMaxSize));
          container.descent =
            std::min(container.descent, nscoord(initialSize.descent * mMaxSize));
          container.width =
            std::min(container.width, nscoord(initialSize.width * mMaxSize));
        }

        if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
          // re-adjust to align the char with the bottom of the initial container
          height = container.ascent + container.descent;
          container.descent = aContainerSize.descent;
          container.ascent = height - container.descent;
        }
      }

      if (mMinSize > 0.0f) {
        // if we are here, there is a user defined minsize ...
        // always allow the char to stretch in its natural direction,
        // even if it is different from the caller's direction 
        if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT &&
            aStretchDirection != mEmbellishData.direction) {
          aStretchDirection = NS_STRETCH_DIRECTION_DEFAULT;
          // but when we are not honoring the requested direction
          // we should not use the caller's container size either
          container = initialSize;
        }
        if (NS_MATHML_OPERATOR_MINSIZE_IS_ABSOLUTE(mFlags)) {
          // there is an explicit value like minsize="20pt"
          // try to maintain the aspect ratio of the char
          float aspect = mMinSize / float(initialSize.ascent + initialSize.descent);
          container.ascent =
            std::max(container.ascent, nscoord(initialSize.ascent * aspect));
          container.descent =
            std::max(container.descent, nscoord(initialSize.descent * aspect));
          container.width =
            std::max(container.width, (nscoord)mMinSize);
        }
        else { // multiplicative value
          container.ascent =
            std::max(container.ascent, nscoord(initialSize.ascent * mMinSize));
          container.descent =
            std::max(container.descent, nscoord(initialSize.descent * mMinSize));
          container.width =
            std::max(container.width, nscoord(initialSize.width * mMinSize));
        }

        if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
          // re-adjust to align the char with the bottom of the initial container
          height = container.ascent + container.descent;
          container.descent = aContainerSize.descent;
          container.ascent = height - container.descent;
        }
      }
    }

    // let the MathMLChar stretch itself...
    nsresult res = mMathMLChar.Stretch(PresContext(), aRenderingContext,
                                       aStretchDirection, container, charSize,
                                       stretchHint,
                                       StyleVisibility()->mDirection);
    if (NS_FAILED(res)) {
      // gracefully handle cases where stretching the char failed (i.e., GetBoundingMetrics failed)
      // clear our 'form' to behave as if the operator wasn't in the dictionary
      mFlags &= ~NS_MATHML_OPERATOR_FORM;
      useMathMLChar = false;
    }
  }

  // Place our children using the default method
  // This will allow our child text frame to get its DidReflow()
  nsresult rv = Place(aRenderingContext, true, aDesiredStretchSize);
  if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
    // Make sure the child frames get their DidReflow() calls.
    DidReflowChildren(mFrames.FirstChild());
  }

  if (useMathMLChar) {
    // update our bounding metrics... it becomes that of our MathML char
    mBoundingMetrics = charSize;

    // if the returned direction is 'unsupported', the char didn't actually change. 
    // So we do the centering only if necessary
    if (mMathMLChar.GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED ||
        NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {

      bool largeopOnly =
        (NS_STRETCH_LARGEOP & stretchHint) != 0 &&
        (NS_STRETCH_VARIABLE_MASK & stretchHint) == 0;

      if (isVertical || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
        // the desired size returned by mMathMLChar maybe different
        // from the size of the container.
        // the mMathMLChar.mRect.y calculation is subtle, watch out!!!

        height = mBoundingMetrics.ascent + mBoundingMetrics.descent;
        if (NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags) ||
            NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
          // For symmetric and vertical operators, or for operators that are always
          // centered ('+', '*', etc) we want to center about the axis of the container
          mBoundingMetrics.descent = height/2 - axisHeight;
        } else if (!largeopOnly) {
          // Align the center of the char with the center of the container
          mBoundingMetrics.descent = height/2 +
            (container.ascent + container.descent)/2 - container.ascent;
        } // else align the baselines
        mBoundingMetrics.ascent = height - mBoundingMetrics.descent;
      }
    }
  }

  // Fixup for the final height.
  // On one hand, our stretchy height can sometimes be shorter than surrounding
  // ASCII chars, e.g., arrow symbols have |mBoundingMetrics.ascent + leading|
  // that is smaller than the ASCII's ascent, hence when painting the background
  // later, it won't look uniform along the line.
  // On the other hand, sometimes we may leave too much gap when our glyph happens
  // to come from a font with tall glyphs. For example, since CMEX10 has very tall
  // glyphs, its natural font metrics are large, even if we pick a small glyph
  // whose size is comparable to the size of a normal ASCII glyph.
  // So to avoid uneven spacing in either of these two cases, we use the height
  // of the ASCII font as a reference and try to match it if possible.

  // special case for accents... keep them short to improve mouse operations...
  // an accent can only be the non-first child of <mover>, <munder>, <munderover>
  bool isAccent =
    NS_MATHML_EMBELLISH_IS_ACCENT(mEmbellishData.flags);
  if (isAccent) {
    nsEmbellishData parentData;
    GetEmbellishDataFrom(GetParent(), parentData);
    isAccent =
       (NS_MATHML_EMBELLISH_IS_ACCENTOVER(parentData.flags) ||
        NS_MATHML_EMBELLISH_IS_ACCENTUNDER(parentData.flags)) &&
       parentData.coreFrame != this;
  }
  if (isAccent && firstChild) {
    // see bug 188467 for what is going on here
    nscoord dy = aDesiredStretchSize.BlockStartAscent() -
      (mBoundingMetrics.ascent + leading);
    aDesiredStretchSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
    aDesiredStretchSize.Height() = aDesiredStretchSize.BlockStartAscent() +
                                   mBoundingMetrics.descent;

    firstChild->SetPosition(firstChild->GetPosition() - nsPoint(0, dy));
  }
  else if (useMathMLChar) {
    nscoord ascent = fm->MaxAscent();
    nscoord descent = fm->MaxDescent();
    aDesiredStretchSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent + leading, ascent));
    aDesiredStretchSize.Height() = aDesiredStretchSize.BlockStartAscent() +
                                 std::max(mBoundingMetrics.descent + leading, descent);
  }
  aDesiredStretchSize.Width() = mBoundingMetrics.width;
  aDesiredStretchSize.mBoundingMetrics = mBoundingMetrics;
  mReference.x = 0;
  mReference.y = aDesiredStretchSize.BlockStartAscent();
  // Place our mMathMLChar, its origin is in our coordinate system
  if (useMathMLChar) {
    nscoord dy = aDesiredStretchSize.BlockStartAscent() - mBoundingMetrics.ascent;
    mMathMLChar.SetRect(nsRect(0, dy, charSize.width, charSize.ascent + charSize.descent));
  }

  // Before we leave... there is a last item in the check-list:
  // If our parent is not embellished, it means we are the outermost embellished
  // container and so we put the spacing, otherwise we don't include the spacing,
  // the outermost embellished container will take care of it.

  if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {

    // Account the spacing if we are not an accent with explicit attributes
    nscoord leadingSpace = mEmbellishData.leadingSpace;
    if (isAccent && !NS_MATHML_OPERATOR_HAS_LSPACE_ATTR(mFlags)) {
      leadingSpace = 0;
    }
    nscoord trailingSpace = mEmbellishData.trailingSpace;
    if (isAccent && !NS_MATHML_OPERATOR_HAS_RSPACE_ATTR(mFlags)) {
      trailingSpace = 0;
    }

    mBoundingMetrics.width += leadingSpace + trailingSpace;
    aDesiredStretchSize.Width() = mBoundingMetrics.width;
    aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;

    nscoord dx = (StyleVisibility()->mDirection ?
                  trailingSpace : leadingSpace);
    if (dx) {
      // adjust the offsets
      mBoundingMetrics.leftBearing += dx;
      mBoundingMetrics.rightBearing += dx;
      aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
      aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;

      if (useMathMLChar) {
        nsRect rect;
        mMathMLChar.GetRect(rect);
        mMathMLChar.SetRect(nsRect(rect.x + dx, rect.y,
                                   rect.width, rect.height));
      }
      else {
        nsIFrame* childFrame = firstChild;
        while (childFrame) {
          childFrame->SetPosition(childFrame->GetPosition() +
                                  nsPoint(dx, 0));
          childFrame = childFrame->GetNextSibling();
        }
      }
    }
  }

  // Finished with these:
  ClearSavedChildMetrics();
  // Set our overflow area
  GatherAndStoreOverflow(&aDesiredStretchSize);

  // There used to be code here to change the height of the child frame to
  // change the caret height, but the text frame that manages the caret is now
  // not a direct child but wrapped in a block frame.  See also bug 412033.

  return NS_OK;
}
/* virtual */ nsresult
nsMathMLmpaddedFrame::Place(nsRenderingContext& aRenderingContext,
                            bool                 aPlaceOrigin,
                            nsHTMLReflowMetrics& aDesiredSize)
{
  nsresult rv =
    nsMathMLContainerFrame::Place(aRenderingContext, false, aDesiredSize);
  if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
    DidReflowChildren(GetFirstPrincipalChild());
    return rv;
  }

  nscoord height = aDesiredSize.BlockStartAscent();
  nscoord depth  = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
  // The REC says:
  //
  // "The lspace attribute ('leading' space) specifies the horizontal location
  // of the positioning point of the child content with respect to the
  // positioning point of the mpadded element. By default they coincide, and
  // therefore absolute values for lspace have the same effect as relative
  // values."
  //
  // "MathML renderers should ensure that, except for the effects of the
  // attributes, the relative spacing between the contents of the mpadded
  // element and surrounding MathML elements would not be modified by replacing
  // an mpadded element with an mrow element with the same content, even if
  // linebreaking occurs within the mpadded element."
  //
  // (http://www.w3.org/TR/MathML/chapter3.html#presm.mpadded)
  // 
  // "In those discussions, the terms leading and trailing are used to specify
  // a side of an object when which side to use depends on the directionality;
  // ie. leading means left in LTR but right in RTL."
  // (http://www.w3.org/TR/MathML/chapter3.html#presm.bidi.math)
  nscoord lspace = 0;
  // In MathML3, "width" will be the bounding box width and "advancewidth" will
  // refer "to the horizontal distance between the positioning point of the
  // mpadded and the positioning point for the following content".  MathML2
  // doesn't make the distinction.
  nscoord width  = aDesiredSize.Width();
  nscoord voffset = 0;

  int32_t pseudoUnit;
  nscoord initialWidth = width;
  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);

  // update width
  pseudoUnit = (mWidthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
             ? NS_MATHML_PSEUDO_UNIT_WIDTH : mWidthPseudoUnit;
  UpdateValue(mWidthSign, pseudoUnit, mWidth,
              aDesiredSize, width, fontSizeInflation);
  width = std::max(0, width);

  // update "height" (this is the ascent in the terminology of the REC)
  pseudoUnit = (mHeightPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
             ? NS_MATHML_PSEUDO_UNIT_HEIGHT : mHeightPseudoUnit;
  UpdateValue(mHeightSign, pseudoUnit, mHeight,
              aDesiredSize, height, fontSizeInflation);
  height = std::max(0, height);

  // update "depth" (this is the descent in the terminology of the REC)
  pseudoUnit = (mDepthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
             ? NS_MATHML_PSEUDO_UNIT_DEPTH : mDepthPseudoUnit;
  UpdateValue(mDepthSign, pseudoUnit, mDepth,
              aDesiredSize, depth, fontSizeInflation);
  depth = std::max(0, depth);

  // update lspace
  if (mLeadingSpacePseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) {
    pseudoUnit = mLeadingSpacePseudoUnit;
    UpdateValue(mLeadingSpaceSign, pseudoUnit, mLeadingSpace,
                aDesiredSize, lspace, fontSizeInflation);
  }

  // update voffset
  if (mVerticalOffsetPseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) {
    pseudoUnit = mVerticalOffsetPseudoUnit;
    UpdateValue(mVerticalOffsetSign, pseudoUnit, mVerticalOffset,
                aDesiredSize, voffset, fontSizeInflation);
  }
  // do the padding now that we have everything
  // The idea here is to maintain the invariant that <mpadded>...</mpadded> (i.e.,
  // with no attributes) looks the same as <mrow>...</mrow>. But when there are
  // attributes, tweak our metrics and move children to achieve the desired visual
  // effects.

  if ((StyleVisibility()->mDirection ?
       mWidthSign : mLeadingSpaceSign) != NS_MATHML_SIGN_INVALID) {
    // there was padding on the left. dismiss the left italic correction now
    // (so that our parent won't correct us)
    mBoundingMetrics.leftBearing = 0;
  }

  if ((StyleVisibility()->mDirection ?
       mLeadingSpaceSign : mWidthSign) != NS_MATHML_SIGN_INVALID) {
    // there was padding on the right. dismiss the right italic correction now
    // (so that our parent won't correct us)
    mBoundingMetrics.width = width;
    mBoundingMetrics.rightBearing = mBoundingMetrics.width;
  }

  nscoord dx = (StyleVisibility()->mDirection ?
                width - initialWidth - lspace : lspace);
    
  aDesiredSize.SetBlockStartAscent(height);
  aDesiredSize.Width() = mBoundingMetrics.width;
  aDesiredSize.Height() = depth + aDesiredSize.BlockStartAscent();
  mBoundingMetrics.ascent = height;
  mBoundingMetrics.descent = depth;
  aDesiredSize.mBoundingMetrics = mBoundingMetrics;

  mReference.x = 0;
  mReference.y = aDesiredSize.BlockStartAscent();

  if (aPlaceOrigin) {
    // Finish reflowing child frames, positioning their origins.
    PositionRowChildFrames(dx, aDesiredSize.BlockStartAscent() - voffset);
  }

  return NS_OK;
}
// exported routine that both munderover and mmultiscripts share.
// munderover uses this when movablelimits is set.
nsresult
nsMathMLmmultiscriptsFrame::PlaceMultiScript(nsPresContext*      aPresContext,
                                        nsRenderingContext& aRenderingContext,
                                        bool                 aPlaceOrigin,
                                        nsHTMLReflowMetrics& aDesiredSize,
                                        nsMathMLContainerFrame* aFrame,
                                        nscoord              aUserSubScriptShift,
                                        nscoord              aUserSupScriptShift,
                                        float                aFontSizeInflation)
{
  nsIAtom* tag = aFrame->GetContent()->NodeInfo()->NameAtom();

  // This function deals with both munderover etc. as well as msubsup etc.
  // As the former behaves identically to the later, we treat it as such
  // to avoid additional checks later.
  if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mover_))
    tag = nsGkAtoms::msup_;
  else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munder_))
    tag = nsGkAtoms::msub_;
  else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munderover_))
    tag = nsGkAtoms::msubsup_;

  nsBoundingMetrics bmFrame;

  nscoord minShiftFromXHeight, subDrop, supDrop;

  ////////////////////////////////////////
  // Initialize super/sub shifts that
  // depend only on the current font
  ////////////////////////////////////////

  nsIFrame* baseFrame = aFrame->GetFirstPrincipalChild();

  if (!baseFrame) {
    if (tag == nsGkAtoms::mmultiscripts_)
      aFrame->ReportErrorToConsole("NoBase");
    else
      aFrame->ReportChildCountError();
    return aFrame->ReflowError(aRenderingContext, aDesiredSize);
  }

  // get x-height (an ex)
  const nsStyleFont* font = aFrame->StyleFont();
  RefPtr<nsFontMetrics> fm;
  nsLayoutUtils::GetFontMetricsForFrame(baseFrame, getter_AddRefs(fm),
                                        aFontSizeInflation);

  nscoord xHeight = fm->XHeight();

  nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
  gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
  // scriptspace from TeX for extra spacing after sup/subscript
  nscoord scriptSpace;
  if (mathFont) {
    scriptSpace =
      mathFont->GetMathConstant(gfxFontEntry::SpaceAfterScript, oneDevPixel);
  } else {
    // (0.5pt in plain TeX)
    scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f);
  }

  // force the scriptSpace to be at least 1 pixel
  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
  scriptSpace = std::max(onePixel, scriptSpace);

  /////////////////////////////////////
  // first the shift for the subscript

  nscoord subScriptShift;
  if (mathFont) {
    // Try and get the sub script shift from the MATH table. Note that contrary
    // to TeX we only have one parameter.
    subScriptShift =
      mathFont->GetMathConstant(gfxFontEntry::SubscriptShiftDown, oneDevPixel);
  } else {
    // subScriptShift{1,2}
    // = minimum amount to shift the subscript down
    // = sub{1,2} in TeXbook
    // subScriptShift1 = subscriptshift attribute * x-height
    nscoord subScriptShift1, subScriptShift2;
    // Get subScriptShift{1,2} default from font
    GetSubScriptShifts (fm, subScriptShift1, subScriptShift2);
    if (tag == nsGkAtoms::msub_) {
      subScriptShift = subScriptShift1;
    } else {
      subScriptShift = std::max(subScriptShift1, subScriptShift2);
    }
  }

  if (0 < aUserSubScriptShift) {
    // the user has set the subscriptshift attribute
    subScriptShift = std::max(subScriptShift, aUserSubScriptShift);
  }

  /////////////////////////////////////
  // next the shift for the superscript

  nscoord supScriptShift;
  nsPresentationData presentationData;
  aFrame->GetPresentationData(presentationData);
  if (mathFont) {
    // Try and get the super script shift from the MATH table. Note that
    // contrary to TeX we only have two parameters.
    supScriptShift = mathFont->
      GetMathConstant(NS_MATHML_IS_COMPRESSED(presentationData.flags) ?
                      gfxFontEntry::SuperscriptShiftUpCramped :
                      gfxFontEntry::SuperscriptShiftUp,
                      oneDevPixel);
  } else {
    // supScriptShift{1,2,3}
    // = minimum amount to shift the supscript up
    // = sup{1,2,3} in TeX
    // supScriptShift1 = superscriptshift attribute * x-height
    // Note that there are THREE values for supscript shifts depending
    // on the current style
    nscoord supScriptShift1, supScriptShift2, supScriptShift3;
    // Set supScriptShift{1,2,3} default from font
    GetSupScriptShifts (fm, supScriptShift1, supScriptShift2, supScriptShift3);

    // get sup script shift depending on current script level and display style
    // Rule 18c, App. G, TeXbook
    if (font->mScriptLevel == 0 &&
        font->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK &&
        !NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
      // Style D in TeXbook
      supScriptShift = supScriptShift1;
    } else if (NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
      // Style C' in TeXbook = D',T',S',SS'
      supScriptShift = supScriptShift3;
    } else {
      // everything else = T,S,SS
      supScriptShift = supScriptShift2;
    }
  }

  if (0 < aUserSupScriptShift) {
    // the user has set the supscriptshift attribute
    supScriptShift = std::max(supScriptShift, aUserSupScriptShift);
  }

  ////////////////////////////////////
  // Get the children's sizes
  ////////////////////////////////////

  const WritingMode wm(aDesiredSize.GetWritingMode());
  nscoord width = 0, prescriptsWidth = 0, rightBearing = 0;
  nscoord minSubScriptShift = 0, minSupScriptShift = 0;
  nscoord trySubScriptShift = subScriptShift;
  nscoord trySupScriptShift = supScriptShift;
  nscoord maxSubScriptShift = subScriptShift;
  nscoord maxSupScriptShift = supScriptShift;
  nsHTMLReflowMetrics baseSize(wm);
  nsHTMLReflowMetrics subScriptSize(wm);
  nsHTMLReflowMetrics supScriptSize(wm);
  nsHTMLReflowMetrics multiSubSize(wm), multiSupSize(wm);
  baseFrame = nullptr;
  nsIFrame* subScriptFrame = nullptr;
  nsIFrame* supScriptFrame = nullptr;
  nsIFrame* prescriptsFrame = nullptr; // frame of <mprescripts/>, if there.

  bool firstPrescriptsPair = false;
  nsBoundingMetrics bmBase, bmSubScript, bmSupScript, bmMultiSub, bmMultiSup;
  multiSubSize.SetBlockStartAscent(-0x7FFFFFFF);
  multiSupSize.SetBlockStartAscent(-0x7FFFFFFF);
  bmMultiSub.ascent = bmMultiSup.ascent = -0x7FFFFFFF;
  bmMultiSub.descent = bmMultiSup.descent = -0x7FFFFFFF;
  nscoord italicCorrection = 0;

  nsBoundingMetrics boundingMetrics;
  boundingMetrics.width = 0;
  boundingMetrics.ascent = boundingMetrics.descent = -0x7FFFFFFF;
  aDesiredSize.Width() = aDesiredSize.Height() = 0;

  int32_t count = 0;
  bool foundNoneTag = false;

  // Boolean to determine whether the current child is a subscript.
  // Note that only msup starts with a superscript.
  bool isSubScript = (tag != nsGkAtoms::msup_);

  nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
  while (childFrame) {
    if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) {
      if (tag != nsGkAtoms::mmultiscripts_) {
        if (aPlaceOrigin) {
          aFrame->ReportInvalidChildError(nsGkAtoms::mprescripts_);
        }
        return aFrame->ReflowError(aRenderingContext, aDesiredSize);
      }
      if (prescriptsFrame) {
        // duplicate <mprescripts/> found
        // report an error, encourage people to get their markups in order
        if (aPlaceOrigin) {
          aFrame->ReportErrorToConsole("DuplicateMprescripts");
        }
        return aFrame->ReflowError(aRenderingContext, aDesiredSize);
      }
      if (!isSubScript) {
        if (aPlaceOrigin) {
          aFrame->ReportErrorToConsole("SubSupMismatch");
        }
        return aFrame->ReflowError(aRenderingContext, aDesiredSize);
      }

      prescriptsFrame = childFrame;
      firstPrescriptsPair = true;
    } else if (0 == count) {
      // base

      if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::none)) {
        if (tag == nsGkAtoms::mmultiscripts_) {
          if (aPlaceOrigin) {
            aFrame->ReportErrorToConsole("NoBase");
          }
          return aFrame->ReflowError(aRenderingContext, aDesiredSize);
        } else {
          //A different error message is triggered later for the other tags
          foundNoneTag = true;
        }
      }
      baseFrame = childFrame;
      GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);

      if (tag != nsGkAtoms::msub_) {
        // Apply italics correction if there is the potential for a 
        // postsupscript.
        GetItalicCorrection(bmBase, italicCorrection);
        // If italics correction is applied, we always add "a little to spare"
        // (see TeXbook Ch.11, p.64), as we estimate the italic creation
        // ourselves and it isn't the same as TeX.
        italicCorrection += onePixel;
      }

      // we update boundingMetrics.{ascent,descent} with that
      // of the baseFrame only after processing all the sup/sub pairs
      boundingMetrics.width = bmBase.width;
      boundingMetrics.rightBearing = bmBase.rightBearing;
      boundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten
    } else {
      // super/subscript block
      if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::none)) {
        foundNoneTag = true;
      }

      if (isSubScript) {
        // subscript
        subScriptFrame = childFrame;
        GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
        // get the subdrop from the subscript font
        GetSubDropFromChild (subScriptFrame, subDrop, aFontSizeInflation);
        // parameter v, Rule 18a, App. G, TeXbook
        minSubScriptShift = bmBase.descent + subDrop;
        trySubScriptShift = std::max(minSubScriptShift,subScriptShift);
        multiSubSize.SetBlockStartAscent(
           std::max(multiSubSize.BlockStartAscent(),
                    subScriptSize.BlockStartAscent()));
        bmMultiSub.ascent = std::max(bmMultiSub.ascent, bmSubScript.ascent);
        bmMultiSub.descent = std::max(bmMultiSub.descent, bmSubScript.descent);
        multiSubSize.Height() = 
          std::max(multiSubSize.Height(),
                   subScriptSize.Height() - subScriptSize.BlockStartAscent());
        if (bmSubScript.width)
          width = bmSubScript.width + scriptSpace;
        rightBearing = bmSubScript.rightBearing;

        if (tag == nsGkAtoms::msub_) {
          boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
          boundingMetrics.width += width;

          nscoord subscriptTopMax;
          if (mathFont) {
            subscriptTopMax =
              mathFont->GetMathConstant(gfxFontEntry::SubscriptTopMax,
                                        oneDevPixel);
          } else {
            // get min subscript shift limit from x-height
            // = h(x) - 4/5 * sigma_5, Rule 18b, App. G, TeXbook
            subscriptTopMax = NSToCoordRound((4.0f/5.0f) * xHeight);
          }
          nscoord minShiftFromXHeight = bmSubScript.ascent - subscriptTopMax;
          maxSubScriptShift = std::max(trySubScriptShift,minShiftFromXHeight);

          maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
          trySubScriptShift = subScriptShift;
        }
      } else {
        // supscript
        supScriptFrame = childFrame;
        GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
        // get the supdrop from the supscript font
        GetSupDropFromChild (supScriptFrame, supDrop, aFontSizeInflation);
        // parameter u, Rule 18a, App. G, TeXbook
        minSupScriptShift = bmBase.ascent - supDrop;
        nscoord superscriptBottomMin;
        if (mathFont) {
          superscriptBottomMin =
            mathFont->GetMathConstant(gfxFontEntry::SuperscriptBottomMin,
                                      oneDevPixel);
        } else {
          // get min supscript shift limit from x-height
          // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook
          superscriptBottomMin = NSToCoordRound((1.0f / 4.0f) * xHeight);
        }
        minShiftFromXHeight = bmSupScript.descent + superscriptBottomMin;
        trySupScriptShift = std::max(minSupScriptShift,
                                     std::max(minShiftFromXHeight,
                                              supScriptShift));
        multiSupSize.SetBlockStartAscent(
          std::max(multiSupSize.BlockStartAscent(),
                   supScriptSize.BlockStartAscent()));
        bmMultiSup.ascent = std::max(bmMultiSup.ascent, bmSupScript.ascent);
        bmMultiSup.descent = std::max(bmMultiSup.descent, bmSupScript.descent);
        multiSupSize.Height() =
          std::max(multiSupSize.Height(),
                   supScriptSize.Height() - supScriptSize.BlockStartAscent());

        if (bmSupScript.width)
          width = std::max(width, bmSupScript.width + scriptSpace);

        if (!prescriptsFrame) { // we are still looping over base & postscripts
          rightBearing = std::max(rightBearing,
                                  italicCorrection + bmSupScript.rightBearing);
          boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
          boundingMetrics.width += width;
        } else {
          prescriptsWidth += width;
          if (firstPrescriptsPair) {
            firstPrescriptsPair = false;
            boundingMetrics.leftBearing =
              std::min(bmSubScript.leftBearing, bmSupScript.leftBearing);
          }
        }
        width = rightBearing = 0;

        // negotiate between the various shifts so that
        // there is enough gap between the sup and subscripts
        // Rule 18e, App. G, TeXbook
        if (tag == nsGkAtoms::mmultiscripts_ || 
            tag == nsGkAtoms::msubsup_) {
          nscoord subSuperscriptGapMin;
          if (mathFont) {
            subSuperscriptGapMin =
              mathFont->GetMathConstant(gfxFontEntry::SubSuperscriptGapMin,
                                        oneDevPixel);
          } else {
            nscoord ruleSize;
            GetRuleThickness(aRenderingContext, fm, ruleSize);
            subSuperscriptGapMin = 4 * ruleSize;
          }
          nscoord gap =
            (trySupScriptShift - bmSupScript.descent) -
            (bmSubScript.ascent - trySubScriptShift);
          if (gap < subSuperscriptGapMin) {
            // adjust trySubScriptShift to get a gap of subSuperscriptGapMin
            trySubScriptShift += subSuperscriptGapMin - gap;
          }

          // next we want to ensure that the bottom of the superscript
          // will be > superscriptBottomMaxWithSubscript
          nscoord superscriptBottomMaxWithSubscript;
          if (mathFont) {
            superscriptBottomMaxWithSubscript = mathFont->
              GetMathConstant(gfxFontEntry::SuperscriptBottomMaxWithSubscript,
                              oneDevPixel);
          } else {
            superscriptBottomMaxWithSubscript =
              NSToCoordRound((4.0f / 5.0f) * xHeight);
          }
          gap = superscriptBottomMaxWithSubscript -
            (trySupScriptShift - bmSupScript.descent);
          if (gap > 0) {
            trySupScriptShift += gap;
            trySubScriptShift -= gap;
          }
        }

        maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
        maxSupScriptShift = std::max(maxSupScriptShift, trySupScriptShift);

        trySubScriptShift = subScriptShift;
        trySupScriptShift = supScriptShift;
      }

      isSubScript = !isSubScript;
    }
    count++;
    childFrame = childFrame->GetNextSibling();
  }

  //NoBase error may also have been reported above
  if ((count != 2 && (tag == nsGkAtoms::msup_ || tag == nsGkAtoms::msub_)) ||
      (count != 3 && tag == nsGkAtoms::msubsup_) || !baseFrame ||
      (foundNoneTag && tag != nsGkAtoms::mmultiscripts_) ||
      (!isSubScript && tag == nsGkAtoms::mmultiscripts_)) {
    // report an error, encourage people to get their markups in order
    if (aPlaceOrigin) {
      if ((count != 2 && (tag == nsGkAtoms::msup_ || 
          tag == nsGkAtoms::msub_)) ||
          (count != 3 && tag == nsGkAtoms::msubsup_ )) {
        aFrame->ReportChildCountError();
      } else if (foundNoneTag && tag != nsGkAtoms::mmultiscripts_) {
        aFrame->ReportInvalidChildError(nsGkAtoms::none);
      } else if (!baseFrame) {
        aFrame->ReportErrorToConsole("NoBase");
      } else {
        aFrame->ReportErrorToConsole("SubSupMismatch");
      }
    }
    return aFrame->ReflowError(aRenderingContext, aDesiredSize);
  }

  // we left out the width of prescripts, so ...
  boundingMetrics.rightBearing += prescriptsWidth;
  boundingMetrics.width += prescriptsWidth;

  // Zero out the shifts in where a frame isn't present to avoid the potential
  // for overflow.
  if (!subScriptFrame)
    maxSubScriptShift = 0;
  if (!supScriptFrame)
    maxSupScriptShift = 0;

  // we left out the base during our bounding box updates, so ...
  if (tag == nsGkAtoms::msub_) {
    boundingMetrics.ascent = std::max(bmBase.ascent,
                                      bmMultiSub.ascent - maxSubScriptShift);
  } else {
    boundingMetrics.ascent =
      std::max(bmBase.ascent, (bmMultiSup.ascent + maxSupScriptShift));
  }
  if (tag == nsGkAtoms::msup_) {
    boundingMetrics.descent = std::max(bmBase.descent,
                                       bmMultiSup.descent - maxSupScriptShift);
  } else {
    boundingMetrics.descent =
      std::max(bmBase.descent, (bmMultiSub.descent + maxSubScriptShift));
  }
  aFrame->SetBoundingMetrics(boundingMetrics);

  // get the reflow metrics ...
  aDesiredSize.SetBlockStartAscent(
    std::max(baseSize.BlockStartAscent(),
             std::max(multiSubSize.BlockStartAscent() - maxSubScriptShift,
                      multiSupSize.BlockStartAscent() + maxSupScriptShift)));
  aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
    std::max(baseSize.Height() - baseSize.BlockStartAscent(),
             std::max(multiSubSize.Height() + maxSubScriptShift,
                      multiSupSize.Height() - maxSupScriptShift));
  aDesiredSize.Width() = boundingMetrics.width;
  aDesiredSize.mBoundingMetrics = boundingMetrics;

  aFrame->SetReference(nsPoint(0, aDesiredSize.BlockStartAscent()));

  //////////////////
  // Place Children

  // Place prescripts, followed by base, and then postscripts.
  // The list of frames is in the order: {base} {postscripts} {prescripts}
  // We go over the list in a circular manner, starting at <prescripts/>

  if (aPlaceOrigin) {
    nscoord dx = 0, dy = 0;

    // With msub and msup there is only one element and 
    // subscriptFrame/supScriptFrame have already been set above where
    // relevant.  In these cases we skip to the reflow part.
    if (tag == nsGkAtoms::msub_ || tag == nsGkAtoms::msup_)
      count = 1;
    else
      count = 0;
    childFrame = prescriptsFrame;
    bool isPreScript = true;
    do {
      if (!childFrame) { // end of prescripts,
        isPreScript = false;
        // place the base ...
        childFrame = baseFrame;
        dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
        FinishReflowChild (baseFrame, aPresContext, baseSize, nullptr,
                           aFrame->MirrorIfRTL(aDesiredSize.Width(),
                                               baseSize.Width(),
                                               dx),
                           dy, 0);
        dx += bmBase.width;
      } else if (prescriptsFrame == childFrame) {
        // Clear reflow flags of prescripts frame.
        prescriptsFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
      } else {
        // process each sup/sub pair
        if (0 == count) {
          subScriptFrame = childFrame;
          count = 1;
        } else if (1 == count) {
          if (tag != nsGkAtoms::msub_)
            supScriptFrame = childFrame;
          count = 0;

          // get the ascent/descent of sup/subscripts stored in their rects
          // rect.x = descent, rect.y = ascent
          if (subScriptFrame)
            GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
          if (supScriptFrame)
            GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);

          width = std::max(subScriptSize.Width(), supScriptSize.Width());

          if (subScriptFrame) {
            nscoord x = dx;
            // prescripts should be right aligned
            // https://bugzilla.mozilla.org/show_bug.cgi?id=928675
            if (isPreScript)
              x += width - subScriptSize.Width();
            dy = aDesiredSize.BlockStartAscent() - subScriptSize.BlockStartAscent() +
              maxSubScriptShift;
            FinishReflowChild (subScriptFrame, aPresContext, subScriptSize,
                               nullptr,
                               aFrame->MirrorIfRTL(aDesiredSize.Width(),
                                                   subScriptSize.Width(),
                                                   x),
                               dy, 0);
          }

          if (supScriptFrame) {
            nscoord x = dx;
            if (isPreScript) {
              x += width - supScriptSize.Width();
            } else {
              // post superscripts are shifted by the italic correction value
              x += italicCorrection;
            }
            dy = aDesiredSize.BlockStartAscent() - supScriptSize.BlockStartAscent() -
              maxSupScriptShift;
            FinishReflowChild (supScriptFrame, aPresContext, supScriptSize,
                               nullptr,
                               aFrame->MirrorIfRTL(aDesiredSize.Width(),
                                                   supScriptSize.Width(),
                                                   x),
                               dy, 0);
          }
          dx += width + scriptSpace;
        }
      }
      childFrame = childFrame->GetNextSibling();
    } while (prescriptsFrame != childFrame);
  }

  return NS_OK;
}
Ejemplo n.º 13
0
void
nsLeafBoxFrame::Reflow(nsPresContext*   aPresContext,
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus)
{
  // This is mostly a copy of nsBoxFrame::Reflow().
  // We aren't able to share an implementation because of the frame
  // class hierarchy.  If you make changes here, please keep
  // nsBoxFrame::Reflow in sync.

  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("nsLeafBoxFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

  NS_ASSERTION(aReflowState.ComputedWidth() >=0 &&
               aReflowState.ComputedHeight() >= 0, "Computed Size < 0");

#ifdef DO_NOISY_REFLOW
  printf("\n-------------Starting LeafBoxFrame Reflow ----------------------------\n");
  printf("%p ** nsLBF::Reflow %d R: ", this, myCounter++);
  switch (aReflowState.reason) {
    case eReflowReason_Initial:
      printf("Ini");break;
    case eReflowReason_Incremental:
      printf("Inc");break;
    case eReflowReason_Resize:
      printf("Rsz");break;
    case eReflowReason_StyleChange:
      printf("Sty");break;
    case eReflowReason_Dirty:
      printf("Drt ");
      break;
    default:printf("<unknown>%d", aReflowState.reason);break;
  }
  
  printSize("AW", aReflowState.AvailableWidth());
  printSize("AH", aReflowState.AvailableHeight());
  printSize("CW", aReflowState.ComputedWidth());
  printSize("CH", aReflowState.ComputedHeight());

  printf(" *\n");

#endif

  aStatus = NS_FRAME_COMPLETE;

  // create the layout state
  nsBoxLayoutState state(aPresContext, aReflowState.rendContext);

  nsSize computedSize(aReflowState.ComputedWidth(),aReflowState.ComputedHeight());

  nsMargin m;
  m = aReflowState.ComputedPhysicalBorderPadding();

  //GetBorderAndPadding(m);

  // this happens sometimes. So lets handle it gracefully.
  if (aReflowState.ComputedHeight() == 0) {
    nsSize minSize = GetMinSize(state);
    computedSize.height = minSize.height - m.top - m.bottom;
  }

  nsSize prefSize(0,0);

  // if we are told to layout intrinic then get our preferred size.
  if (computedSize.width == NS_INTRINSICSIZE || computedSize.height == NS_INTRINSICSIZE) {
     prefSize = GetPrefSize(state);
     nsSize minSize = GetMinSize(state);
     nsSize maxSize = GetMaxSize(state);
     prefSize = BoundsCheck(minSize, prefSize, maxSize);
  }

  // get our desiredSize
  if (aReflowState.ComputedWidth() == NS_INTRINSICSIZE) {
    computedSize.width = prefSize.width;
  } else {
    computedSize.width += m.left + m.right;
  }

  if (aReflowState.ComputedHeight() == NS_INTRINSICSIZE) {
    computedSize.height = prefSize.height;
  } else {
    computedSize.height += m.top + m.bottom;
  }

  // handle reflow state min and max sizes
  // XXXbz the width handling here seems to be wrong, since
  // mComputedMin/MaxWidth is a content-box size, whole
  // computedSize.width is a border-box size...
  if (computedSize.width > aReflowState.ComputedMaxWidth())
    computedSize.width = aReflowState.ComputedMaxWidth();

  if (computedSize.width < aReflowState.ComputedMinWidth())
    computedSize.width = aReflowState.ComputedMinWidth();

  // Now adjust computedSize.height for our min and max computed
  // height.  The only problem is that those are content-box sizes,
  // while computedSize.height is a border-box size.  So subtract off
  // m.TopBottom() before adjusting, then readd it.
  computedSize.height = std::max(0, computedSize.height - m.TopBottom());
  computedSize.height = NS_CSS_MINMAX(computedSize.height,
                                      aReflowState.ComputedMinHeight(),
                                      aReflowState.ComputedMaxHeight());
  computedSize.height += m.TopBottom();

  nsRect r(mRect.x, mRect.y, computedSize.width, computedSize.height);

  SetBounds(state, r);
 
  // layout our children
  Layout(state);
  
  // ok our child could have gotten bigger. So lets get its bounds
  aDesiredSize.Width() = mRect.width;
  aDesiredSize.Height() = mRect.height;
  aDesiredSize.SetBlockStartAscent(GetBoxAscent(state));

  // the overflow rect is set in SetBounds() above
  aDesiredSize.mOverflowAreas = GetOverflowAreas();

#ifdef DO_NOISY_REFLOW
  {
    printf("%p ** nsLBF(done) W:%d H:%d  ", this, aDesiredSize.Width(), aDesiredSize.Height());

    if (maxElementWidth) {
      printf("MW:%d\n", *maxElementWidth); 
    } else {
      printf("MW:?\n"); 
    }

  }
#endif
}
Ejemplo n.º 14
0
void
nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext,
                                               nsHTMLReflowMetrics& aButtonDesiredSize,
                                               const nsHTMLReflowState& aButtonReflowState,
                                               nsIFrame* aFirstKid)
{
  // Buttons have some bonus renderer-determined border/padding,
  // which occupies part of the button's content-box area:
  const nsMargin focusPadding = mRenderer.GetAddedButtonBorderAndPadding();

  WritingMode wm = aFirstKid->GetWritingMode();
  LogicalSize availSize = aButtonReflowState.ComputedSize(GetWritingMode());
  availSize.BSize(wm) = NS_INTRINSICSIZE;

  // Indent the child inside us by the focus border. We must do this separate
  // from the regular border.
  availSize.ISize(wm) -= LogicalMargin(wm, focusPadding).IStartEnd(wm);

  // See whether out availSize's width is big enough.  If it's smaller than our
  // intrinsic min width, that means that the kid wouldn't really fit; for a
  // better look in such cases we adjust the available width and our left
  // offset to allow the kid to spill left into our padding.
  nscoord xoffset = focusPadding.left +
    aButtonReflowState.ComputedPhysicalBorderPadding().left;
  nscoord extrawidth = GetMinISize(aButtonReflowState.rendContext) -
    aButtonReflowState.ComputedWidth();
  if (extrawidth > 0) {
    nscoord extraleft = extrawidth / 2;
    nscoord extraright = extrawidth - extraleft;
    NS_ASSERTION(extraright >=0, "How'd that happen?");

    // Do not allow the extras to be bigger than the relevant padding
    extraleft = std::min(extraleft, aButtonReflowState.ComputedPhysicalPadding().left);
    extraright = std::min(extraright, aButtonReflowState.ComputedPhysicalPadding().right);
    xoffset -= extraleft;
    availSize.Width(wm) = availSize.Width(wm) + extraleft + extraright;
  }
  availSize.Width(wm) = std::max(availSize.Width(wm), 0);

  // Give child a clone of the button's reflow state, with height/width reduced
  // by focusPadding, so that descendants with height:100% don't protrude.
  nsHTMLReflowState adjustedButtonReflowState =
    CloneReflowStateWithReducedContentBox(aButtonReflowState, focusPadding);

  nsHTMLReflowState contentsReflowState(aPresContext,
                                        adjustedButtonReflowState,
                                        aFirstKid, availSize);

  nsReflowStatus contentsReflowStatus;
  nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState);
  ReflowChild(aFirstKid, aPresContext,
              contentsDesiredSize, contentsReflowState,
              xoffset,
              focusPadding.top + aButtonReflowState.ComputedPhysicalBorderPadding().top,
              0, contentsReflowStatus);
  MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus),
             "We gave button-contents frame unconstrained available height, "
             "so it should be complete");

  // Compute the button's content-box height:
  nscoord buttonContentBoxHeight = 0;
  if (aButtonReflowState.ComputedHeight() != NS_INTRINSICSIZE) {
    // Button has a fixed height -- that's its content-box height.
    buttonContentBoxHeight = aButtonReflowState.ComputedHeight();
  } else {
    // Button is intrinsically sized -- it should shrinkwrap the
    // button-contents' height, plus any focus-padding space:
    buttonContentBoxHeight =
      contentsDesiredSize.Height() + focusPadding.TopBottom();

    // Make sure we obey min/max-height in the case when we're doing intrinsic
    // sizing (we get it for free when we have a non-intrinsic
    // aButtonReflowState.ComputedHeight()).  Note that we do this before
    // adjusting for borderpadding, since mComputedMaxHeight and
    // mComputedMinHeight are content heights.
    buttonContentBoxHeight =
      NS_CSS_MINMAX(buttonContentBoxHeight,
                    aButtonReflowState.ComputedMinHeight(),
                    aButtonReflowState.ComputedMaxHeight());
  }

  // Center child vertically in the button
  // (technically, inside of the button's focus-padding area)
  nscoord extraSpace =
    buttonContentBoxHeight - focusPadding.TopBottom() -
    contentsDesiredSize.Height();

  nscoord yoffset = std::max(0, extraSpace / 2);

  // Adjust yoffset to be in terms of the button's frame-rect, instead of
  // its focus-padding rect:
  yoffset += focusPadding.top + aButtonReflowState.ComputedPhysicalBorderPadding().top;

  // Place the child
  FinishReflowChild(aFirstKid, aPresContext,
                    contentsDesiredSize, &contentsReflowState,
                    xoffset, yoffset, 0);

  // Make sure we have a useful 'ascent' value for the child
  if (contentsDesiredSize.BlockStartAscent() ==
      nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
    WritingMode wm = aButtonReflowState.GetWritingMode();
    contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm));
  }

  // OK, we're done with the child frame.
  // Use what we learned to populate the button frame's reflow metrics.
  //  * Button's height & width are content-box size + border-box contribution:
  aButtonDesiredSize.Width() = aButtonReflowState.ComputedWidth() +
    aButtonReflowState.ComputedPhysicalBorderPadding().LeftRight();

  aButtonDesiredSize.Height() = buttonContentBoxHeight +
    aButtonReflowState.ComputedPhysicalBorderPadding().TopBottom();

  //  * Button's ascent is its child's ascent, plus the child's y-offset
  // within our frame:
  aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.BlockStartAscent() +
                                         yoffset);

  aButtonDesiredSize.SetOverflowAreasToDesiredBounds();
}
Ejemplo n.º 15
0
void
nsMathMLmtableOuterFrame::Reflow(nsPresContext*          aPresContext,
                                 nsHTMLReflowMetrics&     aDesiredSize,
                                 const nsHTMLReflowState& aReflowState,
                                 nsReflowStatus&          aStatus)
{
  nsAutoString value;
  // we want to return a table that is anchored according to the align attribute

  nsTableOuterFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
  NS_ASSERTION(aDesiredSize.Height() >= 0, "illegal height for mtable");
  NS_ASSERTION(aDesiredSize.Width() >= 0, "illegal width for mtable");

  // see if the user has set the align attribute on the <mtable>
  int32_t rowIndex = 0;
  eAlign tableAlign = eAlign_axis;
  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, value);
  if (!value.IsEmpty()) {
    ParseAlignAttribute(value, tableAlign, rowIndex);
  }

  // adjustments if there is a specified row from where to anchor the table
  // (conceptually: when there is no row of reference, picture the table as if
  // it is wrapped in a single big fictional row at dy = 0, this way of
  // doing so allows us to have a single code path for all cases).
  nscoord dy = 0;
  WritingMode wm = aDesiredSize.GetWritingMode();
  nscoord blockSize = aDesiredSize.BSize(wm);
  nsIFrame* rowFrame = nullptr;
  if (rowIndex) {
    rowFrame = GetRowFrameAt(rowIndex);
    if (rowFrame) {
      // translate the coordinates to be relative to us and in our writing mode
      nsIFrame* frame = rowFrame;
      LogicalRect rect(wm, frame->GetRect(),
                       aReflowState.ComputedSizeAsContainerIfConstrained());
      blockSize = rect.BSize(wm);
      do {
        dy += rect.BStart(wm);
        frame = frame->GetParent();
      } while (frame != this);
    }
  }
  switch (tableAlign) {
    case eAlign_top:
      aDesiredSize.SetBlockStartAscent(dy);
      break;
    case eAlign_bottom:
      aDesiredSize.SetBlockStartAscent(dy + blockSize);
      break;
    case eAlign_center:
      aDesiredSize.SetBlockStartAscent(dy + blockSize / 2);
      break;
    case eAlign_baseline:
      if (rowFrame) {
        // anchor the table on the baseline of the row of reference
        nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
        if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
          aDesiredSize.SetBlockStartAscent(dy + rowAscent);
          break;
        }
      }
      // in other situations, fallback to center
      aDesiredSize.SetBlockStartAscent(dy + blockSize / 2);
      break;
    case eAlign_axis:
    default: {
      // XXX should instead use style data from the row of reference here ?
      RefPtr<nsFontMetrics> fm;
      nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
                                            nsLayoutUtils::
                                            FontSizeInflationFor(this));
      nscoord axisHeight;
      GetAxisHeight(aReflowState.rendContext->GetDrawTarget(), fm, axisHeight);
      if (rowFrame) {
        // anchor the table on the axis of the row of reference
        // XXX fallback to baseline because it is a hard problem
        // XXX need to fetch the axis of the row; would need rowalign=axis to work better
        nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
        if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
          aDesiredSize.SetBlockStartAscent(dy + rowAscent);
          break;
        }
      }
      // in other situations, fallback to using half of the height
      aDesiredSize.SetBlockStartAscent(dy + blockSize / 2 + axisHeight);
    }
  }

  mReference.x = 0;
  mReference.y = aDesiredSize.BlockStartAscent();

  // just make-up a bounding metrics
  mBoundingMetrics = nsBoundingMetrics();
  mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent();
  mBoundingMetrics.descent = aDesiredSize.Height() -
                             aDesiredSize.BlockStartAscent();
  mBoundingMetrics.width = aDesiredSize.Width();
  mBoundingMetrics.leftBearing = 0;
  mBoundingMetrics.rightBearing = aDesiredSize.Width();

  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
}
void
nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext,
        nsHTMLReflowMetrics& aButtonDesiredSize,
        const nsHTMLReflowState& aButtonReflowState,
        nsIFrame* aFirstKid)
{
    WritingMode wm = GetWritingMode();
    bool isVertical = wm.IsVertical();
    LogicalSize availSize = aButtonReflowState.ComputedSize(wm);
    availSize.BSize(wm) = NS_INTRINSICSIZE;

    // Buttons have some bonus renderer-determined border/padding,
    // which occupies part of the button's content-box area:
    const LogicalMargin focusPadding =
        LogicalMargin(wm, mRenderer.GetAddedButtonBorderAndPadding());

    // shorthand for a value we need to use in a bunch of places
    const LogicalMargin& clbp = aButtonReflowState.ComputedLogicalBorderPadding();

    // Indent the child inside us by the focus border. We must do this separate
    // from the regular border.
    availSize.ISize(wm) -= focusPadding.IStartEnd(wm);

    // See whether out availSize's inline-size is big enough.  If it's smaller than
    // our intrinsic min iSize, that means that the kid wouldn't really fit; for a
    // better look in such cases we adjust the available iSize and our inline-start
    // offset to allow the kid to spill start-wards into our padding.
    nscoord ioffset = focusPadding.IStart(wm) + clbp.IStart(wm);
    nscoord extraISize = GetMinISize(aButtonReflowState.rendContext) -
                         aButtonReflowState.ComputedISize();
    if (extraISize > 0) {
        nscoord extraIStart = extraISize / 2;
        nscoord extraIEnd = extraISize - extraIStart;
        NS_ASSERTION(extraIEnd >=0, "How'd that happen?");

        // Do not allow the extras to be bigger than the relevant padding
        const LogicalMargin& padding = aButtonReflowState.ComputedLogicalPadding();
        extraIStart = std::min(extraIStart, padding.IStart(wm));
        extraIEnd = std::min(extraIEnd, padding.IEnd(wm));
        ioffset -= extraIStart;
        availSize.ISize(wm) = availSize.ISize(wm) + extraIStart + extraIEnd;
    }
    availSize.ISize(wm) = std::max(availSize.ISize(wm), 0);

    // Give child a clone of the button's reflow state, with height/width reduced
    // by focusPadding, so that descendants with height:100% don't protrude.
    nsHTMLReflowState adjustedButtonReflowState =
        CloneReflowStateWithReducedContentBox(aButtonReflowState,
                focusPadding.GetPhysicalMargin(wm));

    nsHTMLReflowState contentsReflowState(aPresContext,
                                          adjustedButtonReflowState,
                                          aFirstKid, availSize);

    nsReflowStatus contentsReflowStatus;
    nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState);
    nscoord boffset = focusPadding.BStart(wm) + clbp.BStart(wm);
    ReflowChild(aFirstKid, aPresContext,
                contentsDesiredSize, contentsReflowState,
                isVertical ? boffset : ioffset,
                isVertical ? ioffset : boffset,
                0, contentsReflowStatus);
    MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus),
               "We gave button-contents frame unconstrained available height, "
               "so it should be complete");

    // Compute the button's content-box height:
    nscoord buttonContentBoxBSize = 0;
    if (aButtonReflowState.ComputedBSize() != NS_INTRINSICSIZE) {
        // Button has a fixed block-size -- that's its content-box bSize.
        buttonContentBoxBSize = aButtonReflowState.ComputedBSize();
    } else {
        // Button is intrinsically sized -- it should shrinkwrap the
        // button-contents' bSize, plus any focus-padding space:
        buttonContentBoxBSize =
            contentsDesiredSize.BSize(wm) + focusPadding.BStartEnd(wm);

        // Make sure we obey min/max-bSize in the case when we're doing intrinsic
        // sizing (we get it for free when we have a non-intrinsic
        // aButtonReflowState.ComputedBSize()).  Note that we do this before
        // adjusting for borderpadding, since mComputedMaxBSize and
        // mComputedMinBSize are content bSizes.
        buttonContentBoxBSize =
            NS_CSS_MINMAX(buttonContentBoxBSize,
                          aButtonReflowState.ComputedMinBSize(),
                          aButtonReflowState.ComputedMaxBSize());
    }

    // Center child in the block-direction in the button
    // (technically, inside of the button's focus-padding area)
    nscoord extraSpace =
        buttonContentBoxBSize - focusPadding.BStartEnd(wm) -
        contentsDesiredSize.BSize(wm);

    boffset = std::max(0, extraSpace / 2);

    // Adjust boffset to be in terms of the button's frame-rect, instead of
    // its focus-padding rect:
    boffset += focusPadding.BStart(wm) + clbp.BStart(wm);

    // Place the child
    FinishReflowChild(aFirstKid, aPresContext,
                      contentsDesiredSize, &contentsReflowState,
                      isVertical ? boffset : ioffset,
                      isVertical ? ioffset : boffset,
                      0);

    // Make sure we have a useful 'ascent' value for the child
    if (contentsDesiredSize.BlockStartAscent() ==
            nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
        WritingMode wm = aButtonReflowState.GetWritingMode();
        contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm));
    }

    // OK, we're done with the child frame.
    // Use what we learned to populate the button frame's reflow metrics.
    //  * Button's height & width are content-box size + border-box contribution:
    aButtonDesiredSize.SetSize(wm,
                               LogicalSize(wm, aButtonReflowState.ComputedISize() + clbp.IStartEnd(wm),
                                           buttonContentBoxBSize + clbp.BStartEnd(wm)));

    //  * Button's ascent is its child's ascent, plus the child's block-offset
    // within our frame... unless it's orthogonal, in which case we'll use the
    // contents inline-size as an approximation for now.
    // XXX is there a better strategy? should we include border-padding?
    if (aButtonDesiredSize.GetWritingMode().IsOrthogonalTo(wm)) {
        aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.ISize(wm));
    } else {
        aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.BlockStartAscent() +
                                               boffset);
    }

    aButtonDesiredSize.SetOverflowAreasToDesiredBounds();
}
Ejemplo n.º 17
0
void
nsNumberControlFrame::Reflow(nsPresContext* aPresContext,
                             nsHTMLReflowMetrics& aDesiredSize,
                             const nsHTMLReflowState& aReflowState,
                             nsReflowStatus& aStatus)
{
  DO_GLOBAL_REFLOW_COUNT("nsNumberControlFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

  NS_ASSERTION(mOuterWrapper, "Outer wrapper div must exist!");

  NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(),
               "nsNumberControlFrame should not have continuations; if it does we "
               "need to call RegUnregAccessKey only for the first");

  NS_ASSERTION(!mFrames.FirstChild() ||
               !mFrames.FirstChild()->GetNextSibling(),
               "We expect at most one direct child frame");

  if (mState & NS_FRAME_FIRST_REFLOW) {
    nsFormControlFrame::RegUnRegAccessKey(this, true);
  }

  // The width of our content box, which is the available width
  // for our anonymous content:
  const nscoord contentBoxWidth = aReflowState.ComputedWidth();
  nscoord contentBoxHeight = aReflowState.ComputedHeight();

  nsIFrame* outerWrapperFrame = mOuterWrapper->GetPrimaryFrame();

  if (!outerWrapperFrame) { // display:none?
    if (contentBoxHeight == NS_INTRINSICSIZE) {
      contentBoxHeight = 0;
    }
  } else {
    NS_ASSERTION(outerWrapperFrame == mFrames.FirstChild(), "huh?");

    nsHTMLReflowMetrics wrappersDesiredSize(aReflowState);

    WritingMode wm = outerWrapperFrame->GetWritingMode();
    LogicalSize availSize = aReflowState.ComputedSize(wm);
    availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;

    nsHTMLReflowState wrapperReflowState(aPresContext, aReflowState,
                                         outerWrapperFrame, availSize);

    // offsets of wrapper frame
    nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left +
                        wrapperReflowState.ComputedPhysicalMargin().left;
    nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top +
                        wrapperReflowState.ComputedPhysicalMargin().top;

    nsReflowStatus childStatus;
    ReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize,
                wrapperReflowState, xoffset, yoffset, 0, childStatus);
    MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus),
               "We gave our child unconstrained height, so it should be complete");

    nscoord wrappersMarginBoxHeight = wrappersDesiredSize.Height() +
      wrapperReflowState.ComputedPhysicalMargin().TopBottom();

    if (contentBoxHeight == NS_INTRINSICSIZE) {
      // We are intrinsically sized -- we should shrinkwrap the outer wrapper's
      // height:
      contentBoxHeight = wrappersMarginBoxHeight;

      // Make sure we obey min/max-height in the case when we're doing intrinsic
      // sizing (we get it for free when we have a non-intrinsic
      // aReflowState.ComputedHeight()).  Note that we do this before
      // adjusting for borderpadding, since mComputedMaxHeight and
      // mComputedMinHeight are content heights.
      contentBoxHeight =
        NS_CSS_MINMAX(contentBoxHeight,
                      aReflowState.ComputedMinHeight(),
                      aReflowState.ComputedMaxHeight());
    }

    // Center child vertically
    nscoord extraSpace = contentBoxHeight - wrappersMarginBoxHeight;
    yoffset += std::max(0, extraSpace / 2);

    // Place the child
    FinishReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize,
                      &wrapperReflowState, xoffset, yoffset, 0);

    aDesiredSize.SetBlockStartAscent(
       wrappersDesiredSize.BlockStartAscent() +
       outerWrapperFrame->BStart(aReflowState.GetWritingMode(),
                                 contentBoxWidth));
  }

  aDesiredSize.Width() = contentBoxWidth +
                         aReflowState.ComputedPhysicalBorderPadding().LeftRight();
  aDesiredSize.Height() = contentBoxHeight +
                          aReflowState.ComputedPhysicalBorderPadding().TopBottom();

  aDesiredSize.SetOverflowAreasToDesiredBounds();

  if (outerWrapperFrame) {
    ConsiderChildOverflow(aDesiredSize.mOverflowAreas, outerWrapperFrame);
  }

  FinishAndStoreOverflow(&aDesiredSize);

  aStatus = NS_FRAME_COMPLETE;

  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
}
Ejemplo n.º 18
0
nsresult
nsMathMLmfracFrame::PlaceInternal(nsRenderingContext& aRenderingContext,
                                  bool                 aPlaceOrigin,
                                  nsHTMLReflowMetrics& aDesiredSize,
                                  bool                 aWidthOnly)
{
    ////////////////////////////////////
    // Get the children's desired sizes
    nsBoundingMetrics bmNum, bmDen;
    nsHTMLReflowMetrics sizeNum(aDesiredSize.GetWritingMode());
    nsHTMLReflowMetrics sizeDen(aDesiredSize.GetWritingMode());
    nsIFrame* frameDen = nullptr;
    nsIFrame* frameNum = mFrames.FirstChild();
    if (frameNum)
        frameDen = frameNum->GetNextSibling();
    if (!frameNum || !frameDen || frameDen->GetNextSibling()) {
        // report an error, encourage people to get their markups in order
        if (aPlaceOrigin) {
            ReportChildCountError();
        }
        return ReflowError(aRenderingContext, aDesiredSize);
    }
    GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum);
    GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen);

    nsPresContext* presContext = PresContext();
    nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);

    float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
    nsRefPtr<nsFontMetrics> fm;
    nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
                                          fontSizeInflation);

    nscoord defaultRuleThickness, axisHeight;
    nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
    gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
    if (mathFont) {
        defaultRuleThickness =
            mathFont->GetMathConstant(gfxFontEntry::FractionRuleThickness,
                                      oneDevPixel);
    } else {
        GetRuleThickness(aRenderingContext, fm, defaultRuleThickness);
    }
    GetAxisHeight(aRenderingContext, fm, axisHeight);

    bool outermostEmbellished = false;
    if (mEmbellishData.coreFrame) {
        nsEmbellishData parentData;
        GetEmbellishDataFrom(GetParent(), parentData);
        outermostEmbellished = parentData.coreFrame != mEmbellishData.coreFrame;
    }

    // see if the linethickness attribute is there
    nsAutoString value;
    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::linethickness_, value);
    mLineThickness = CalcLineThickness(presContext, mStyleContext, value,
                                       onePixel, defaultRuleThickness,
                                       fontSizeInflation);

    // bevelled attribute
    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::bevelled_, value);
    mIsBevelled = value.EqualsLiteral("true");

    bool displayStyle = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK;

    if (!mIsBevelled) {
        mLineRect.height = mLineThickness;

        // by default, leave at least one-pixel padding at either end, and add
        // lspace & rspace that may come from <mo> if we are an outermost
        // embellished container (we fetch values from the core since they may use
        // units that depend on style data, and style changes could have occurred
        // in the core since our last visit there)
        nscoord leftSpace = onePixel;
        nscoord rightSpace = onePixel;
        if (outermostEmbellished) {
            nsEmbellishData coreData;
            GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
            leftSpace += StyleVisibility()->mDirection ?
                         coreData.trailingSpace : coreData.leadingSpace;
            rightSpace += StyleVisibility()->mDirection ?
                          coreData.leadingSpace : coreData.trailingSpace;
        }

        nscoord actualRuleThickness =  mLineThickness;

        //////////////////
        // Get shifts
        nscoord numShift = 0;
        nscoord denShift = 0;

        // Rule 15b, App. G, TeXbook
        nscoord numShift1, numShift2, numShift3;
        nscoord denShift1, denShift2;

        GetNumeratorShifts(fm, numShift1, numShift2, numShift3);
        GetDenominatorShifts(fm, denShift1, denShift2);

        if (0 == actualRuleThickness) {
            numShift = displayStyle ? numShift1 : numShift3;
            denShift = displayStyle ? denShift1 : denShift2;
            if (mathFont) {
                numShift = mathFont->
                           GetMathConstant(displayStyle ?
                                           gfxFontEntry::StackTopDisplayStyleShiftUp :
                                           gfxFontEntry::StackTopShiftUp,
                                           oneDevPixel);
                denShift = mathFont->
                           GetMathConstant(displayStyle ?
                                           gfxFontEntry::StackBottomDisplayStyleShiftDown :
                                           gfxFontEntry::StackBottomShiftDown,
                                           oneDevPixel);
            }
        } else {
            numShift = displayStyle ? numShift1 : numShift2;
            denShift = displayStyle ? denShift1 : denShift2;
            if (mathFont) {
                numShift = mathFont->
                           GetMathConstant(displayStyle ?
                                           gfxFontEntry::FractionNumeratorDisplayStyleShiftUp :
                                           gfxFontEntry::FractionNumeratorShiftUp,
                                           oneDevPixel);
                denShift = mathFont->
                           GetMathConstant(
                               displayStyle ?
                               gfxFontEntry::FractionDenominatorDisplayStyleShiftDown :
                               gfxFontEntry::FractionDenominatorShiftDown,
                               oneDevPixel);
            }
        }

        if (0 == actualRuleThickness) {
            // Rule 15c, App. G, TeXbook

            // min clearance between numerator and denominator
            nscoord minClearance = displayStyle ?
                                   7 * defaultRuleThickness : 3 * defaultRuleThickness;
            if (mathFont) {
                minClearance =
                    mathFont->GetMathConstant(displayStyle ?
                                              gfxFontEntry::StackDisplayStyleGapMin :
                                              gfxFontEntry::StackGapMin,
                                              oneDevPixel);
            }
            nscoord actualClearance =
                (numShift - bmNum.descent) - (bmDen.ascent - denShift);
            // actualClearance should be >= minClearance
            if (actualClearance < minClearance) {
                nscoord halfGap = (minClearance - actualClearance)/2;
                numShift += halfGap;
                denShift += halfGap;
            }
        }
        else {
            // Rule 15d, App. G, TeXbook

            // min clearance between numerator or denominator and middle of bar

            // TeX has a different interpretation of the thickness.
            // Try $a \above10pt b$ to see. Here is what TeX does:
            // minClearance = displayStyle ?
            //   3 * actualRuleThickness : actualRuleThickness;

            // we slightly depart from TeX here. We use the defaultRuleThickness instead
            // of the value coming from the linethickness attribute, i.e., we recover what
            // TeX does if the user hasn't set linethickness. But when the linethickness
            // is set, we avoid the wide gap problem.
            nscoord minClearanceNum = displayStyle ?
                                      3 * defaultRuleThickness : defaultRuleThickness + onePixel;
            nscoord minClearanceDen = minClearanceNum;
            if (mathFont) {
                minClearanceNum = mathFont->
                                  GetMathConstant(displayStyle ?
                                                  gfxFontEntry::FractionNumDisplayStyleGapMin :
                                                  gfxFontEntry::FractionNumeratorGapMin,
                                                  oneDevPixel);
                minClearanceDen = mathFont->
                                  GetMathConstant(displayStyle ?
                                                  gfxFontEntry::FractionDenomDisplayStyleGapMin :
                                                  gfxFontEntry::FractionDenominatorGapMin,
                                                  oneDevPixel);
            }

            // adjust numShift to maintain minClearanceNum if needed
            nscoord actualClearanceNum =
                (numShift - bmNum.descent) - (axisHeight + actualRuleThickness/2);
            if (actualClearanceNum < minClearanceNum) {
                numShift += (minClearanceNum - actualClearanceNum);
            }
            // adjust denShift to maintain minClearanceDen if needed
            nscoord actualClearanceDen =
                (axisHeight - actualRuleThickness/2) - (bmDen.ascent - denShift);
            if (actualClearanceDen < minClearanceDen) {
                denShift += (minClearanceDen - actualClearanceDen);
            }
        }

        //////////////////
        // Place Children

        // XXX Need revisiting the width. TeX uses the exact width
        // e.g. in $$\huge\frac{\displaystyle\int}{i}$$
        nscoord width = std::max(bmNum.width, bmDen.width);
        nscoord dxNum = leftSpace + (width - sizeNum.Width())/2;
        nscoord dxDen = leftSpace + (width - sizeDen.Width())/2;
        width += leftSpace + rightSpace;

        // see if the numalign attribute is there
        mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::numalign_, value);
        if (value.EqualsLiteral("left"))
            dxNum = leftSpace;
        else if (value.EqualsLiteral("right"))
            dxNum = width - rightSpace - sizeNum.Width();

        // see if the denomalign attribute is there
        mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::denomalign_, value);
        if (value.EqualsLiteral("left"))
            dxDen = leftSpace;
        else if (value.EqualsLiteral("right"))
            dxDen = width - rightSpace - sizeDen.Width();

        mBoundingMetrics.rightBearing =
            std::max(dxNum + bmNum.rightBearing, dxDen + bmDen.rightBearing);
        if (mBoundingMetrics.rightBearing < width - rightSpace)
            mBoundingMetrics.rightBearing = width - rightSpace;
        mBoundingMetrics.leftBearing =
            std::min(dxNum + bmNum.leftBearing, dxDen + bmDen.leftBearing);
        if (mBoundingMetrics.leftBearing > leftSpace)
            mBoundingMetrics.leftBearing = leftSpace;
        mBoundingMetrics.ascent = bmNum.ascent + numShift;
        mBoundingMetrics.descent = bmDen.descent + denShift;
        mBoundingMetrics.width = width;

        aDesiredSize.SetBlockStartAscent(sizeNum.BlockStartAscent() + numShift);
        aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
                                sizeDen.Height() - sizeDen.BlockStartAscent() + denShift;
        aDesiredSize.Width() = mBoundingMetrics.width;
        aDesiredSize.mBoundingMetrics = mBoundingMetrics;

        mReference.x = 0;
        mReference.y = aDesiredSize.BlockStartAscent();

        if (aPlaceOrigin) {
            nscoord dy;
            // place numerator
            dy = 0;
            FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dxNum, dy, 0);
            // place denominator
            dy = aDesiredSize.Height() - sizeDen.Height();
            FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dxDen, dy, 0);
            // place the fraction bar - dy is top of bar
            dy = aDesiredSize.BlockStartAscent() - (axisHeight + actualRuleThickness/2);
            mLineRect.SetRect(leftSpace, dy, width - (leftSpace + rightSpace),
                              actualRuleThickness);
        }
    } else {
        nscoord numShift = 0.0;
        nscoord denShift = 0.0;
        nscoord padding = 3 * defaultRuleThickness;
        nscoord slashRatio = 3;

        // Define the constant used in the expression of the maximum width
        nscoord em = fm->EmHeight();
        nscoord slashMaxWidthConstant = 2 * em;

        // For large line thicknesses the minimum slash height is limited to the
        // largest expected height of a fraction
        nscoord slashMinHeight = slashRatio *
                                 std::min(2 * mLineThickness, slashMaxWidthConstant);

        nscoord leadingSpace = padding;
        nscoord trailingSpace = padding;
        if (outermostEmbellished) {
            nsEmbellishData coreData;
            GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
            leadingSpace += coreData.leadingSpace;
            trailingSpace += coreData.trailingSpace;
        }
        nscoord delta;

        //           ___________
        //          |           |    /
        //         {|-NUMERATOR-|   /
        //         {|___________|  S
        //         {               L
        // numShift{               A
        // ------------------------------------------------------- baseline
        //                         S   _____________ } denShift
        //                         H  |             |}
        //                        /   |-DENOMINATOR-|}
        //                       /    |_____________|
        //

        // first, ensure that the top of the numerator is at least as high as the
        // top of the denominator (and the reverse for the bottoms)
        delta = std::max(bmDen.ascent - bmNum.ascent,
                         bmNum.descent - bmDen.descent) / 2;
        if (delta > 0) {
            numShift += delta;
            denShift += delta;
        }

        if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) {
            delta = std::min(bmDen.ascent + bmDen.descent,
                             bmNum.ascent + bmNum.descent) / 2;
            numShift += delta;
            denShift += delta;
        } else {
            nscoord xHeight = fm->XHeight();
            numShift += xHeight / 2;
            denShift += xHeight / 4;
        }

        // Set the ascent/descent of our BoundingMetrics.
        mBoundingMetrics.ascent = bmNum.ascent + numShift;
        mBoundingMetrics.descent = bmDen.descent + denShift;

        // At this point the height of the slash is
        // mBoundingMetrics.ascent + mBoundingMetrics.descent
        // Ensure that it is greater than slashMinHeight
        delta = (slashMinHeight -
                 (mBoundingMetrics.ascent + mBoundingMetrics.descent)) / 2;
        if (delta > 0) {
            mBoundingMetrics.ascent += delta;
            mBoundingMetrics.descent += delta;
        }

        // Set the width of the slash
        if (aWidthOnly) {
            mLineRect.width = mLineThickness + slashMaxWidthConstant;
        } else {
            mLineRect.width = mLineThickness +
                              std::min(slashMaxWidthConstant,
                                       (mBoundingMetrics.ascent + mBoundingMetrics.descent) /
                                       slashRatio);
        }

        // Set horizontal bounding metrics
        if (StyleVisibility()->mDirection) {
            mBoundingMetrics.leftBearing = trailingSpace + bmDen.leftBearing;
            mBoundingMetrics.rightBearing = trailingSpace + bmDen.width + mLineRect.width + bmNum.rightBearing;
        } else {
            mBoundingMetrics.leftBearing = leadingSpace + bmNum.leftBearing;
            mBoundingMetrics.rightBearing = leadingSpace + bmNum.width + mLineRect.width + bmDen.rightBearing;
        }
        mBoundingMetrics.width =
            leadingSpace + bmNum.width + mLineRect.width + bmDen.width +
            trailingSpace;

        // Set aDesiredSize
        aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + padding);
        aDesiredSize.Height() =
            mBoundingMetrics.ascent + mBoundingMetrics.descent + 2 * padding;
        aDesiredSize.Width() = mBoundingMetrics.width;
        aDesiredSize.mBoundingMetrics = mBoundingMetrics;

        mReference.x = 0;
        mReference.y = aDesiredSize.BlockStartAscent();

        if (aPlaceOrigin) {
            nscoord dx, dy;

            // place numerator
            dx = MirrorIfRTL(aDesiredSize.Width(), sizeNum.Width(),
                             leadingSpace);
            dy = aDesiredSize.BlockStartAscent() - numShift - sizeNum.BlockStartAscent();
            FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dx, dy, 0);

            // place the fraction bar
            dx = MirrorIfRTL(aDesiredSize.Width(), mLineRect.width,
                             leadingSpace + bmNum.width);
            dy = aDesiredSize.BlockStartAscent() - mBoundingMetrics.ascent;
            mLineRect.SetRect(dx, dy,
                              mLineRect.width, aDesiredSize.Height() - 2 * padding);

            // place denominator
            dx = MirrorIfRTL(aDesiredSize.Width(), sizeDen.Width(),
                             leadingSpace + bmNum.width + mLineRect.width);
            dy = aDesiredSize.BlockStartAscent() + denShift - sizeDen.BlockStartAscent();
            FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dx, dy, 0);
        }

    }

    return NS_OK;
}
Ejemplo n.º 19
0
void
nsFirstLetterFrame::Reflow(nsPresContext*          aPresContext,
                           nsHTMLReflowMetrics&     aMetrics,
                           const nsHTMLReflowState& aReflowState,
                           nsReflowStatus&          aReflowStatus)
{
  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus);

  // Grab overflow list
  DrainOverflowFrames(aPresContext);

  nsIFrame* kid = mFrames.FirstChild();

  // Setup reflow state for our child
  WritingMode wm = aReflowState.GetWritingMode();
  LogicalSize availSize = aReflowState.AvailableSize();
  const LogicalMargin& bp = aReflowState.ComputedLogicalBorderPadding();
  NS_ASSERTION(availSize.ISize(wm) != NS_UNCONSTRAINEDSIZE,
               "should no longer use unconstrained inline size");
  availSize.ISize(wm) -= bp.IStartEnd(wm);
  if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) {
    availSize.BSize(wm) -= bp.BStartEnd(wm);
  }

  WritingMode lineWM = aMetrics.GetWritingMode();
  nsHTMLReflowMetrics kidMetrics(lineWM);

  // Reflow the child
  if (!aReflowState.mLineLayout) {
    // When there is no lineLayout provided, we provide our own. The
    // only time that the first-letter-frame is not reflowing in a
    // line context is when its floating.
    WritingMode kidWritingMode = GetWritingMode(kid);
    LogicalSize kidAvailSize = availSize.ConvertTo(kidWritingMode, wm);
    nsHTMLReflowState rs(aPresContext, aReflowState, kid, kidAvailSize);
    nsLineLayout ll(aPresContext, nullptr, &aReflowState, nullptr, nullptr);

    ll.BeginLineReflow(bp.IStart(wm), bp.BStart(wm),
                       availSize.ISize(wm), NS_UNCONSTRAINEDSIZE,
                       false, true, kidWritingMode,
                       nsSize(aReflowState.AvailableWidth(),
                              aReflowState.AvailableHeight()));
    rs.mLineLayout = &ll;
    ll.SetInFirstLetter(true);
    ll.SetFirstLetterStyleOK(true);

    kid->Reflow(aPresContext, kidMetrics, rs, aReflowStatus);

    ll.EndLineReflow();
    ll.SetInFirstLetter(false);

    // In the floating first-letter case, we need to set this ourselves;
    // nsLineLayout::BeginSpan will set it in the other case
    mBaseline = kidMetrics.BlockStartAscent();

    // Place and size the child and update the output metrics
    LogicalSize convertedSize = kidMetrics.Size(lineWM).ConvertTo(wm, lineWM);
    kid->SetRect(nsRect(bp.IStart(wm), bp.BStart(wm),
                        convertedSize.ISize(wm), convertedSize.BSize(wm)));
    kid->FinishAndStoreOverflow(&kidMetrics);
    kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);

    convertedSize.ISize(wm) += bp.IStartEnd(wm);
    convertedSize.BSize(wm) += bp.BStartEnd(wm);
    aMetrics.SetSize(wm, convertedSize);
    aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() +
                                 bp.BStart(wm));

    // Ensure that the overflow rect contains the child textframe's
    // overflow rect.
    // Note that if this is floating, the overline/underline drawable
    // area is in the overflow rect of the child textframe.
    aMetrics.UnionOverflowAreasWithDesiredBounds();
    ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);

    FinishAndStoreOverflow(&aMetrics);
  }
  else {
    // Pretend we are a span and reflow the child frame
    nsLineLayout* ll = aReflowState.mLineLayout;
    bool          pushedFrame;

    ll->SetInFirstLetter(
      mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter);
    ll->BeginSpan(this, &aReflowState, bp.IStart(wm),
                  availSize.ISize(wm), &mBaseline);
    ll->ReflowFrame(kid, aReflowStatus, &kidMetrics, pushedFrame);
    NS_ASSERTION(lineWM.IsVertical() == wm.IsVertical(),
                 "we're assuming we can mix sizes between lineWM and wm "
                 "since we shouldn't have orthogonal writing modes within "
                 "a line.");
    aMetrics.ISize(lineWM) = ll->EndSpan(this) + bp.IStartEnd(wm);
    ll->SetInFirstLetter(false);

    nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, bp, lineWM, wm);
  }

  if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
    // Create a continuation or remove existing continuations based on
    // the reflow completion status.
    if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
      if (aReflowState.mLineLayout) {
        aReflowState.mLineLayout->SetFirstLetterStyleOK(false);
      }
      nsIFrame* kidNextInFlow = kid->GetNextInFlow();
      if (kidNextInFlow) {
        // Remove all of the childs next-in-flows
        kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
      }
    }
    else {
      // Create a continuation for the child frame if it doesn't already
      // have one.
      if (!IsFloating()) {
        CreateNextInFlow(kid);
        // And then push it to our overflow list
        const nsFrameList& overflow = mFrames.RemoveFramesAfter(kid);
        if (overflow.NotEmpty()) {
          SetOverflowFrames(overflow);
        }
      } else if (!kid->GetNextInFlow()) {
        // For floating first letter frames (if a continuation wasn't already
        // created for us) we need to put the continuation with the rest of the
        // text that the first letter frame was made out of.
        nsIFrame* continuation;
        CreateContinuationForFloatingParent(aPresContext, kid,
                                            &continuation, true);
      }
    }
  }

  NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowState, aMetrics);
}
Ejemplo n.º 20
0
void
nsNumberControlFrame::Reflow(nsPresContext* aPresContext,
                             nsHTMLReflowMetrics& aDesiredSize,
                             const nsHTMLReflowState& aReflowState,
                             nsReflowStatus& aStatus)
{
    MarkInReflow();
    DO_GLOBAL_REFLOW_COUNT("nsNumberControlFrame");
    DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

    NS_ASSERTION(mOuterWrapper, "Outer wrapper div must exist!");

    NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(),
                 "nsNumberControlFrame should not have continuations; if it does we "
                 "need to call RegUnregAccessKey only for the first");

    NS_ASSERTION(!mFrames.FirstChild() ||
                 !mFrames.FirstChild()->GetNextSibling(),
                 "We expect at most one direct child frame");

    if (mState & NS_FRAME_FIRST_REFLOW) {
        nsFormControlFrame::RegUnRegAccessKey(this, true);
    }

    const WritingMode myWM = aReflowState.GetWritingMode();

    // The ISize of our content box, which is the available ISize
    // for our anonymous content:
    const nscoord contentBoxISize = aReflowState.ComputedISize();
    nscoord contentBoxBSize = aReflowState.ComputedBSize();

    // Figure out our border-box sizes as well (by adding borderPadding to
    // content-box sizes):
    const nscoord borderBoxISize = contentBoxISize +
                                   aReflowState.ComputedLogicalBorderPadding().IStartEnd(myWM);

    nscoord borderBoxBSize;
    if (contentBoxBSize != NS_INTRINSICSIZE) {
        borderBoxBSize = contentBoxBSize +
                         aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM);
    } // else, we'll figure out borderBoxBSize after we resolve contentBoxBSize.

    nsIFrame* outerWrapperFrame = mOuterWrapper->GetPrimaryFrame();

    if (!outerWrapperFrame) { // display:none?
        if (contentBoxBSize == NS_INTRINSICSIZE) {
            contentBoxBSize = 0;
            borderBoxBSize =
                aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM);
        }
    } else {
        NS_ASSERTION(outerWrapperFrame == mFrames.FirstChild(), "huh?");

        nsHTMLReflowMetrics wrappersDesiredSize(aReflowState);

        WritingMode wrapperWM = outerWrapperFrame->GetWritingMode();
        LogicalSize availSize = aReflowState.ComputedSize(wrapperWM);
        availSize.BSize(wrapperWM) = NS_UNCONSTRAINEDSIZE;

        nsHTMLReflowState wrapperReflowState(aPresContext, aReflowState,
                                             outerWrapperFrame, availSize);

        // Convert wrapper margin into my own writing-mode (in case it differs):
        LogicalMargin wrapperMargin =
            wrapperReflowState.ComputedLogicalMargin().ConvertTo(myWM, wrapperWM);

        // offsets of wrapper frame within this frame:
        LogicalPoint
        wrapperOffset(myWM,
                      aReflowState.ComputedLogicalBorderPadding().IStart(myWM) +
                      wrapperMargin.IStart(myWM),
                      aReflowState.ComputedLogicalBorderPadding().BStart(myWM) +
                      wrapperMargin.BStart(myWM));

        nsReflowStatus childStatus;
        // We initially reflow the child with a dummy containerSize; positioning
        // will be fixed later.
        const nsSize dummyContainerSize;
        ReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize,
                    wrapperReflowState, myWM, wrapperOffset, dummyContainerSize, 0,
                    childStatus);
        MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus),
                   "We gave our child unconstrained available block-size, "
                   "so it should be complete");

        nscoord wrappersMarginBoxBSize =
            wrappersDesiredSize.BSize(myWM) + wrapperMargin.BStartEnd(myWM);

        if (contentBoxBSize == NS_INTRINSICSIZE) {
            // We are intrinsically sized -- we should shrinkwrap the outer wrapper's
            // block-size:
            contentBoxBSize = wrappersMarginBoxBSize;

            // Make sure we obey min/max-bsize in the case when we're doing intrinsic
            // sizing (we get it for free when we have a non-intrinsic
            // aReflowState.ComputedBSize()).  Note that we do this before
            // adjusting for borderpadding, since ComputedMaxBSize and
            // ComputedMinBSize are content heights.
            contentBoxBSize =
                NS_CSS_MINMAX(contentBoxBSize,
                              aReflowState.ComputedMinBSize(),
                              aReflowState.ComputedMaxBSize());

            borderBoxBSize = contentBoxBSize +
                             aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM);
        }

        // Center child in block axis
        nscoord extraSpace = contentBoxBSize - wrappersMarginBoxBSize;
        wrapperOffset.B(myWM) += std::max(0, extraSpace / 2);

        // Needed in FinishReflowChild, for logical-to-physical conversion:
        nsSize borderBoxSize = LogicalSize(myWM, borderBoxISize, borderBoxBSize).
                               GetPhysicalSize(myWM);

        // Place the child
        FinishReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize,
                          &wrapperReflowState, myWM, wrapperOffset,
                          borderBoxSize, 0);

        nsSize contentBoxSize =
            LogicalSize(myWM, contentBoxISize, contentBoxBSize).
            GetPhysicalSize(myWM);
        aDesiredSize.SetBlockStartAscent(
            wrappersDesiredSize.BlockStartAscent() +
            outerWrapperFrame->BStart(aReflowState.GetWritingMode(),
                                      contentBoxSize));
    }

    LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize);
    aDesiredSize.SetSize(myWM, logicalDesiredSize);

    aDesiredSize.SetOverflowAreasToDesiredBounds();

    if (outerWrapperFrame) {
        ConsiderChildOverflow(aDesiredSize.mOverflowAreas, outerWrapperFrame);
    }

    FinishAndStoreOverflow(&aDesiredSize);

    aStatus = NS_FRAME_COMPLETE;

    NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
}
Ejemplo n.º 21
0
void
nsMathMLmrootFrame::Reflow(nsPresContext*          aPresContext,
                           nsHTMLReflowMetrics&     aDesiredSize,
                           const nsHTMLReflowState& aReflowState,
                           nsReflowStatus&          aStatus)
{
  MarkInReflow();
  nsReflowStatus childStatus;

  mPresentationData.flags &= ~NS_MATHML_ERROR;
  aDesiredSize.ClearSize();
  aDesiredSize.SetBlockStartAscent(0);

  nsBoundingMetrics bmSqr, bmBase, bmIndex;
  DrawTarget* drawTarget = aReflowState.rendContext->GetDrawTarget();

  //////////////////
  // Reflow Children

  int32_t count = 0;
  nsIFrame* baseFrame = nullptr;
  nsIFrame* indexFrame = nullptr;
  nsHTMLReflowMetrics baseSize(aReflowState);
  nsHTMLReflowMetrics indexSize(aReflowState);
  nsIFrame* childFrame = mFrames.FirstChild();
  while (childFrame) {
    // ask our children to compute their bounding metrics 
    nsHTMLReflowMetrics childDesiredSize(aReflowState,
                                         aDesiredSize.mFlags
                                         | NS_REFLOW_CALC_BOUNDING_METRICS);
    WritingMode wm = childFrame->GetWritingMode();
    LogicalSize availSize = aReflowState.ComputedSize(wm);
    availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
    nsHTMLReflowState childReflowState(aPresContext, aReflowState,
                                       childFrame, availSize);
    ReflowChild(childFrame, aPresContext,
                     childDesiredSize, childReflowState, childStatus);
    //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
    if (0 == count) {
      // base 
      baseFrame = childFrame;
      baseSize = childDesiredSize;
      bmBase = childDesiredSize.mBoundingMetrics;
    }
    else if (1 == count) {
      // index
      indexFrame = childFrame;
      indexSize = childDesiredSize;
      bmIndex = childDesiredSize.mBoundingMetrics;
    }
    count++;
    childFrame = childFrame->GetNextSibling();
  }
  if (2 != count) {
    // report an error, encourage people to get their markups in order
    ReportChildCountError();
    ReflowError(drawTarget, aDesiredSize);
    aStatus = NS_FRAME_COMPLETE;
    NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
    // Call DidReflow() for the child frames we successfully did reflow.
    DidReflowChildren(mFrames.FirstChild(), childFrame);
    return;
  }

  ////////////
  // Prepare the radical symbol and the overline bar

  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
  RefPtr<nsFontMetrics> fm =
    nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);

  nscoord ruleThickness, leading, psi;
  GetRadicalParameters(fm, StyleFont()->mMathDisplay ==
                       NS_MATHML_DISPLAYSTYLE_BLOCK,
                       ruleThickness, leading, psi);

  // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
  char16_t one = '1';
  nsBoundingMetrics bmOne =
    nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, drawTarget);
  if (bmOne.ascent > bmBase.ascent)
    psi += bmOne.ascent - bmBase.ascent;

  // make sure that the rule appears on on screen
  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
  if (ruleThickness < onePixel) {
    ruleThickness = onePixel;
  }

  // adjust clearance psi to get an exact number of pixels -- this
  // gives a nicer & uniform look on stacked radicals (bug 130282)
  nscoord delta = psi % onePixel;
  if (delta)
    psi += onePixel - delta; // round up

  // Stretch the radical symbol to the appropriate height if it is not big enough.
  nsBoundingMetrics contSize = bmBase;
  contSize.descent = bmBase.ascent + bmBase.descent + psi;
  contSize.ascent = ruleThickness;

  // height(radical) should be >= height(base) + psi + ruleThickness
  nsBoundingMetrics radicalSize;
  mSqrChar.Stretch(aPresContext, drawTarget,
                   fontSizeInflation,
                   NS_STRETCH_DIRECTION_VERTICAL, 
                   contSize, radicalSize,
                   NS_STRETCH_LARGER,
                   StyleVisibility()->mDirection);
  // radicalSize have changed at this point, and should match with
  // the bounding metrics of the char
  mSqrChar.GetBoundingMetrics(bmSqr);

  // Update the desired size for the container (like msqrt, index is not yet included)
  // the baseline will be that of the base.
  mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness;
  mBoundingMetrics.descent = 
    std::max(bmBase.descent,
           (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
  mBoundingMetrics.width = bmSqr.width + bmBase.width;
  mBoundingMetrics.leftBearing = bmSqr.leftBearing;
  mBoundingMetrics.rightBearing = bmSqr.width + 
    std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule

  aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
  aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
    std::max(baseSize.Height() - baseSize.BlockStartAscent(),
             mBoundingMetrics.descent + ruleThickness);
  aDesiredSize.Width() = mBoundingMetrics.width;

  /////////////
  // Re-adjust the desired size to include the index.
  
  // the index is raised by some fraction of the height
  // of the radical, see \mroot macro in App. B, TexBook
  float raiseIndexPercent = 0.6f;
  gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
  if (mathFont) {
    raiseIndexPercent =
      mathFont->GetMathConstant(gfxFontEntry::RadicalDegreeBottomRaisePercent);
  }
  nscoord raiseIndexDelta = NSToCoordRound(raiseIndexPercent *
                                           (bmSqr.ascent + bmSqr.descent));
  nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical 
    - (bmSqr.ascent + bmSqr.descent) // to bottom of radical
    + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index

  nscoord indexClearance = 0;
  if (mBoundingMetrics.ascent < indexRaisedAscent) {
    indexClearance = 
      indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index 
    mBoundingMetrics.ascent = indexRaisedAscent;
    nscoord descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
    aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
    aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + descent;
  }

  nscoord dxIndex, dxSqr;
  GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr);

  mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width;
  mBoundingMetrics.leftBearing = 
    std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
  mBoundingMetrics.rightBearing = dxSqr + bmSqr.width +
    std::max(bmBase.width, bmBase.rightBearing);

  aDesiredSize.Width() = mBoundingMetrics.width;
  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
  GatherAndStoreOverflow(&aDesiredSize);

  // place the index
  nscoord dx = dxIndex;
  nscoord dy = aDesiredSize.BlockStartAscent() -
    (indexRaisedAscent + indexSize.BlockStartAscent() - bmIndex.ascent);
  FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr,
                    MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx),
                    dy, 0);

  // place the radical symbol and the radical bar
  dx = dxSqr;
  dy = indexClearance + leading; // leave a leading at the top
  mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx),
                          dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
  dx += bmSqr.width;
  mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx),
                   dy, bmBase.width, ruleThickness);

  // place the base
  dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
  FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr,
                    MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx),
                    dy, 0);

  mReference.x = 0;
  mReference.y = aDesiredSize.BlockStartAscent();

  aStatus = NS_FRAME_COMPLETE;
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
}
void
nsMathMLmfencedFrame::Reflow(nsPresContext*          aPresContext,
                             nsHTMLReflowMetrics&     aDesiredSize,
                             const nsHTMLReflowState& aReflowState,
                             nsReflowStatus&          aStatus)
{
  MarkInReflow();
  mPresentationData.flags &= ~NS_MATHML_ERROR;
  aDesiredSize.ClearSize();
  aDesiredSize.SetBlockStartAscent(0);
  aDesiredSize.mBoundingMetrics = nsBoundingMetrics();

  int32_t i;
  const nsStyleFont* font = StyleFont();
  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
  RefPtr<nsFontMetrics> fm;
  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
                                        fontSizeInflation);
  nscoord axisHeight, em;
  GetAxisHeight(*aReflowState.rendContext, fm, axisHeight);
  GetEmHeight(fm, em);
  // leading to be left at the top and the bottom of stretched chars
  nscoord leading = NSToCoordRound(0.2f * em); 

  /////////////
  // Reflow children
  // Asking each child to cache its bounding metrics

  // Note that we don't use the base method nsMathMLContainerFrame::Reflow()
  // because we want to stretch our fences, separators and stretchy frames using
  // the *same* initial aDesiredSize.mBoundingMetrics. If we were to use the base
  // method here, our stretchy frames will be stretched and placed, and we may
  // end up stretching our fences/separators with a different aDesiredSize.
  // XXX The above decision was revisited in bug 121748 and this code can be
  // refactored to use nsMathMLContainerFrame::Reflow() at some stage.

  nsReflowStatus childStatus;
  nsIFrame* firstChild = GetFirstPrincipalChild();
  nsIFrame* childFrame = firstChild;
  nscoord ascent = 0, descent = 0;
  if (firstChild || mOpenChar || mCloseChar || mSeparatorsCount > 0) {
    // We use the ASCII metrics to get our minimum height. This way,
    // if we have borders or a background, they will fit better with
    // other elements on the line.
    ascent = fm->MaxAscent();
    descent = fm->MaxDescent();
  }
  while (childFrame) {
    nsHTMLReflowMetrics childDesiredSize(aReflowState,
                                         aDesiredSize.mFlags
                                         | NS_REFLOW_CALC_BOUNDING_METRICS);
    WritingMode wm = childFrame->GetWritingMode();
    LogicalSize availSize = aReflowState.ComputedSize(wm);
    availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
    nsHTMLReflowState childReflowState(aPresContext, aReflowState,
                                       childFrame, availSize);
    ReflowChild(childFrame, aPresContext, childDesiredSize,
                childReflowState, childStatus);
    //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
    SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
                                    childDesiredSize.mBoundingMetrics);

    mozilla::WritingMode outerWM = aReflowState.GetWritingMode();
    nscoord childDescent = childDesiredSize.BSize(outerWM) -
                           childDesiredSize.BlockStartAscent();
    if (descent < childDescent)
      descent = childDescent;
    if (ascent < childDesiredSize.BlockStartAscent())
      ascent = childDesiredSize.BlockStartAscent();

    childFrame = childFrame->GetNextSibling();
  }

  /////////////
  // Ask stretchy children to stretch themselves

  nsBoundingMetrics containerSize;
  nsStretchDirection stretchDir = NS_STRETCH_DIRECTION_VERTICAL;

  GetPreferredStretchSize(*aReflowState.rendContext,
                          0, /* i.e., without embellishments */
                          stretchDir, containerSize);
  childFrame = firstChild;
  while (childFrame) {
    nsIMathMLFrame* mathmlChild = do_QueryFrame(childFrame);
    if (mathmlChild) {
      nsHTMLReflowMetrics childDesiredSize(aReflowState);
      // retrieve the metrics that was stored at the previous pass
      GetReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
                                     childDesiredSize.mBoundingMetrics);
      
      mathmlChild->Stretch(*aReflowState.rendContext, 
                           stretchDir, containerSize, childDesiredSize);
      // store the updated metrics
      SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
                                      childDesiredSize.mBoundingMetrics);
      
      nscoord childDescent = childDesiredSize.Height() - childDesiredSize.BlockStartAscent();
      if (descent < childDescent)
        descent = childDescent;
      if (ascent < childDesiredSize.BlockStartAscent())
        ascent = childDesiredSize.BlockStartAscent();
    }
    childFrame = childFrame->GetNextSibling();
  }

  // bug 121748: for surrounding fences & separators, use a size that covers everything
  GetPreferredStretchSize(*aReflowState.rendContext,
                          STRETCH_CONSIDER_EMBELLISHMENTS,
                          stretchDir, containerSize);

  bool isRTL = StyleVisibility()->mDirection;

  // To achieve a minimum size of "1", the container should be enlarged by the
  // unstretched metrics of the fences and separators.
  ApplyUnstretchedMetrics(aPresContext, *aReflowState.rendContext,
                          fontSizeInflation, mOpenChar,
                          containerSize, isRTL);
  for (i = 0; i < mSeparatorsCount; i++) {
    ApplyUnstretchedMetrics(aPresContext, *aReflowState.rendContext,
                            fontSizeInflation, &mSeparatorsChar[i],
                            containerSize, isRTL);
  }
  ApplyUnstretchedMetrics(aPresContext, *aReflowState.rendContext,
                          fontSizeInflation, mCloseChar,
                          containerSize, isRTL);

  //////////////////////////////////////////
  // Prepare the opening fence, separators, and closing fence, and
  // adjust the origin of children.

  // we need to center around the axis
  nscoord delta = std::max(containerSize.ascent - axisHeight, 
                         containerSize.descent + axisHeight);
  containerSize.ascent = delta + axisHeight;
  containerSize.descent = delta - axisHeight;

  /////////////////
  // opening fence ...
  ReflowChar(aPresContext, *aReflowState.rendContext, *fm,
             fontSizeInflation, mOpenChar,
             NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel, 
             axisHeight, leading, em, containerSize, ascent, descent, isRTL);
  /////////////////
  // separators ...
  for (i = 0; i < mSeparatorsCount; i++) {
    ReflowChar(aPresContext, *aReflowState.rendContext, *fm,
               fontSizeInflation, &mSeparatorsChar[i],
               NS_MATHML_OPERATOR_FORM_INFIX, font->mScriptLevel,
               axisHeight, leading, em, containerSize, ascent, descent, isRTL);
  }
  /////////////////
  // closing fence ...
  ReflowChar(aPresContext, *aReflowState.rendContext, *fm,
             fontSizeInflation, mCloseChar,
             NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel,
             axisHeight, leading, em, containerSize, ascent, descent, isRTL);

  //////////////////
  // Adjust the origins of each child.
  // and update our bounding metrics

  i = 0;
  nscoord dx = 0;
  nsBoundingMetrics bm;
  bool firstTime = true;
  nsMathMLChar *leftChar, *rightChar;
  if (isRTL) {
    leftChar = mCloseChar;
    rightChar = mOpenChar;
  } else {
    leftChar = mOpenChar;
    rightChar = mCloseChar;
  }

  if (leftChar) {
    PlaceChar(leftChar, ascent, bm, dx);
    aDesiredSize.mBoundingMetrics = bm;
    firstTime = false;
  }

  if (isRTL) {
    childFrame = this->GetLastChild(nsIFrame::kPrincipalList);
  } else {
    childFrame = firstChild;
  }
  while (childFrame) {
    nsHTMLReflowMetrics childSize(aReflowState);
    GetReflowAndBoundingMetricsFor(childFrame, childSize, bm);
    if (firstTime) {
      firstTime = false;
      aDesiredSize.mBoundingMetrics  = bm;
    }
    else  
      aDesiredSize.mBoundingMetrics += bm;

    FinishReflowChild(childFrame, aPresContext, childSize, nullptr,
                      dx, ascent - childSize.BlockStartAscent(), 0);
    dx += childSize.Width();

    if (i < mSeparatorsCount) {
      PlaceChar(&mSeparatorsChar[isRTL ? mSeparatorsCount - 1 - i : i],
                ascent, bm, dx);
      aDesiredSize.mBoundingMetrics += bm;
    }
    i++;

    if (isRTL) {
      childFrame = childFrame->GetPrevSibling();
    } else {
      childFrame = childFrame->GetNextSibling();
    }
  }

  if (rightChar) {
    PlaceChar(rightChar, ascent, bm, dx);
    if (firstTime)
      aDesiredSize.mBoundingMetrics  = bm;
    else  
      aDesiredSize.mBoundingMetrics += bm;
  }

  aDesiredSize.Width() = aDesiredSize.mBoundingMetrics.width;
  aDesiredSize.Height() = ascent + descent;
  aDesiredSize.SetBlockStartAscent(ascent);

  SetBoundingMetrics(aDesiredSize.mBoundingMetrics);
  SetReference(nsPoint(0, aDesiredSize.BlockStartAscent()));

  // see if we should fix the spacing
  FixInterFrameSpacing(aDesiredSize);

  // Finished with these:
  ClearSavedChildMetrics();

  // Set our overflow area
  GatherAndStoreOverflow(&aDesiredSize);

  aStatus = NS_FRAME_COMPLETE;
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
}
Ejemplo n.º 23
0
/* virtual */ nsresult
nsMathMLmunderoverFrame::Place(nsRenderingContext& aRenderingContext,
                               bool                 aPlaceOrigin,
                               nsHTMLReflowMetrics& aDesiredSize)
{
  nsIAtom* tag = mContent->Tag();
  if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
      StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) {
    //place like sub sup or subsup
    if (tag == nsGkAtoms::munderover_) {
      return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(),
                                                          aRenderingContext,
                                                          aPlaceOrigin,
                                                          aDesiredSize,
                                                          this, 0, 0);
    } else if (tag == nsGkAtoms::munder_) {
      return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(),
                                                          aRenderingContext,
                                                          aPlaceOrigin,
                                                          aDesiredSize,
                                                          this, 0, 0);
    } else {
      NS_ASSERTION(tag == nsGkAtoms::mover_, "mContent->Tag() not recognized");
      return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(),
                                                          aRenderingContext,
                                                          aPlaceOrigin,
                                                          aDesiredSize,
                                                          this, 0, 0);
    }
    
  }

  ////////////////////////////////////
  // Get the children's desired sizes

  nsBoundingMetrics bmBase, bmUnder, bmOver;
  nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode());
  nsHTMLReflowMetrics underSize(aDesiredSize.GetWritingMode());
  nsHTMLReflowMetrics overSize(aDesiredSize.GetWritingMode());
  nsIFrame* overFrame = nullptr;
  nsIFrame* underFrame = nullptr;
  nsIFrame* baseFrame = mFrames.FirstChild();
  underSize.SetBlockStartAscent(0);
  overSize.SetBlockStartAscent(0);
  bool haveError = false;
  if (baseFrame) {
    if (tag == nsGkAtoms::munder_ ||
        tag == nsGkAtoms::munderover_) {
      underFrame = baseFrame->GetNextSibling();
    } else if (tag == nsGkAtoms::mover_) {
      overFrame = baseFrame->GetNextSibling();
    }
  }
  if (underFrame && tag == nsGkAtoms::munderover_) {
    overFrame = underFrame->GetNextSibling();
  }
  
  if (tag == nsGkAtoms::munder_) {
    if (!baseFrame || !underFrame || underFrame->GetNextSibling()) {
      // report an error, encourage people to get their markups in order
      haveError = true;
    }
  }
  if (tag == nsGkAtoms::mover_) {
    if (!baseFrame || !overFrame || overFrame->GetNextSibling()) {
      // report an error, encourage people to get their markups in order
      haveError = true;
    }
  }
  if (tag == nsGkAtoms::munderover_) {
    if (!baseFrame || !underFrame || !overFrame || overFrame->GetNextSibling()) {
      // report an error, encourage people to get their markups in order
      haveError = true;
    }
  }
  if (haveError) {
    if (aPlaceOrigin) {
      ReportChildCountError();
    } 
    return ReflowError(aRenderingContext, aDesiredSize);
  }
  GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
  if (underFrame) {
    GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder);
  }
  if (overFrame) {
    GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver);
  }

  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);

  ////////////////////
  // Place Children

  nsRefPtr<nsFontMetrics> fm;
  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));

  nscoord xHeight = fm->XHeight();
  nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
  gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();

  nscoord ruleThickness;
  GetRuleThickness (aRenderingContext, fm, ruleThickness);

  nscoord correction = 0;
  GetItalicCorrection (bmBase, correction);

  // there are 2 different types of placement depending on 
  // whether we want an accented under or not

  nscoord underDelta1 = 0; // gap between base and underscript
  nscoord underDelta2 = 0; // extra space beneath underscript

  if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {
    // Rule 13a, App. G, TeXbook
    nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy; 
    GetBigOpSpacings (fm, 
                      dummy, bigOpSpacing2, 
                      dummy, bigOpSpacing4, 
                      bigOpSpacing5);
    if (mathFont) {
      // XXXfredw The Open Type MATH table has some StretchStack* parameters
      // that we may use when the base is a stretchy horizontal operator. See
      // bug 963131.
      bigOpSpacing2 =
        mathFont->GetMathConstant(gfxFontEntry::LowerLimitGapMin,
                                  oneDevPixel);
      bigOpSpacing4 =
        mathFont->GetMathConstant(gfxFontEntry::LowerLimitBaselineDropMin,
                                  oneDevPixel);
      bigOpSpacing5 = 0;
    }
    underDelta1 = std::max(bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent));
    underDelta2 = bigOpSpacing5;
  }
  else {
    // No corresponding rule in TeXbook - we are on our own here
    // XXX tune the gap delta between base and underscript 
    // XXX Should we use Rule 10 like \underline does?
    // XXXfredw Perhaps use the Underbar* parameters of the MATH table. See
    // bug 963125.
    underDelta1 = ruleThickness + onePixel/2;
    underDelta2 = ruleThickness;
  }
  // empty under?
  if (!(bmUnder.ascent + bmUnder.descent)) {
    underDelta1 = 0;
    underDelta2 = 0;
  }

  nscoord overDelta1 = 0; // gap between base and overscript
  nscoord overDelta2 = 0; // extra space above overscript

  if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {    
    // Rule 13a, App. G, TeXbook
    // XXXfredw The Open Type MATH table has some StretchStack* parameters
    // that we may use when the base is a stretchy horizontal operator. See
    // bug 963131.
    nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy; 
    GetBigOpSpacings (fm, 
                      bigOpSpacing1, dummy, 
                      bigOpSpacing3, dummy, 
                      bigOpSpacing5);
    if (mathFont) {
      // XXXfredw The Open Type MATH table has some StretchStack* parameters
      // that we may use when the base is a stretchy horizontal operator. See
      // bug 963131.
      bigOpSpacing1 =
        mathFont->GetMathConstant(gfxFontEntry::UpperLimitGapMin,
                                  oneDevPixel);
      bigOpSpacing3 =
        mathFont->GetMathConstant(gfxFontEntry::UpperLimitBaselineRiseMin,
                                  oneDevPixel);
      bigOpSpacing5 = 0;
    }
    overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - bmOver.descent));
    overDelta2 = bigOpSpacing5;

    // XXX This is not a TeX rule... 
    // delta1 (as computed abvove) can become really big when bmOver.descent is
    // negative,  e.g., if the content is &OverBar. In such case, we use the height
    if (bmOver.descent < 0)    
      overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - (bmOver.ascent + bmOver.descent)));
  }
  else {
    // Rule 12, App. G, TeXbook
    // We are going to modify this rule to make it more general.
    // The idea behind Rule 12 in the TeXBook is to keep the accent
    // as close to the base as possible, while ensuring that the
    // distance between the *baseline* of the accent char and 
    // the *baseline* of the base is atleast x-height. 
    // The idea is that for normal use, we would like all the accents
    // on a line to line up atleast x-height above the baseline 
    // if possible. 
    // When the ascent of the base is >= x-height, 
    // the baseline of the accent char is placed just above the base
    // (specifically, the baseline of the accent char is placed 
    // above the baseline of the base by the ascent of the base).
    // For ease of implementation, 
    // this assumes that the font-designer designs accents 
    // in such a way that the bottom of the accent is atleast x-height
    // above its baseline, otherwise there will be collisions
    // with the base. Also there should be proper padding between
    // the bottom of the accent char and its baseline.
    // The above rule may not be obvious from a first
    // reading of rule 12 in the TeXBook !!!
    // The mathml <mover> tag can use accent chars that
    // do not follow this convention. So we modify TeX's rule 
    // so that TeX's rule gets subsumed for accents that follow 
    // TeX's convention,
    // while also allowing accents that do not follow the convention :
    // we try to keep the *bottom* of the accent char atleast x-height 
    // from the baseline of the base char. we also slap on an extra
    // padding between the accent and base chars.
    overDelta1 = ruleThickness + onePixel/2;
    nscoord accentBaseHeight = xHeight;
    if (mathFont) {
      accentBaseHeight =
        mathFont->GetMathConstant(gfxFontEntry::AccentBaseHeight,
                                  oneDevPixel);
    }
    if (bmBase.ascent < accentBaseHeight) {
      // also ensure at least accentBaseHeight above the baseline of the base
      overDelta1 += accentBaseHeight - bmBase.ascent;
    }
    overDelta2 = ruleThickness;
  }
  // empty over?
  if (!(bmOver.ascent + bmOver.descent)) {
    overDelta1 = 0;
    overDelta2 = 0;
  }

  nscoord dxBase = 0, dxOver = 0, dxUnder = 0;
  nsAutoString valueAlign;
  enum {
    center,
    left,
    right
  } alignPosition = center;

  if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, valueAlign)) {
    if (valueAlign.EqualsLiteral("left")) {
      alignPosition = left;
    } else if (valueAlign.EqualsLiteral("right")) {
      alignPosition = right;
    }
  }

  //////////
  // pass 1, do what <mover> does: attach the overscript on the base

  // Ad-hoc - This is to override fonts which have ready-made _accent_
  // glyphs with negative lbearing and rbearing. We want to position
  // the overscript ourselves
  nscoord overWidth = bmOver.width;
  if (!overWidth && (bmOver.rightBearing - bmOver.leftBearing > 0)) {
    overWidth = bmOver.rightBearing - bmOver.leftBearing;
    dxOver = -bmOver.leftBearing;
  }

  if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {
    mBoundingMetrics.width = bmBase.width; 
    if (alignPosition == center) {
      dxOver += correction;
    }
  }
  else {
    mBoundingMetrics.width = std::max(bmBase.width, overWidth);
    if (alignPosition == center) {
      dxOver += correction/2;
    }
  }
  
  if (alignPosition == center) {
    dxOver += (mBoundingMetrics.width - overWidth)/2;
    dxBase = (mBoundingMetrics.width - bmBase.width)/2;
  } else if (alignPosition == right) {
    dxOver += mBoundingMetrics.width - overWidth;
    dxBase = mBoundingMetrics.width - bmBase.width;
  }

  mBoundingMetrics.ascent = 
    bmBase.ascent + overDelta1 + bmOver.ascent + bmOver.descent;
  mBoundingMetrics.descent = bmBase.descent;
  mBoundingMetrics.leftBearing = 
    std::min(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing);
  mBoundingMetrics.rightBearing = 
    std::max(dxBase + bmBase.rightBearing, dxOver + bmOver.rightBearing);

  //////////
  // pass 2, do what <munder> does: attach the underscript on the previous
  // result. We conceptually view the previous result as an "anynomous base" 
  // from where to attach the underscript. Hence if the underscript is empty,
  // we should end up like <mover>. If the overscript is empty, we should
  // end up like <munder>.

  nsBoundingMetrics bmAnonymousBase = mBoundingMetrics;
  nscoord ascentAnonymousBase =
    std::max(mBoundingMetrics.ascent + overDelta2,
             overSize.BlockStartAscent() + bmOver.descent +
             overDelta1 + bmBase.ascent);
  ascentAnonymousBase = std::max(ascentAnonymousBase,
                                 baseSize.BlockStartAscent());

  // Width of non-spacing marks is zero so use left and right bearing.
  nscoord underWidth = bmUnder.width;
  if (!underWidth) {
    underWidth = bmUnder.rightBearing - bmUnder.leftBearing;
    dxUnder = -bmUnder.leftBearing;
  }

  nscoord maxWidth = std::max(bmAnonymousBase.width, underWidth);
  if (alignPosition == center &&
      !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {
    GetItalicCorrection(bmAnonymousBase, correction);
    dxUnder += -correction/2;
  }
  nscoord dxAnonymousBase = 0;
  if (alignPosition == center) {
    dxUnder += (maxWidth - underWidth)/2;
    dxAnonymousBase = (maxWidth - bmAnonymousBase.width)/2;
  } else if (alignPosition == right) {
    dxUnder += maxWidth - underWidth;
    dxAnonymousBase = maxWidth - bmAnonymousBase.width;
  }

  // adjust the offsets of the real base and overscript since their
  // final offsets should be relative to us...
  dxOver += dxAnonymousBase;
  dxBase += dxAnonymousBase;

  mBoundingMetrics.width =
    std::max(dxAnonymousBase + bmAnonymousBase.width, dxUnder + bmUnder.width);
  // At this point, mBoundingMetrics.ascent = bmAnonymousBase.ascent 
  mBoundingMetrics.descent = 
    bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + bmUnder.descent;
  mBoundingMetrics.leftBearing =
    std::min(dxAnonymousBase + bmAnonymousBase.leftBearing, dxUnder + bmUnder.leftBearing);
  mBoundingMetrics.rightBearing = 
    std::max(dxAnonymousBase + bmAnonymousBase.rightBearing, dxUnder + bmUnder.rightBearing);

  aDesiredSize.SetBlockStartAscent(ascentAnonymousBase);
  aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
    std::max(mBoundingMetrics.descent + underDelta2,
           bmAnonymousBase.descent + underDelta1 + bmUnder.ascent +
             underSize.Height() - underSize.BlockStartAscent());
  aDesiredSize.Height() = std::max(aDesiredSize.Height(),
                               aDesiredSize.BlockStartAscent() +
                               baseSize.Height() - baseSize.BlockStartAscent());
  aDesiredSize.Width() = mBoundingMetrics.width;
  aDesiredSize.mBoundingMetrics = mBoundingMetrics;

  mReference.x = 0;
  mReference.y = aDesiredSize.BlockStartAscent();

  if (aPlaceOrigin) {
    nscoord dy;
    // place overscript
    if (overFrame) {
      dy = aDesiredSize.BlockStartAscent() -
           mBoundingMetrics.ascent + bmOver.ascent -
           overSize.BlockStartAscent();
      FinishReflowChild (overFrame, PresContext(), overSize, nullptr, dxOver, dy, 0);
    }
    // place base
    dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
    FinishReflowChild (baseFrame, PresContext(), baseSize, nullptr, dxBase, dy, 0);
    // place underscript
    if (underFrame) {
      dy = aDesiredSize.BlockStartAscent() +
           mBoundingMetrics.descent - bmUnder.descent -
           underSize.BlockStartAscent();
      FinishReflowChild (underFrame, PresContext(), underSize, nullptr,
                         dxUnder, dy, 0);
    }
  }
  return NS_OK;
}
Ejemplo n.º 24
0
/* virtual */ nsresult
nsMathMLmencloseFrame::PlaceInternal(nsRenderingContext& aRenderingContext,
                                     bool                 aPlaceOrigin,
                                     nsHTMLReflowMetrics& aDesiredSize,
                                     bool                 aWidthOnly)
{
  ///////////////
  // Measure the size of our content using the base class to format like an
  // inferred mrow.
  nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode());
  nsresult rv =
    nsMathMLContainerFrame::Place(aRenderingContext, false, baseSize);

  if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
      DidReflowChildren(GetFirstPrincipalChild());
      return rv;
    }

  nsBoundingMetrics bmBase = baseSize.mBoundingMetrics;
  nscoord dx_left = 0, dx_right = 0;
  nsBoundingMetrics bmLongdivChar, bmRadicalChar;
  nscoord radicalAscent = 0, radicalDescent = 0;
  nscoord longdivAscent = 0, longdivDescent = 0;
  nscoord psi = 0;
  nscoord leading = 0;

  ///////////////
  // Thickness of bars and font metrics
  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);

  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
  nsRefPtr<nsFontMetrics> fm;
  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
                                        fontSizeInflation);
  GetRuleThickness(aRenderingContext, fm, mRuleThickness);
  if (mRuleThickness < onePixel) {
    mRuleThickness = onePixel;
  }

  char16_t one = '1';
  nsBoundingMetrics bmOne =
    nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, aRenderingContext);

  ///////////////
  // General rules: the menclose element takes the size of the enclosed content.
  // We add a padding when needed.

  // determine padding & psi
  nscoord padding = 3 * mRuleThickness;
  nscoord delta = padding % onePixel;
  if (delta)
    padding += onePixel - delta; // round up

  if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
    GetRadicalParameters(fm, StyleFont()->mMathDisplay ==
                         NS_MATHML_DISPLAYSTYLE_BLOCK,
                         mRadicalRuleThickness, leading, psi);

    // make sure that the rule appears on on screen
    if (mRadicalRuleThickness < onePixel) {
      mRadicalRuleThickness = onePixel;
    }

    // adjust clearance psi to get an exact number of pixels -- this
    // gives a nicer & uniform look on stacked radicals (bug 130282)
    delta = psi % onePixel;
    if (delta) {
      psi += onePixel - delta; // round up
    }
  }

  // Set horizontal parameters
  if (IsToDraw(NOTATION_ROUNDEDBOX) ||
      IsToDraw(NOTATION_TOP) ||
      IsToDraw(NOTATION_LEFT) ||
      IsToDraw(NOTATION_BOTTOM) ||
      IsToDraw(NOTATION_CIRCLE))
    dx_left = padding;

  if (IsToDraw(NOTATION_ROUNDEDBOX) ||
      IsToDraw(NOTATION_TOP) ||
      IsToDraw(NOTATION_RIGHT) ||
      IsToDraw(NOTATION_BOTTOM) ||
      IsToDraw(NOTATION_CIRCLE))
    dx_right = padding;

  // Set vertical parameters
  if (IsToDraw(NOTATION_RIGHT) ||
      IsToDraw(NOTATION_LEFT) ||
      IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
      IsToDraw(NOTATION_UPDIAGONALARROW) ||
      IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
      IsToDraw(NOTATION_VERTICALSTRIKE) ||
      IsToDraw(NOTATION_CIRCLE) ||
      IsToDraw(NOTATION_ROUNDEDBOX) ||
      IsToDraw(NOTATION_RADICAL) ||
      IsToDraw(NOTATION_LONGDIV) ||
      IsToDraw(NOTATION_PHASORANGLE)) {
      // set a minimal value for the base height
      bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent);
      bmBase.descent = std::max(0, bmBase.descent);
  }

  mBoundingMetrics.ascent = bmBase.ascent;
  mBoundingMetrics.descent = bmBase.descent;
    
  if (IsToDraw(NOTATION_ROUNDEDBOX) ||
      IsToDraw(NOTATION_TOP) ||
      IsToDraw(NOTATION_LEFT) ||
      IsToDraw(NOTATION_RIGHT) ||
      IsToDraw(NOTATION_CIRCLE))
    mBoundingMetrics.ascent += padding;
  
  if (IsToDraw(NOTATION_ROUNDEDBOX) ||
      IsToDraw(NOTATION_LEFT) ||
      IsToDraw(NOTATION_RIGHT) ||
      IsToDraw(NOTATION_BOTTOM) ||
      IsToDraw(NOTATION_CIRCLE))
    mBoundingMetrics.descent += padding;

   ///////////////
   // phasorangle notation
  if (IsToDraw(NOTATION_PHASORANGLE)) {
    nscoord phasorangleWidth = kPhasorangleWidth * mRuleThickness;
    // Update horizontal parameters
    dx_left = std::max(dx_left, phasorangleWidth);
  }

  ///////////////
  // updiagonal arrow notation. We need enough space at the top right corner to
  // draw the arrow head.
  if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
    // This is an estimate, see nsDisplayNotation::Paint for the exact head size
    nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness;

    // We want that the arrow shaft strikes the menclose content and that the
    // arrow head does not overlap with that content. Hence we add some space
    // on the right. We don't add space on the top but only ensure that the
    // ascent is large enough.
    dx_right = std::max(dx_right, arrowHeadSize);
    mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize);
  }

  ///////////////
  // circle notation: we don't want the ellipse to overlap the enclosed
  // content. Hence, we need to increase the size of the bounding box by a
  // factor of at least sqrt(2).
  if (IsToDraw(NOTATION_CIRCLE)) {
    double ratio = (sqrt(2.0) - 1.0) / 2.0;
    nscoord padding2;

    // Update horizontal parameters
    padding2 = ratio * bmBase.width;

    dx_left = std::max(dx_left, padding2);
    dx_right = std::max(dx_right, padding2);

    // Update vertical parameters
    padding2 = ratio * (bmBase.ascent + bmBase.descent);

    mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
                                     bmBase.ascent + padding2);
    mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
                                      bmBase.descent + padding2);
  }

  ///////////////
  // longdiv notation:
  if (IsToDraw(NOTATION_LONGDIV)) {
    if (aWidthOnly) {
        nscoord longdiv_width = mMathMLChar[mLongDivCharIndex].
          GetMaxWidth(PresContext(), aRenderingContext, fontSizeInflation);

        // Update horizontal parameters
        dx_left = std::max(dx_left, longdiv_width);
    } else {
      // Stretch the parenthesis to the appropriate height if it is not
      // big enough.
      nsBoundingMetrics contSize = bmBase;
      contSize.ascent = mRuleThickness;
      contSize.descent = bmBase.ascent + bmBase.descent + psi;

      // height(longdiv) should be >= height(base) + psi + mRuleThickness
      mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aRenderingContext,
                                             fontSizeInflation,
                                             NS_STRETCH_DIRECTION_VERTICAL,
                                             contSize, bmLongdivChar,
                                             NS_STRETCH_LARGER, false);
      mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar);

      // Update horizontal parameters
      dx_left = std::max(dx_left, bmLongdivChar.width);

      // Update vertical parameters
      longdivAscent = bmBase.ascent + psi + mRuleThickness;
      longdivDescent = std::max(bmBase.descent,
                              (bmLongdivChar.ascent + bmLongdivChar.descent -
                               longdivAscent));

      mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
                                       longdivAscent);
      mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
                                        longdivDescent);
    }
  }

  ///////////////
  // radical notation:
  if (IsToDraw(NOTATION_RADICAL)) {
    nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left;
    
    if (aWidthOnly) {
      nscoord radical_width = mMathMLChar[mRadicalCharIndex].
        GetMaxWidth(PresContext(), aRenderingContext, fontSizeInflation);
      
      // Update horizontal parameters
      *dx_leading = std::max(*dx_leading, radical_width);
    } else {
      // Stretch the radical symbol to the appropriate height if it is not
      // big enough.
      nsBoundingMetrics contSize = bmBase;
      contSize.ascent = mRadicalRuleThickness;
      contSize.descent = bmBase.ascent + bmBase.descent + psi;

      // height(radical) should be >= height(base) + psi + mRadicalRuleThickness
      mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aRenderingContext,
                                             fontSizeInflation,
                                             NS_STRETCH_DIRECTION_VERTICAL,
                                             contSize, bmRadicalChar,
                                             NS_STRETCH_LARGER,
                                             StyleVisibility()->mDirection);
      mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar);

      // Update horizontal parameters
      *dx_leading = std::max(*dx_leading, bmRadicalChar.width);

      // Update vertical parameters
      radicalAscent = bmBase.ascent + psi + mRadicalRuleThickness;
      radicalDescent = std::max(bmBase.descent,
                              (bmRadicalChar.ascent + bmRadicalChar.descent -
                               radicalAscent));

      mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
                                       radicalAscent);
      mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
                                        radicalDescent);
    }
  }

  ///////////////
  //
  if (IsToDraw(NOTATION_CIRCLE) ||
      IsToDraw(NOTATION_ROUNDEDBOX) ||
      (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) {
    // center the menclose around the content (horizontally)
    dx_left = dx_right = std::max(dx_left, dx_right);
  }

  ///////////////
  // The maximum size is now computed: set the remaining parameters
  mBoundingMetrics.width = dx_left + bmBase.width + dx_right;

  mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing);
  mBoundingMetrics.rightBearing =
    std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing);
  
  aDesiredSize.Width() = mBoundingMetrics.width;

  aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent,
                                            baseSize.BlockStartAscent()));
  aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
    std::max(mBoundingMetrics.descent,
             baseSize.Height() - baseSize.BlockStartAscent());

  if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
    nscoord desiredSizeAscent = aDesiredSize.BlockStartAscent();
    nscoord desiredSizeDescent = aDesiredSize.Height() -
                                 aDesiredSize.BlockStartAscent();
    
    if (IsToDraw(NOTATION_LONGDIV)) {
      desiredSizeAscent = std::max(desiredSizeAscent,
                                 longdivAscent + leading);
      desiredSizeDescent = std::max(desiredSizeDescent,
                                  longdivDescent + mRuleThickness);
    }
    
    if (IsToDraw(NOTATION_RADICAL)) {
      desiredSizeAscent = std::max(desiredSizeAscent,
                                 radicalAscent + leading);
      desiredSizeDescent = std::max(desiredSizeDescent,
                                    radicalDescent + mRadicalRuleThickness);
    }

    aDesiredSize.SetBlockStartAscent(desiredSizeAscent);
    aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent;
  }
    
  if (IsToDraw(NOTATION_CIRCLE) ||
      IsToDraw(NOTATION_ROUNDEDBOX) ||
      (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) {
    // center the menclose around the content (vertically)
    nscoord dy = std::max(aDesiredSize.BlockStartAscent() - bmBase.ascent,
                          aDesiredSize.Height() -
                          aDesiredSize.BlockStartAscent() - bmBase.descent);

    aDesiredSize.SetBlockStartAscent(bmBase.ascent + dy);
    aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + bmBase.descent + dy;
  }

  // Update mBoundingMetrics ascent/descent
  if (IsToDraw(NOTATION_TOP) ||
      IsToDraw(NOTATION_RIGHT) ||
      IsToDraw(NOTATION_LEFT) ||
      IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
      IsToDraw(NOTATION_UPDIAGONALARROW) ||
      IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
      IsToDraw(NOTATION_VERTICALSTRIKE) ||
      IsToDraw(NOTATION_CIRCLE) ||
      IsToDraw(NOTATION_ROUNDEDBOX))
    mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent();
  
  if (IsToDraw(NOTATION_BOTTOM) ||
      IsToDraw(NOTATION_RIGHT) ||
      IsToDraw(NOTATION_LEFT) ||
      IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
      IsToDraw(NOTATION_UPDIAGONALARROW) ||
      IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
      IsToDraw(NOTATION_VERTICALSTRIKE) ||
      IsToDraw(NOTATION_CIRCLE) ||
      IsToDraw(NOTATION_ROUNDEDBOX))
    mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();

  // phasorangle notation:
  // move up from the bottom by the angled line height
  if (IsToDraw(NOTATION_PHASORANGLE))
    mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, 2 * kPhasorangleWidth * mRuleThickness - mBoundingMetrics.descent);
  
  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
  
  mReference.x = 0;
  mReference.y = aDesiredSize.BlockStartAscent();

  if (aPlaceOrigin) {
    //////////////////
    // Set position and size of MathMLChars
    if (IsToDraw(NOTATION_LONGDIV))
      mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left -
                                                    bmLongdivChar.width,
                                                    aDesiredSize.BlockStartAscent() -
                                                    longdivAscent,
                                                    bmLongdivChar.width,
                                                    bmLongdivChar.ascent +
                                                    bmLongdivChar.descent));

    if (IsToDraw(NOTATION_RADICAL)) {
      nscoord dx = (StyleVisibility()->mDirection ?
                    dx_left + bmBase.width : dx_left - bmRadicalChar.width);

      mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx,
                                                    aDesiredSize.BlockStartAscent() -
                                                    radicalAscent,
                                                    bmRadicalChar.width,
                                                    bmRadicalChar.ascent +
                                                    bmRadicalChar.descent));
    }

    mContentWidth = bmBase.width;

    //////////////////
    // Finish reflowing child frames
    PositionRowChildFrames(dx_left, aDesiredSize.BlockStartAscent());
  }

  return NS_OK;
}