// Only place the selected child ... /* virtual */ nsresult nsMathMLSelectedFrame::Place(DrawTarget* aDrawTarget, bool aPlaceOrigin, ReflowOutput& aDesiredSize) { nsIFrame* childFrame = GetSelectedFrame(); if (mInvalidMarkup) { return ReflowError(aDrawTarget, aDesiredSize); } aDesiredSize.ClearSize(); aDesiredSize.SetBlockStartAscent(0); mBoundingMetrics = nsBoundingMetrics(); if (childFrame) { GetReflowAndBoundingMetricsFor(childFrame, aDesiredSize, mBoundingMetrics); if (aPlaceOrigin) { FinishReflowChild(childFrame, PresContext(), aDesiredSize, nullptr, 0, 0, 0); } mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); } aDesiredSize.mBoundingMetrics = mBoundingMetrics; return NS_OK; }
// Only reflow the selected child ... void nsMathMLSelectedFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); mPresentationData.flags &= ~NS_MATHML_ERROR; aStatus.Reset(); aDesiredSize.ClearSize(); aDesiredSize.SetBlockStartAscent(0); mBoundingMetrics = nsBoundingMetrics(); nsIFrame* childFrame = GetSelectedFrame(); if (childFrame) { WritingMode wm = childFrame->GetWritingMode(); LogicalSize availSize = aReflowInput.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; ReflowInput childReflowInput(aPresContext, aReflowInput, childFrame, availSize); ReflowChild(childFrame, aPresContext, aDesiredSize, childReflowInput, aStatus); SaveReflowAndBoundingMetricsFor(childFrame, aDesiredSize, aDesiredSize.mBoundingMetrics); mBoundingMetrics = aDesiredSize.mBoundingMetrics; } FinalizeReflow(aReflowInput.mRenderingContext->GetDrawTarget(), aDesiredSize); NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); }
void nsTextControlFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); // make sure that the form registers itself on the initial/first reflow if (mState & NS_FRAME_FIRST_REFLOW) { nsFormControlFrame::RegUnRegAccessKey(this, true); } // set values of reflow's out parameters WritingMode wm = aReflowInput.GetWritingMode(); LogicalSize finalSize(wm, aReflowInput.ComputedISize() + aReflowInput.ComputedLogicalBorderPadding().IStartEnd(wm), aReflowInput.ComputedBSize() + aReflowInput.ComputedLogicalBorderPadding().BStartEnd(wm)); aDesiredSize.SetSize(wm, finalSize); // computation of the ascent wrt the input height nscoord lineHeight = aReflowInput.ComputedBSize(); float inflation = nsLayoutUtils::FontSizeInflationFor(this); if (!IsSingleLineTextControl()) { lineHeight = ReflowInput::CalcLineHeight(GetContent(), StyleContext(), NS_AUTOHEIGHT, inflation); } RefPtr<nsFontMetrics> fontMet = nsLayoutUtils::GetFontMetricsForFrame(this, inflation); // now adjust for our borders and padding aDesiredSize.SetBlockStartAscent( nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight, wm.IsLineInverted()) + aReflowInput.ComputedLogicalBorderPadding().BStart(wm)); // overflow handling aDesiredSize.SetOverflowAreasToDesiredBounds(); // perform reflow on all kids nsIFrame* kid = mFrames.FirstChild(); while (kid) { ReflowTextControlChild(kid, aPresContext, aReflowInput, aStatus, aDesiredSize); kid = kid->GetNextSibling(); } // take into account css properties that affect overflow handling FinishAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); }
/* virtual */ nsresult nsMathMLmpaddedFrame::Place(DrawTarget* aDrawTarget, bool aPlaceOrigin, ReflowOutput& aDesiredSize) { nsresult rv = nsMathMLContainerFrame::Place(aDrawTarget, false, aDesiredSize); if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { DidReflowChildren(PrincipalChildList().FirstChild()); return rv; } nscoord height = aDesiredSize.BlockStartAscent(); nscoord depth = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); // The REC says: // // "The lspace attribute ('leading' space) specifies the horizontal location // of the positioning point of the child content with respect to the // positioning point of the mpadded element. By default they coincide, and // therefore absolute values for lspace have the same effect as relative // values." // // "MathML renderers should ensure that, except for the effects of the // attributes, the relative spacing between the contents of the mpadded // element and surrounding MathML elements would not be modified by replacing // an mpadded element with an mrow element with the same content, even if // linebreaking occurs within the mpadded element." // // (http://www.w3.org/TR/MathML/chapter3.html#presm.mpadded) // // "In those discussions, the terms leading and trailing are used to specify // a side of an object when which side to use depends on the directionality; // ie. leading means left in LTR but right in RTL." // (http://www.w3.org/TR/MathML/chapter3.html#presm.bidi.math) nscoord lspace = 0; // In MathML3, "width" will be the bounding box width and "advancewidth" will // refer "to the horizontal distance between the positioning point of the // mpadded and the positioning point for the following content". MathML2 // doesn't make the distinction. nscoord width = aDesiredSize.Width(); nscoord voffset = 0; int32_t pseudoUnit; nscoord initialWidth = width; float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); // update width pseudoUnit = (mWidthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF) ? NS_MATHML_PSEUDO_UNIT_WIDTH : mWidthPseudoUnit; UpdateValue(mWidthSign, pseudoUnit, mWidth, aDesiredSize, width, fontSizeInflation); width = std::max(0, width); // update "height" (this is the ascent in the terminology of the REC) pseudoUnit = (mHeightPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF) ? NS_MATHML_PSEUDO_UNIT_HEIGHT : mHeightPseudoUnit; UpdateValue(mHeightSign, pseudoUnit, mHeight, aDesiredSize, height, fontSizeInflation); height = std::max(0, height); // update "depth" (this is the descent in the terminology of the REC) pseudoUnit = (mDepthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF) ? NS_MATHML_PSEUDO_UNIT_DEPTH : mDepthPseudoUnit; UpdateValue(mDepthSign, pseudoUnit, mDepth, aDesiredSize, depth, fontSizeInflation); depth = std::max(0, depth); // update lspace if (mLeadingSpacePseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) { pseudoUnit = mLeadingSpacePseudoUnit; UpdateValue(mLeadingSpaceSign, pseudoUnit, mLeadingSpace, aDesiredSize, lspace, fontSizeInflation); } // update voffset if (mVerticalOffsetPseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) { pseudoUnit = mVerticalOffsetPseudoUnit; UpdateValue(mVerticalOffsetSign, pseudoUnit, mVerticalOffset, aDesiredSize, voffset, fontSizeInflation); } // do the padding now that we have everything // The idea here is to maintain the invariant that <mpadded>...</mpadded> (i.e., // with no attributes) looks the same as <mrow>...</mrow>. But when there are // attributes, tweak our metrics and move children to achieve the desired visual // effects. if ((StyleVisibility()->mDirection ? mWidthSign : mLeadingSpaceSign) != NS_MATHML_SIGN_INVALID) { // there was padding on the left. dismiss the left italic correction now // (so that our parent won't correct us) mBoundingMetrics.leftBearing = 0; } if ((StyleVisibility()->mDirection ? mLeadingSpaceSign : mWidthSign) != NS_MATHML_SIGN_INVALID) { // there was padding on the right. dismiss the right italic correction now // (so that our parent won't correct us) mBoundingMetrics.width = width; mBoundingMetrics.rightBearing = mBoundingMetrics.width; } nscoord dx = (StyleVisibility()->mDirection ? width - initialWidth - lspace : lspace); aDesiredSize.SetBlockStartAscent(height); aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.Height() = depth + aDesiredSize.BlockStartAscent(); mBoundingMetrics.ascent = height; mBoundingMetrics.descent = depth; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); if (aPlaceOrigin) { // Finish reflowing child frames, positioning their origins. PositionRowChildFrames(dx, aDesiredSize.BlockStartAscent() - voffset); } return NS_OK; }
/* virtual */ nsresult nsMathMLmencloseFrame::PlaceInternal(DrawTarget* aDrawTarget, bool aPlaceOrigin, ReflowOutput& aDesiredSize, bool aWidthOnly) { /////////////// // Measure the size of our content using the base class to format like an // inferred mrow. ReflowOutput baseSize(aDesiredSize.GetWritingMode()); nsresult rv = nsMathMLContainerFrame::Place(aDrawTarget, false, baseSize); if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { DidReflowChildren(PrincipalChildList().FirstChild()); return rv; } nsBoundingMetrics bmBase = baseSize.mBoundingMetrics; nscoord dx_left = 0, dx_right = 0; nsBoundingMetrics bmLongdivChar, bmRadicalChar; nscoord radicalAscent = 0, radicalDescent = 0; nscoord longdivAscent = 0, longdivDescent = 0; nscoord psi = 0; nscoord leading = 0; /////////////// // Thickness of bars and font metrics nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation); GetRuleThickness(aDrawTarget, fm, mRuleThickness); if (mRuleThickness < onePixel) { mRuleThickness = onePixel; } char16_t one = '1'; nsBoundingMetrics bmOne = nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, aDrawTarget); /////////////// // General rules: the menclose element takes the size of the enclosed content. // We add a padding when needed. // determine padding & psi nscoord padding = 3 * mRuleThickness; nscoord delta = padding % onePixel; if (delta) padding += onePixel - delta; // round up if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { GetRadicalParameters(fm, StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK, mRadicalRuleThickness, leading, psi); // make sure that the rule appears on on screen if (mRadicalRuleThickness < onePixel) { mRadicalRuleThickness = onePixel; } // adjust clearance psi to get an exact number of pixels -- this // gives a nicer & uniform look on stacked radicals (bug 130282) delta = psi % onePixel; if (delta) { psi += onePixel - delta; // round up } } // Set horizontal parameters if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_CIRCLE)) dx_left = padding; if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_CIRCLE)) dx_right = padding; // Set vertical parameters if (IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) || IsToDraw(NOTATION_UPDIAGONALARROW) || IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_RADICAL) || IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_PHASORANGLE)) { // set a minimal value for the base height bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent); bmBase.descent = std::max(0, bmBase.descent); } mBoundingMetrics.ascent = bmBase.ascent; mBoundingMetrics.descent = bmBase.descent; if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_CIRCLE)) mBoundingMetrics.ascent += padding; if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_CIRCLE)) mBoundingMetrics.descent += padding; /////////////// // phasorangle notation if (IsToDraw(NOTATION_PHASORANGLE)) { nscoord phasorangleWidth = kPhasorangleWidth * mRuleThickness; // Update horizontal parameters dx_left = std::max(dx_left, phasorangleWidth); } /////////////// // updiagonal arrow notation. We need enough space at the top right corner to // draw the arrow head. if (IsToDraw(NOTATION_UPDIAGONALARROW)) { // This is an estimate, see nsDisplayNotation::Paint for the exact head size nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness; // We want that the arrow shaft strikes the menclose content and that the // arrow head does not overlap with that content. Hence we add some space // on the right. We don't add space on the top but only ensure that the // ascent is large enough. dx_right = std::max(dx_right, arrowHeadSize); mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize); } /////////////// // circle notation: we don't want the ellipse to overlap the enclosed // content. Hence, we need to increase the size of the bounding box by a // factor of at least sqrt(2). if (IsToDraw(NOTATION_CIRCLE)) { double ratio = (sqrt(2.0) - 1.0) / 2.0; nscoord padding2; // Update horizontal parameters padding2 = ratio * bmBase.width; dx_left = std::max(dx_left, padding2); dx_right = std::max(dx_right, padding2); // Update vertical parameters padding2 = ratio * (bmBase.ascent + bmBase.descent); mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, bmBase.ascent + padding2); mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, bmBase.descent + padding2); } /////////////// // longdiv notation: if (IsToDraw(NOTATION_LONGDIV)) { if (aWidthOnly) { nscoord longdiv_width = mMathMLChar[mLongDivCharIndex]. GetMaxWidth(PresContext(), aDrawTarget, fontSizeInflation); // Update horizontal parameters dx_left = std::max(dx_left, longdiv_width); } else { // Stretch the parenthesis to the appropriate height if it is not // big enough. nsBoundingMetrics contSize = bmBase; contSize.ascent = mRuleThickness; contSize.descent = bmBase.ascent + bmBase.descent + psi; // height(longdiv) should be >= height(base) + psi + mRuleThickness mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aDrawTarget, fontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL, contSize, bmLongdivChar, NS_STRETCH_LARGER, false); mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar); // Update horizontal parameters dx_left = std::max(dx_left, bmLongdivChar.width); // Update vertical parameters longdivAscent = bmBase.ascent + psi + mRuleThickness; longdivDescent = std::max(bmBase.descent, (bmLongdivChar.ascent + bmLongdivChar.descent - longdivAscent)); mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, longdivAscent); mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, longdivDescent); } } /////////////// // radical notation: if (IsToDraw(NOTATION_RADICAL)) { nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left; if (aWidthOnly) { nscoord radical_width = mMathMLChar[mRadicalCharIndex]. GetMaxWidth(PresContext(), aDrawTarget, fontSizeInflation); // Update horizontal parameters *dx_leading = std::max(*dx_leading, radical_width); } else { // Stretch the radical symbol to the appropriate height if it is not // big enough. nsBoundingMetrics contSize = bmBase; contSize.ascent = mRadicalRuleThickness; contSize.descent = bmBase.ascent + bmBase.descent + psi; // height(radical) should be >= height(base) + psi + mRadicalRuleThickness mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aDrawTarget, fontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL, contSize, bmRadicalChar, NS_STRETCH_LARGER, StyleVisibility()->mDirection); mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar); // Update horizontal parameters *dx_leading = std::max(*dx_leading, bmRadicalChar.width); // Update vertical parameters radicalAscent = bmBase.ascent + psi + mRadicalRuleThickness; radicalDescent = std::max(bmBase.descent, (bmRadicalChar.ascent + bmRadicalChar.descent - radicalAscent)); mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, radicalAscent); mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, radicalDescent); } } /////////////// // if (IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) || (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) { // center the menclose around the content (horizontally) dx_left = dx_right = std::max(dx_left, dx_right); } /////////////// // The maximum size is now computed: set the remaining parameters mBoundingMetrics.width = dx_left + bmBase.width + dx_right; mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing); mBoundingMetrics.rightBearing = std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing); aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent, baseSize.BlockStartAscent())); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + std::max(mBoundingMetrics.descent, baseSize.Height() - baseSize.BlockStartAscent()); if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { nscoord desiredSizeAscent = aDesiredSize.BlockStartAscent(); nscoord desiredSizeDescent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); if (IsToDraw(NOTATION_LONGDIV)) { desiredSizeAscent = std::max(desiredSizeAscent, longdivAscent + leading); desiredSizeDescent = std::max(desiredSizeDescent, longdivDescent + mRuleThickness); } if (IsToDraw(NOTATION_RADICAL)) { desiredSizeAscent = std::max(desiredSizeAscent, radicalAscent + leading); desiredSizeDescent = std::max(desiredSizeDescent, radicalDescent + mRadicalRuleThickness); } aDesiredSize.SetBlockStartAscent(desiredSizeAscent); aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent; } if (IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) || (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) { // center the menclose around the content (vertically) nscoord dy = std::max(aDesiredSize.BlockStartAscent() - bmBase.ascent, aDesiredSize.Height() - aDesiredSize.BlockStartAscent() - bmBase.descent); aDesiredSize.SetBlockStartAscent(bmBase.ascent + dy); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + bmBase.descent + dy; } // Update mBoundingMetrics ascent/descent if (IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) || IsToDraw(NOTATION_UPDIAGONALARROW) || IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX)) mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent(); if (IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) || IsToDraw(NOTATION_UPDIAGONALARROW) || IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX)) mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); // phasorangle notation: // move up from the bottom by the angled line height if (IsToDraw(NOTATION_PHASORANGLE)) mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, 2 * kPhasorangleWidth * mRuleThickness - mBoundingMetrics.descent); aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); if (aPlaceOrigin) { ////////////////// // Set position and size of MathMLChars if (IsToDraw(NOTATION_LONGDIV)) mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left - bmLongdivChar.width, aDesiredSize.BlockStartAscent() - longdivAscent, bmLongdivChar.width, bmLongdivChar.ascent + bmLongdivChar.descent)); if (IsToDraw(NOTATION_RADICAL)) { nscoord dx = (StyleVisibility()->mDirection ? dx_left + bmBase.width : dx_left - bmRadicalChar.width); mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx, aDesiredSize.BlockStartAscent() - radicalAscent, bmRadicalChar.width, bmRadicalChar.ascent + bmRadicalChar.descent)); } mContentWidth = bmBase.width; ////////////////// // Finish reflowing child frames PositionRowChildFrames(dx_left, aDesiredSize.BlockStartAscent()); } return NS_OK; }
void nsFirstLetterFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, const ReflowInput& aReflowInput, nsReflowStatus& aReflowStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aReflowStatus); // Grab overflow list DrainOverflowFrames(aPresContext); nsIFrame* kid = mFrames.FirstChild(); // Setup reflow state for our child WritingMode wm = aReflowInput.GetWritingMode(); LogicalSize availSize = aReflowInput.AvailableSize(); const LogicalMargin& bp = aReflowInput.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(); ReflowOutput kidMetrics(lineWM); // Reflow the child if (!aReflowInput.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 = WritingModeForLine(wm, kid); LogicalSize kidAvailSize = availSize.ConvertTo(kidWritingMode, wm); ReflowInput rs(aPresContext, aReflowInput, kid, kidAvailSize); nsLineLayout ll(aPresContext, nullptr, &aReflowInput, nullptr, nullptr); ll.BeginLineReflow(bp.IStart(wm), bp.BStart(wm), availSize.ISize(wm), NS_UNCONSTRAINEDSIZE, false, true, kidWritingMode, nsSize(aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight())); rs.mLineLayout = ≪ 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 = aReflowInput.mLineLayout; bool pushedFrame; ll->SetInFirstLetter( mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter); ll->BeginSpan(this, &aReflowInput, 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); if (mStyleContext->StyleTextReset()->mInitialLetterSize != 0.0f) { aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() + bp.BStart(wm)); aMetrics.BSize(lineWM) = kidMetrics.BSize(lineWM) + bp.BStartEnd(wm); } else { 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 (aReflowInput.mLineLayout) { aReflowInput.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, aReflowInput, aMetrics); }
void BRFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("BRFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); WritingMode wm = aReflowInput.GetWritingMode(); LogicalSize finalSize(wm); finalSize.BSize(wm) = 0; // BR frames with block size 0 are ignored in quirks // mode by nsLineLayout::VerticalAlignFrames . // However, it's not always 0. See below. finalSize.ISize(wm) = 0; aMetrics.SetBlockStartAscent(0); // Only when the BR is operating in a line-layout situation will it // behave like a BR. Additionally, we suppress breaks from BR inside // of ruby frames. To determine if we're inside ruby, we have to rely // on the *parent's* ShouldSuppressLineBreak() method, instead of our // own, because we may have custom "display" value that makes our // ShouldSuppressLineBreak() return false. nsLineLayout* ll = aReflowInput.mLineLayout; if (ll && !GetParent()->StyleContext()->ShouldSuppressLineBreak()) { // Note that the compatibility mode check excludes AlmostStandards // mode, since this is the inline box model. See bug 161691. if ( ll->LineIsEmpty() || aPresContext->CompatibilityMode() == eCompatibility_FullStandards ) { // The line is logically empty; any whitespace is trimmed away. // // If this frame is going to terminate the line we know // that nothing else will go on the line. Therefore, in this // case, we provide some height for the BR frame so that it // creates some vertical whitespace. It's necessary to use the // line-height rather than the font size because the // quirks-mode fix that doesn't apply the block's min // line-height makes this necessary to make BR cause a line // of the full line-height // We also do this in strict mode because BR should act like a // normal inline frame. That line-height is used is important // here for cases where the line-height is less than 1. RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetInflatedFontMetricsForFrame(this); if (fm) { nscoord logicalHeight = aReflowInput.CalcLineHeight(); finalSize.BSize(wm) = logicalHeight; aMetrics.SetBlockStartAscent(nsLayoutUtils::GetCenteredFontBaseline( fm, logicalHeight, wm.IsLineInverted())); } else { aMetrics.SetBlockStartAscent(aMetrics.BSize(wm) = 0); } // XXX temporary until I figure out a better solution; see the // code in nsLineLayout::VerticalAlignFrames that zaps minY/maxY // if the width is zero. // XXX This also fixes bug 10036! // Warning: nsTextControlFrame::CalculateSizeStandard depends on // the following line, see bug 228752. // The code below in AddInlinePrefISize also adds 1 appunit to width finalSize.ISize(wm) = 1; } // Return our reflow status uint32_t breakType = aReflowInput.mStyleDisplay->PhysicalBreakType(wm); if (NS_STYLE_CLEAR_NONE == breakType) { breakType = NS_STYLE_CLEAR_LINE; } aStatus = NS_INLINE_BREAK | NS_INLINE_BREAK_AFTER | NS_INLINE_MAKE_BREAK_TYPE(breakType); ll->SetLineEndsInBR(true); } else { aStatus = NS_FRAME_COMPLETE; } aMetrics.SetSize(wm, finalSize); aMetrics.SetOverflowAreasToDesiredBounds(); mAscent = aMetrics.BlockStartAscent(); NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics); }
void nsNumberControlFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsNumberControlFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); NS_ASSERTION(mOuterWrapper, "Outer wrapper div must exist!"); NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(), "nsNumberControlFrame should not have continuations; if it does we " "need to call RegUnregAccessKey only for the first"); NS_ASSERTION(!mFrames.FirstChild() || !mFrames.FirstChild()->GetNextSibling(), "We expect at most one direct child frame"); if (mState & NS_FRAME_FIRST_REFLOW) { nsFormControlFrame::RegUnRegAccessKey(this, true); } const WritingMode myWM = aReflowInput.GetWritingMode(); // The ISize of our content box, which is the available ISize // for our anonymous content: const nscoord contentBoxISize = aReflowInput.ComputedISize(); nscoord contentBoxBSize = aReflowInput.ComputedBSize(); // Figure out our border-box sizes as well (by adding borderPadding to // content-box sizes): const nscoord borderBoxISize = contentBoxISize + aReflowInput.ComputedLogicalBorderPadding().IStartEnd(myWM); nscoord borderBoxBSize; if (contentBoxBSize != NS_INTRINSICSIZE) { borderBoxBSize = contentBoxBSize + aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM); } // else, we'll figure out borderBoxBSize after we resolve contentBoxBSize. nsIFrame* outerWrapperFrame = mOuterWrapper->GetPrimaryFrame(); if (!outerWrapperFrame) { // display:none? if (contentBoxBSize == NS_INTRINSICSIZE) { contentBoxBSize = 0; borderBoxBSize = aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM); } } else { NS_ASSERTION(outerWrapperFrame == mFrames.FirstChild(), "huh?"); ReflowOutput wrappersDesiredSize(aReflowInput); WritingMode wrapperWM = outerWrapperFrame->GetWritingMode(); LogicalSize availSize = aReflowInput.ComputedSize(wrapperWM); availSize.BSize(wrapperWM) = NS_UNCONSTRAINEDSIZE; ReflowInput wrapperReflowInput(aPresContext, aReflowInput, outerWrapperFrame, availSize); // Convert wrapper margin into my own writing-mode (in case it differs): LogicalMargin wrapperMargin = wrapperReflowInput.ComputedLogicalMargin().ConvertTo(myWM, wrapperWM); // offsets of wrapper frame within this frame: LogicalPoint wrapperOffset(myWM, aReflowInput.ComputedLogicalBorderPadding().IStart(myWM) + wrapperMargin.IStart(myWM), aReflowInput.ComputedLogicalBorderPadding().BStart(myWM) + wrapperMargin.BStart(myWM)); nsReflowStatus childStatus; // We initially reflow the child with a dummy containerSize; positioning // will be fixed later. const nsSize dummyContainerSize; ReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, wrapperReflowInput, myWM, wrapperOffset, dummyContainerSize, 0, childStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus), "We gave our child unconstrained available block-size, " "so it should be complete"); nscoord wrappersMarginBoxBSize = wrappersDesiredSize.BSize(myWM) + wrapperMargin.BStartEnd(myWM); if (contentBoxBSize == NS_INTRINSICSIZE) { // We are intrinsically sized -- we should shrinkwrap the outer wrapper's // block-size: contentBoxBSize = wrappersMarginBoxBSize; // Make sure we obey min/max-bsize in the case when we're doing intrinsic // sizing (we get it for free when we have a non-intrinsic // aReflowInput.ComputedBSize()). Note that we do this before // adjusting for borderpadding, since ComputedMaxBSize and // ComputedMinBSize are content heights. contentBoxBSize = NS_CSS_MINMAX(contentBoxBSize, aReflowInput.ComputedMinBSize(), aReflowInput.ComputedMaxBSize()); borderBoxBSize = contentBoxBSize + aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM); } // Center child in block axis nscoord extraSpace = contentBoxBSize - wrappersMarginBoxBSize; wrapperOffset.B(myWM) += std::max(0, extraSpace / 2); // Needed in FinishReflowChild, for logical-to-physical conversion: nsSize borderBoxSize = LogicalSize(myWM, borderBoxISize, borderBoxBSize). GetPhysicalSize(myWM); // Place the child FinishReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, &wrapperReflowInput, myWM, wrapperOffset, borderBoxSize, 0); nsSize contentBoxSize = LogicalSize(myWM, contentBoxISize, contentBoxBSize). GetPhysicalSize(myWM); aDesiredSize.SetBlockStartAscent( wrappersDesiredSize.BlockStartAscent() + outerWrapperFrame->BStart(aReflowInput.GetWritingMode(), contentBoxSize)); } LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize); aDesiredSize.SetSize(myWM, logicalDesiredSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); if (outerWrapperFrame) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, outerWrapperFrame); } FinishAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); }
void nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); nsReflowStatus childStatus; mPresentationData.flags &= ~NS_MATHML_ERROR; aDesiredSize.ClearSize(); aDesiredSize.SetBlockStartAscent(0); nsBoundingMetrics bmSqr, bmBase, bmIndex; DrawTarget* drawTarget = aReflowInput.mRenderingContext->GetDrawTarget(); ////////////////// // Reflow Children int32_t count = 0; nsIFrame* baseFrame = nullptr; nsIFrame* indexFrame = nullptr; ReflowOutput baseSize(aReflowInput); ReflowOutput indexSize(aReflowInput); nsIFrame* childFrame = mFrames.FirstChild(); while (childFrame) { // ask our children to compute their bounding metrics ReflowOutput childDesiredSize(aReflowInput, aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS); WritingMode wm = childFrame->GetWritingMode(); LogicalSize availSize = aReflowInput.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; ReflowInput childReflowInput(aPresContext, aReflowInput, childFrame, availSize); ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowInput, childStatus); //NS_ASSERTION(childStatus.IsComplete(), "bad status"); if (0 == count) { // base baseFrame = childFrame; baseSize = childDesiredSize; bmBase = childDesiredSize.mBoundingMetrics; } else if (1 == count) { // index indexFrame = childFrame; indexSize = childDesiredSize; bmIndex = childDesiredSize.mBoundingMetrics; } count++; childFrame = childFrame->GetNextSibling(); } if (2 != count) { // report an error, encourage people to get their markups in order ReportChildCountError(); ReflowError(drawTarget, aDesiredSize); aStatus.Reset(); NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); // Call DidReflow() for the child frames we successfully did reflow. DidReflowChildren(mFrames.FirstChild(), childFrame); return; } //////////// // Prepare the radical symbol and the overline bar float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation); nscoord ruleThickness, leading, psi; GetRadicalParameters(fm, StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK, ruleThickness, leading, psi); // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131) char16_t one = '1'; nsBoundingMetrics bmOne = nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, drawTarget); if (bmOne.ascent > bmBase.ascent) psi += bmOne.ascent - bmBase.ascent; // make sure that the rule appears on on screen nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); if (ruleThickness < onePixel) { ruleThickness = onePixel; } // adjust clearance psi to get an exact number of pixels -- this // gives a nicer & uniform look on stacked radicals (bug 130282) nscoord delta = psi % onePixel; if (delta) psi += onePixel - delta; // round up // Stretch the radical symbol to the appropriate height if it is not big enough. nsBoundingMetrics contSize = bmBase; contSize.descent = bmBase.ascent + bmBase.descent + psi; contSize.ascent = ruleThickness; // height(radical) should be >= height(base) + psi + ruleThickness nsBoundingMetrics radicalSize; mSqrChar.Stretch(aPresContext, drawTarget, fontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL, contSize, radicalSize, NS_STRETCH_LARGER, StyleVisibility()->mDirection); // radicalSize have changed at this point, and should match with // the bounding metrics of the char mSqrChar.GetBoundingMetrics(bmSqr); // Update the desired size for the container (like msqrt, index is not yet included) // the baseline will be that of the base. mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness; mBoundingMetrics.descent = std::max(bmBase.descent, (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent)); mBoundingMetrics.width = bmSqr.width + bmBase.width; mBoundingMetrics.leftBearing = bmSqr.leftBearing; mBoundingMetrics.rightBearing = bmSqr.width + std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + std::max(baseSize.Height() - baseSize.BlockStartAscent(), mBoundingMetrics.descent + ruleThickness); aDesiredSize.Width() = mBoundingMetrics.width; ///////////// // Re-adjust the desired size to include the index. // the index is raised by some fraction of the height // of the radical, see \mroot macro in App. B, TexBook float raiseIndexPercent = 0.6f; gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont(); if (mathFont) { raiseIndexPercent = mathFont->MathTable()-> Constant(gfxMathTable::RadicalDegreeBottomRaisePercent); } nscoord raiseIndexDelta = NSToCoordRound(raiseIndexPercent * (bmSqr.ascent + bmSqr.descent)); nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical - (bmSqr.ascent + bmSqr.descent) // to bottom of radical + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index nscoord indexClearance = 0; if (mBoundingMetrics.ascent < indexRaisedAscent) { indexClearance = indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index mBoundingMetrics.ascent = indexRaisedAscent; nscoord descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + descent; } nscoord dxIndex, dxSqr; GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr); mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width; mBoundingMetrics.leftBearing = std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing); mBoundingMetrics.rightBearing = dxSqr + bmSqr.width + std::max(bmBase.width, bmBase.rightBearing); aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; GatherAndStoreOverflow(&aDesiredSize); // place the index nscoord dx = dxIndex; nscoord dy = aDesiredSize.BlockStartAscent() - (indexRaisedAscent + indexSize.BlockStartAscent() - bmIndex.ascent); FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr, MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx), dy, 0); // place the radical symbol and the radical bar dx = dxSqr; dy = indexClearance + leading; // leave a leading at the top mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx), dy, bmSqr.width, bmSqr.ascent + bmSqr.descent)); dx += bmSqr.width; mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx), dy, bmBase.width, ruleThickness); // place the base dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent(); FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr, MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx), dy, 0); mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); aStatus.Reset(); NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); }
/* virtual */ nsresult nsMathMLmunderoverFrame::Place(DrawTarget* aDrawTarget, bool aPlaceOrigin, ReflowOutput& aDesiredSize) { float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) && StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) { //place like sub sup or subsup if (mContent->IsMathMLElement(nsGkAtoms::munderover_)) { return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aDrawTarget, aPlaceOrigin, aDesiredSize, this, 0, 0, fontSizeInflation); } else if (mContent->IsMathMLElement( nsGkAtoms::munder_)) { return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aDrawTarget, aPlaceOrigin, aDesiredSize, this, 0, 0, fontSizeInflation); } else { NS_ASSERTION(mContent->IsMathMLElement(nsGkAtoms::mover_), "mContent->NodeInfo()->NameAtom() not recognized"); return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aDrawTarget, aPlaceOrigin, aDesiredSize, this, 0, 0, fontSizeInflation); } } //////////////////////////////////// // Get the children's desired sizes nsBoundingMetrics bmBase, bmUnder, bmOver; ReflowOutput baseSize(aDesiredSize.GetWritingMode()); ReflowOutput underSize(aDesiredSize.GetWritingMode()); ReflowOutput 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 (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_, nsGkAtoms::munderover_)) { underFrame = baseFrame->GetNextSibling(); } else if (mContent->IsMathMLElement(nsGkAtoms::mover_)) { overFrame = baseFrame->GetNextSibling(); } } if (underFrame && mContent->IsMathMLElement(nsGkAtoms::munderover_)) { overFrame = underFrame->GetNextSibling(); } if (mContent->IsMathMLElement(nsGkAtoms::munder_)) { if (!baseFrame || !underFrame || underFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order haveError = true; } } if (mContent->IsMathMLElement(nsGkAtoms::mover_)) { if (!baseFrame || !overFrame || overFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order haveError = true; } } if (mContent->IsMathMLElement(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(aDrawTarget, aDesiredSize); } GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); if (underFrame) { GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder); } if (overFrame) { GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver); } nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); //////////////////// // Place Children RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation); nscoord xHeight = fm->XHeight(); nscoord oneDevPixel = fm->AppUnitsPerDevPixel(); gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont(); nscoord ruleThickness; GetRuleThickness (aDrawTarget, 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; }
nsresult nsMathMLmfracFrame::PlaceInternal(DrawTarget* aDrawTarget, bool aPlaceOrigin, ReflowOutput& aDesiredSize, bool aWidthOnly) { //////////////////////////////////// // Get the children's desired sizes nsBoundingMetrics bmNum, bmDen; ReflowOutput sizeNum(aDesiredSize.GetWritingMode()); ReflowOutput 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(aDrawTarget, aDesiredSize); } GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum); GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen); nsPresContext* presContext = PresContext(); nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation); nscoord defaultRuleThickness, axisHeight; nscoord oneDevPixel = fm->AppUnitsPerDevPixel(); gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont(); if (mathFont) { defaultRuleThickness = mathFont->GetMathConstant(gfxFontEntry::FractionRuleThickness, oneDevPixel); } else { GetRuleThickness(aDrawTarget, fm, defaultRuleThickness); } GetAxisHeight(aDrawTarget, 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); } // Factor in axis height // http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2 numShift += axisHeight; denShift += axisHeight; 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; }
void nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext, ReflowOutput& aButtonDesiredSize, const ReflowInput& aButtonReflowInput, nsIFrame* aFirstKid) { WritingMode wm = GetWritingMode(); LogicalSize availSize = aButtonReflowInput.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(aButtonReflowInput.mRenderingContext) - aButtonReflowInput.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 = aButtonReflowInput.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. ReflowInput adjustedButtonReflowInput = CloneReflowInputWithReducedContentBox(aButtonReflowInput, focusPadding); ReflowInput contentsReflowInput(aPresContext, adjustedButtonReflowInput, aFirstKid, availSize); nsReflowStatus contentsReflowStatus; ReflowOutput contentsDesiredSize(aButtonReflowInput); 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, contentsReflowInput, 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 (aButtonReflowInput.ComputedBSize() != NS_INTRINSICSIZE) { // Button has a fixed block-size -- that's its content-box bSize. buttonContentBox.BSize(wm) = aButtonReflowInput.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 // aButtonReflowInput.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), aButtonReflowInput.ComputedMinBSize(), aButtonReflowInput.ComputedMaxBSize()); } if (aButtonReflowInput.ComputedISize() != NS_INTRINSICSIZE) { buttonContentBox.ISize(wm) = aButtonReflowInput.ComputedISize(); } else { buttonContentBox.ISize(wm) = contentsDesiredSize.ISize(wm) + focusPadding.IStartEnd(wm); buttonContentBox.ISize(wm) = NS_CSS_MINMAX(buttonContentBox.ISize(wm), aButtonReflowInput.ComputedMinISize(), aButtonReflowInput.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, &contentsReflowInput, wm, childPos, containerSize, 0); // Make sure we have a useful 'ascent' value for the child if (contentsDesiredSize.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) { WritingMode wm = aButtonReflowInput.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, aButtonReflowInput.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(); }