コード例 #1
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;
}
コード例 #2
0
static
void DebugCheckChildSize(nsIFrame*            aChild,
                         nsHTMLReflowMetrics& aMet)
{
  WritingMode wm = aMet.GetWritingMode();
  if ((aMet.ISize(wm) < 0) || (aMet.ISize(wm) > PROBABLY_TOO_LARGE)) {
    printf("WARNING: cell content %p has large inline size %d \n",
           static_cast<void*>(aChild), int32_t(aMet.ISize(wm)));
  }
}
コード例 #3
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;
}
コード例 #4
0
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();
}
コード例 #5
0
// 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;
}
コード例 #6
0
ファイル: nsVideoFrame.cpp プロジェクト: Natim/gecko-dev
void
nsVideoFrame::Reflow(nsPresContext*           aPresContext,
                     nsHTMLReflowMetrics&     aMetrics,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus)
{
  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("nsVideoFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                  ("enter nsVideoFrame::Reflow: availSize=%d,%d",
                  aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));

  NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");

  aStatus = NS_FRAME_COMPLETE;

  aMetrics.Width() = aReflowState.ComputedWidth();
  aMetrics.Height() = aReflowState.ComputedHeight();

  // stash this away so we can compute our inner area later
  mBorderPadding   = aReflowState.ComputedPhysicalBorderPadding();

  aMetrics.Width() += mBorderPadding.left + mBorderPadding.right;
  aMetrics.Height() += mBorderPadding.top + mBorderPadding.bottom;

  // Reflow the child frames. We may have up to two, an image frame
  // which is the poster, and a box frame, which is the video controls.
  for (nsIFrame* child : mFrames) {
    if (child->GetContent() == mPosterImage) {
      // Reflow the poster frame.
      nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child);
      nsHTMLReflowMetrics kidDesiredSize(aReflowState);
      WritingMode wm = imageFrame->GetWritingMode();
      LogicalSize availableSize = aReflowState.AvailableSize(wm);
      LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()).
                             ConvertTo(wm, aMetrics.GetWritingMode());
      nsHTMLReflowState kidReflowState(aPresContext,
                                       aReflowState,
                                       imageFrame,
                                       availableSize,
                                       &cbSize);

      nsRect posterRenderRect;
      if (ShouldDisplayPoster()) {
        posterRenderRect =
          nsRect(nsPoint(mBorderPadding.left, mBorderPadding.top),
                 nsSize(aReflowState.ComputedWidth(),
                        aReflowState.ComputedHeight()));
      }
      kidReflowState.SetComputedWidth(posterRenderRect.width);
      kidReflowState.SetComputedHeight(posterRenderRect.height);
      ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowState,
                  posterRenderRect.x, posterRenderRect.y, 0, aStatus);
      FinishReflowChild(imageFrame, aPresContext,
                        kidDesiredSize, &kidReflowState,
                        posterRenderRect.x, posterRenderRect.y, 0);
    } else if (child->GetContent() == mVideoControls) {
      // Reflow the video controls frame.
      nsBoxLayoutState boxState(PresContext(), aReflowState.rendContext);
      nsSize size = child->GetSize();
      nsBoxFrame::LayoutChildAt(boxState,
                                child,
                                nsRect(mBorderPadding.left,
                                       mBorderPadding.top,
                                       aReflowState.ComputedWidth(),
                                       aReflowState.ComputedHeight()));
      if (child->GetSize() != size) {
        RefPtr<nsRunnable> event = new DispatchResizeToControls(child->GetContent());
        nsContentUtils::AddScriptRunner(event);
      }
    } else if (child->GetContent() == mCaptionDiv) {
      // Reflow to caption div
      nsHTMLReflowMetrics kidDesiredSize(aReflowState);
      WritingMode wm = child->GetWritingMode();
      LogicalSize availableSize = aReflowState.AvailableSize(wm);
      LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()).
                             ConvertTo(wm, aMetrics.GetWritingMode());
      nsHTMLReflowState kidReflowState(aPresContext,
                                       aReflowState,
                                       child,
                                       availableSize,
                                       &cbSize);
      nsSize size(aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
      size.width -= kidReflowState.ComputedPhysicalBorderPadding().LeftRight();
      size.height -= kidReflowState.ComputedPhysicalBorderPadding().TopBottom();

      kidReflowState.SetComputedWidth(std::max(size.width, 0));
      kidReflowState.SetComputedHeight(std::max(size.height, 0));

      ReflowChild(child, aPresContext, kidDesiredSize, kidReflowState,
                  mBorderPadding.left, mBorderPadding.top, 0, aStatus);
      FinishReflowChild(child, aPresContext,
                        kidDesiredSize, &kidReflowState,
                        mBorderPadding.left, mBorderPadding.top, 0);
    }
  }
  aMetrics.SetOverflowAreasToDesiredBounds();

  FinishAndStoreOverflow(&aMetrics);

  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                  ("exit nsVideoFrame::Reflow: size=%d,%d",
                  aMetrics.Width(), aMetrics.Height()));
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
}
コード例 #7
0
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();
}
コード例 #8
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;
}
コード例 #9
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);
}
コード例 #10
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;

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

  nscoord mEmHeight;
  nsRefPtr<nsFontMetrics> fm;
  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
  aRenderingContext.SetFont(fm);
  GetRuleThickness(aRenderingContext, fm, mRuleThickness);
  GetEmHeight(fm, mEmHeight);

  char16_t one = '1';
  nsBoundingMetrics bmOne = aRenderingContext.GetBoundingMetrics(&one, 1);

  ///////////////
  // 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)) {
      nscoord phi;
      // Rule 11, App. G, TeXbook
      // psi = clearance between rule and content
      if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags))
        phi = fm->XHeight();
      else
        phi = mRuleThickness;
      psi = mRuleThickness + phi / 4;

      delta = psi % onePixel;
      if (delta)
        psi += onePixel - delta; // round up
    }

  if (mRuleThickness < onePixel)
    mRuleThickness = onePixel;
 
  // 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)) {
      // 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;

  ///////////////
  // 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);

        // 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,
                                             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);
      
      // 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 = mRuleThickness;
      contSize.descent = bmBase.ascent + bmBase.descent + psi;

      // height(radical) should be >= height(base) + psi + mRuleThickness
      mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aRenderingContext,
                                             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 + mRuleThickness;
      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.SetTopAscent(std::max(mBoundingMetrics.ascent, baseSize.TopAscent()));
  aDesiredSize.Height() = aDesiredSize.TopAscent() +
    std::max(mBoundingMetrics.descent, baseSize.Height() - baseSize.TopAscent());

  if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
    // get the leading to be left at the top of the resulting frame
    // this seems more reliable than using fm->GetLeading() on suspicious
    // fonts
    nscoord leading = nscoord(0.2f * mEmHeight);
    nscoord desiredSizeAscent = aDesiredSize.TopAscent();
    nscoord desiredSizeDescent = aDesiredSize.Height() - aDesiredSize.TopAscent();
    
    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 + mRuleThickness);
    }

    aDesiredSize.SetTopAscent(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.TopAscent() - bmBase.ascent,
                        aDesiredSize.Height() - aDesiredSize.TopAscent() -
                        bmBase.descent);

    aDesiredSize.SetTopAscent(bmBase.ascent + dy);
    aDesiredSize.Height() = aDesiredSize.TopAscent() + 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.TopAscent();
  
  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.TopAscent();

  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
  
  mReference.x = 0;
  mReference.y = aDesiredSize.TopAscent();

  if (aPlaceOrigin) {
    //////////////////
    // Set position and size of MathMLChars
    if (IsToDraw(NOTATION_LONGDIV))
      mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left -
                                                    bmLongdivChar.width,
                                                    aDesiredSize.TopAscent() -
                                                    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.TopAscent() -
                                                    radicalAscent,
                                                    bmRadicalChar.width,
                                                    bmRadicalChar.ascent +
                                                    bmRadicalChar.descent));
    }

    mContentWidth = bmBase.width;

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

  return NS_OK;
}
コード例 #11
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);
}