void nsRangeFrame::ReflowAnonymousContent(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState) { // The width/height of our content box, which is the available width/height // for our anonymous content: nscoord rangeFrameContentBoxWidth = aReflowState.ComputedWidth(); nscoord rangeFrameContentBoxHeight = aReflowState.ComputedHeight(); if (rangeFrameContentBoxHeight == NS_AUTOHEIGHT) { rangeFrameContentBoxHeight = 0; } nsIFrame* trackFrame = mTrackDiv->GetPrimaryFrame(); if (trackFrame) { // display:none? // Position the track: // The idea here is that we allow content authors to style the width, // height, border and padding of the track, but we ignore margin and // positioning properties and do the positioning ourself to keep the center // of the track's border box on the center of the nsRangeFrame's content // box. WritingMode wm = trackFrame->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState trackReflowState(aPresContext, aReflowState, trackFrame, availSize); // Find the x/y position of the track frame such that it will be positioned // as described above. These coordinates are with respect to the // nsRangeFrame's border-box. nscoord trackX = rangeFrameContentBoxWidth / 2; nscoord trackY = rangeFrameContentBoxHeight / 2; // Account for the track's border and padding (we ignore its margin): trackX -= trackReflowState.ComputedPhysicalBorderPadding().left + trackReflowState.ComputedWidth() / 2; trackY -= trackReflowState.ComputedPhysicalBorderPadding().top + trackReflowState.ComputedHeight() / 2; // Make relative to our border box instead of our content box: trackX += aReflowState.ComputedPhysicalBorderPadding().left; trackY += aReflowState.ComputedPhysicalBorderPadding().top; nsReflowStatus frameStatus; nsHTMLReflowMetrics trackDesiredSize(aReflowState); ReflowChild(trackFrame, aPresContext, trackDesiredSize, trackReflowState, trackX, trackY, 0, frameStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(frameStatus), "We gave our child unconstrained height, so it should be complete"); FinishReflowChild(trackFrame, aPresContext, trackDesiredSize, &trackReflowState, trackX, trackY, 0); } nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); if (thumbFrame) { // display:none? WritingMode wm = thumbFrame->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState thumbReflowState(aPresContext, aReflowState, thumbFrame, availSize); // Where we position the thumb depends on its size, so we first reflow // the thumb at {0,0} to obtain its size, then position it afterwards. nsReflowStatus frameStatus; nsHTMLReflowMetrics thumbDesiredSize(aReflowState); ReflowChild(thumbFrame, aPresContext, thumbDesiredSize, thumbReflowState, 0, 0, 0, frameStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(frameStatus), "We gave our child unconstrained height, so it should be complete"); FinishReflowChild(thumbFrame, aPresContext, thumbDesiredSize, &thumbReflowState, 0, 0, 0); DoUpdateThumbPosition(thumbFrame, nsSize(aDesiredSize.Width(), aDesiredSize.Height())); } nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame(); if (rangeProgressFrame) { // display:none? WritingMode wm = rangeProgressFrame->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState progressReflowState(aPresContext, aReflowState, rangeProgressFrame, availSize); // We first reflow the range-progress frame at {0,0} to obtain its // unadjusted dimensions, then we adjust it to so that the appropriate edge // ends at the thumb. nsReflowStatus frameStatus; nsHTMLReflowMetrics progressDesiredSize(aReflowState); ReflowChild(rangeProgressFrame, aPresContext, progressDesiredSize, progressReflowState, 0, 0, 0, frameStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(frameStatus), "We gave our child unconstrained height, so it should be complete"); FinishReflowChild(rangeProgressFrame, aPresContext, progressDesiredSize, &progressReflowState, 0, 0, 0); DoUpdateRangeProgressFrame(rangeProgressFrame, nsSize(aDesiredSize.Width(), aDesiredSize.Height())); } }
nsresult nsInlineFrame::ReflowFrames(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, InlineReflowState& irs, nsHTMLReflowMetrics& aMetrics, nsReflowStatus& aStatus) { nsresult rv = NS_OK; aStatus = NS_FRAME_COMPLETE; nsLineLayout* lineLayout = aReflowState.mLineLayout; bool inFirstLine = aReflowState.mLineLayout->GetInFirstLine(); RestyleManager* restyleManager = aPresContext->RestyleManager(); bool ltr = (NS_STYLE_DIRECTION_LTR == aReflowState.mStyleVisibility->mDirection); nscoord leftEdge = 0; // Don't offset by our start borderpadding if we have a prev continuation or // if we're in a part of an {ib} split other than the first one. if (!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) { leftEdge = ltr ? aReflowState.ComputedPhysicalBorderPadding().left : aReflowState.ComputedPhysicalBorderPadding().right; } nscoord availableWidth = aReflowState.AvailableWidth(); NS_ASSERTION(availableWidth != NS_UNCONSTRAINEDSIZE, "should no longer use available widths"); // Subtract off left and right border+padding from availableWidth availableWidth -= leftEdge; availableWidth -= ltr ? aReflowState.ComputedPhysicalBorderPadding().right : aReflowState.ComputedPhysicalBorderPadding().left; lineLayout->BeginSpan(this, &aReflowState, leftEdge, leftEdge + availableWidth, &mBaseline); // First reflow our principal children. nsIFrame* frame = mFrames.FirstChild(); bool done = false; while (frame) { // Check if we should lazily set the child frame's parent pointer. if (irs.mSetParentPointer) { bool havePrevBlock = irs.mLineContainer && irs.mLineContainer->GetPrevContinuation(); nsIFrame* child = frame; do { // If our block is the first in flow, then any floats under the pulled // frame must already belong to our block. if (havePrevBlock) { // This has to happen before we update frame's parent; we need to // know frame's ancestry under its old block. // The blockChildren.ContainsFrame check performed by // ReparentFloatsForInlineChild here may be slow, but we can't // easily avoid it because we don't know where 'frame' originally // came from. If we really really have to optimize this we could // cache whether frame->GetParent() is under its containing blocks // overflowList or not. ReparentFloatsForInlineChild(irs.mLineContainer, child, false); } child->SetParent(this); if (inFirstLine) { restyleManager->ReparentStyleContext(child); } // We also need to do the same for |frame|'s next-in-flows that are in // the sibling list. Otherwise, if we reflow |frame| and it's complete // we'll crash when trying to delete its next-in-flow. // This scenario doesn't happen often, but it can happen. nsIFrame* nextSibling = child->GetNextSibling(); child = child->GetNextInFlow(); if (MOZ_UNLIKELY(child)) { while (child != nextSibling && nextSibling) { nextSibling = nextSibling->GetNextSibling(); } if (!nextSibling) { child = nullptr; } } MOZ_ASSERT(!child || mFrames.ContainsFrame(child)); } while (child); // Fix the parent pointer for ::first-letter child frame next-in-flows, // so nsFirstLetterFrame::Reflow can destroy them safely (bug 401042). nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(frame); if (realFrame->GetType() == nsGkAtoms::letterFrame) { nsIFrame* child = realFrame->GetFirstPrincipalChild(); if (child) { NS_ASSERTION(child->GetType() == nsGkAtoms::textFrame, "unexpected frame type"); nsIFrame* nextInFlow = child->GetNextInFlow(); for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) { NS_ASSERTION(nextInFlow->GetType() == nsGkAtoms::textFrame, "unexpected frame type"); if (mFrames.ContainsFrame(nextInFlow)) { nextInFlow->SetParent(this); if (inFirstLine) { restyleManager->ReparentStyleContext(nextInFlow); } } else { #ifdef DEBUG // Once we find a next-in-flow that isn't ours none of the // remaining next-in-flows should be either. for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) { NS_ASSERTION(!mFrames.ContainsFrame(nextInFlow), "unexpected letter frame flow"); } #endif break; } } } } } MOZ_ASSERT(frame->GetParent() == this); if (!done) { bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK(); rv = ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus); done = NS_FAILED(rv) || NS_INLINE_IS_BREAK(aStatus) || (!reflowingFirstLetter && NS_FRAME_IS_NOT_COMPLETE(aStatus)); if (done) { if (!irs.mSetParentPointer) { break; } // Keep reparenting the remaining siblings, but don't reflow them. nsFrameList* pushedFrames = GetOverflowFrames(); if (pushedFrames && pushedFrames->FirstChild() == frame) { // Don't bother if |frame| was pushed to our overflow list. break; } } else { irs.mPrevFrame = frame; } } frame = frame->GetNextSibling(); } // Attempt to pull frames from our next-in-flow until we can't if (!done && GetNextInFlow()) { while (true) { bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK(); bool isComplete; if (!frame) { // Could be non-null if we pulled a first-letter frame and // it created a continuation, since we don't push those. frame = PullOneFrame(aPresContext, irs, &isComplete); } #ifdef NOISY_PUSHING printf("%p pulled up %p\n", this, frame); #endif if (nullptr == frame) { if (!isComplete) { aStatus = NS_FRAME_NOT_COMPLETE; } break; } rv = ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus); if (NS_FAILED(rv) || NS_INLINE_IS_BREAK(aStatus) || (!reflowingFirstLetter && NS_FRAME_IS_NOT_COMPLETE(aStatus))) { break; } irs.mPrevFrame = frame; frame = frame->GetNextSibling(); } } NS_ASSERTION(!NS_FRAME_IS_COMPLETE(aStatus) || !GetOverflowFrames(), "We can't be complete AND have overflow frames!"); // If after reflowing our children they take up no area then make // sure that we don't either. // // Note: CSS demands that empty inline elements still affect the // line-height calculations. However, continuations of an inline // that are empty we force to empty so that things like collapsed // whitespace in an inline element don't affect the line-height. aMetrics.Width() = lineLayout->EndSpan(this); // Compute final width. // Make sure to not include our start border and padding if we have a prev // continuation or if we're in a part of an {ib} split other than the first // one. if (!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) { aMetrics.Width() += ltr ? aReflowState.ComputedPhysicalBorderPadding().left : aReflowState.ComputedPhysicalBorderPadding().right; } /* * We want to only apply the end border and padding if we're the last * continuation and either not in an {ib} split or the last part of it. To * be the last continuation we have to be complete (so that we won't get a * next-in-flow) and have no non-fluid continuations on our continuation * chain. */ if (NS_FRAME_IS_COMPLETE(aStatus) && !LastInFlow()->GetNextContinuation() && !FrameIsNonLastInIBSplit()) { aMetrics.Width() += ltr ? aReflowState.ComputedPhysicalBorderPadding().right : aReflowState.ComputedPhysicalBorderPadding().left; } nsRefPtr<nsFontMetrics> fm; float inflation = nsLayoutUtils::FontSizeInflationFor(this); nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), inflation); aReflowState.rendContext->SetFont(fm); if (fm) { // Compute final height of the frame. // // Do things the standard css2 way -- though it's hard to find it // in the css2 spec! It's actually found in the css1 spec section // 4.4 (you will have to read between the lines to really see // it). // // The height of our box is the sum of our font size plus the top // and bottom border and padding. The height of children do not // affect our height. aMetrics.SetTopAscent(fm->MaxAscent()); aMetrics.Height() = fm->MaxHeight(); } else { NS_WARNING("Cannot get font metrics - defaulting sizes to 0"); aMetrics.SetTopAscent(aMetrics.Height() = 0); } aMetrics.SetTopAscent(aMetrics.TopAscent() + aReflowState.ComputedPhysicalBorderPadding().top); aMetrics.Height() += aReflowState.ComputedPhysicalBorderPadding().top + aReflowState.ComputedPhysicalBorderPadding().bottom; // For now our overflow area is zero. The real value will be // computed in |nsLineLayout::RelativePositionFrames|. aMetrics.mOverflowAreas.Clear(); #ifdef NOISY_FINAL_SIZE ListTag(stdout); printf(": metrics=%d,%d ascent=%d\n", aMetrics.Width(), aMetrics.Height(), aMetrics.TopAscent()); #endif return rv; }
void nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsSVGOuterSVGFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsSVGOuterSVGFrame::Reflow: availSize=%d,%d", aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; aDesiredSize.Width() = aReflowState.ComputedWidth() + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); aDesiredSize.Height() = aReflowState.ComputedHeight() + aReflowState.ComputedPhysicalBorderPadding().TopBottom(); NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages."); SVGSVGElement *svgElem = static_cast<SVGSVGElement*>(mContent); nsSVGOuterSVGAnonChildFrame *anonKid = static_cast<nsSVGOuterSVGAnonChildFrame*>(GetFirstPrincipalChild()); if (mState & NS_FRAME_FIRST_REFLOW) { // Initialize svgElem->UpdateHasChildrenOnlyTransform(); } // If our SVG viewport has changed, update our content and notify. // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace svgFloatSize newViewportSize( nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedWidth()), nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedHeight())); svgFloatSize oldViewportSize = svgElem->GetViewportSize(); uint32_t changeBits = 0; if (newViewportSize != oldViewportSize) { // When our viewport size changes, we may need to update the overflow rects // of our child frames. This is the case if: // // * We have a real/synthetic viewBox (a children-only transform), since // the viewBox transform will change as the viewport dimensions change. // // * We do not have a real/synthetic viewBox, but the last time we // reflowed (or the last time UpdateOverflow() was called) we did. // // We only handle the former case here, in which case we mark all our child // frames as dirty so that we reflow them below and update their overflow // rects. // // In the latter case, updating of overflow rects is handled for removal of // real viewBox (the viewBox attribute) in AttributeChanged. Synthetic // viewBox "removal" (e.g. a document references the same SVG via both an // <svg:image> and then as a CSS background image (a synthetic viewBox is // used when painting the former, but not when painting the latter)) is // handled in SVGSVGElement::FlushImageTransformInvalidation. // if (svgElem->HasViewBoxOrSyntheticViewBox()) { nsIFrame* anonChild = GetFirstPrincipalChild(); anonChild->AddStateBits(NS_FRAME_IS_DIRTY); for (nsIFrame* child = anonChild->GetFirstPrincipalChild(); child; child = child->GetNextSibling()) { child->AddStateBits(NS_FRAME_IS_DIRTY); } } changeBits |= COORD_CONTEXT_CHANGED; svgElem->SetViewportSize(newViewportSize); } if (mFullZoom != PresContext()->GetFullZoom()) { changeBits |= FULL_ZOOM_CHANGED; mFullZoom = PresContext()->GetFullZoom(); } if (changeBits) { NotifyViewportOrTransformChanged(changeBits); } mViewportInitialized = true; // Now that we've marked the necessary children as dirty, call // ReflowSVG() or ReflowSVGNonDisplayText() on them, depending // on whether we are non-display. mCallingReflowSVG = true; if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) { ReflowSVGNonDisplayText(this); } else { // Update the mRects and visual overflow rects of all our descendants, // including our anonymous wrapper kid: anonKid->AddStateBits(mState & NS_FRAME_IS_DIRTY); anonKid->ReflowSVG(); NS_ABORT_IF_FALSE(!anonKid->GetNextSibling(), "We should have one anonymous child frame wrapping our real children"); } mCallingReflowSVG = false; // Set our anonymous kid's offset from our border box: anonKid->SetPosition(GetContentRectRelativeToSelf().TopLeft()); // Including our size in our overflow rects regardless of the value of // 'background', 'border', etc. makes sure that we usually (when we clip to // our content area) don't have to keep changing our overflow rects as our // descendants move about (see perf comment below). Including our size in our // scrollable overflow rect also makes sure that we scroll if we're too big // for our viewport. // // <svg> never allows scrolling to anything outside its mRect (only panning), // so we must always keep our scrollable overflow set to our size. // // With regards to visual overflow, we always clip root-<svg> (see our // BuildDisplayList method) regardless of the value of the 'overflow' // property since that is per-spec, even for the initial 'visible' value. For // that reason there's no point in adding descendant visual overflow to our // own when this frame is for a root-<svg>. That said, there's also a very // good performance reason for us wanting to avoid doing so. If we did, then // the frame's overflow would often change as descendants that are partially // or fully outside its rect moved (think animation on/off screen), and that // would cause us to do a full NS_FRAME_IS_DIRTY reflow and repaint of the // entire document tree each such move (see bug 875175). // // So it's only non-root outer-<svg> that has the visual overflow of its // descendants added to its own. (Note that the default user-agent style // sheet makes 'hidden' the default value for :not(root(svg)), so usually // FinishAndStoreOverflow will still clip this back to the frame's rect.) // // WARNING!! Keep UpdateBounds below in sync with whatever we do for our // overflow rects here! (Again, see bug 875175.) // aDesiredSize.SetOverflowAreasToDesiredBounds(); if (!mIsRootContent) { aDesiredSize.mOverflowAreas.VisualOverflow().UnionRect( aDesiredSize.mOverflowAreas.VisualOverflow(), anonKid->GetVisualOverflowRect() + anonKid->GetPosition()); } FinishAndStoreOverflow(&aDesiredSize); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsSVGOuterSVGFrame::Reflow: size=%d,%d", aDesiredSize.Width(), aDesiredSize.Height())); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
/* virtual */ nsresult nsMathMLmpaddedFrame::Place(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { nsresult rv = nsMathMLContainerFrame::Place(aRenderingContext, false, aDesiredSize); if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { DidReflowChildren(GetFirstPrincipalChild()); return rv; } nscoord height = aDesiredSize.TopAscent(); nscoord depth = aDesiredSize.Height() - aDesiredSize.TopAscent(); // 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; // update width pseudoUnit = (mWidthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF) ? NS_MATHML_PSEUDO_UNIT_WIDTH : mWidthPseudoUnit; UpdateValue(mWidthSign, pseudoUnit, mWidth, aDesiredSize, width); 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); 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); depth = std::max(0, depth); // update lspace if (mLeadingSpacePseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) { pseudoUnit = mLeadingSpacePseudoUnit; UpdateValue(mLeadingSpaceSign, pseudoUnit, mLeadingSpace, aDesiredSize, lspace); } // update voffset if (mVerticalOffsetPseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) { pseudoUnit = mVerticalOffsetPseudoUnit; UpdateValue(mVerticalOffsetSign, pseudoUnit, mVerticalOffset, aDesiredSize, voffset); } // 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.SetTopAscent(height); aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.Height() = depth + aDesiredSize.TopAscent(); mBoundingMetrics.ascent = height; mBoundingMetrics.descent = depth; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.TopAscent(); if (aPlaceOrigin) { // Finish reflowing child frames, positioning their origins. PositionRowChildFrames(dx, aDesiredSize.TopAscent() - voffset); } return NS_OK; }
/* virtual */ nsresult nsMathMLmencloseFrame::PlaceInternal(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize, bool aWidthOnly) { /////////////// // Measure the size of our content using the base class to format like an // inferred mrow. nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode()); nsresult rv = nsMathMLContainerFrame::Place(aRenderingContext, false, baseSize); if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { DidReflowChildren(GetFirstPrincipalChild()); return rv; } nsBoundingMetrics bmBase = baseSize.mBoundingMetrics; nscoord dx_left = 0, dx_right = 0; nsBoundingMetrics bmLongdivChar, bmRadicalChar; nscoord radicalAscent = 0, radicalDescent = 0; nscoord longdivAscent = 0, longdivDescent = 0; nscoord psi = 0; nscoord leading = 0; /////////////// // Thickness of bars and font metrics nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), fontSizeInflation); GetRuleThickness(aRenderingContext, fm, mRuleThickness); if (mRuleThickness < onePixel) { mRuleThickness = onePixel; } char16_t one = '1'; nsBoundingMetrics bmOne = nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, aRenderingContext); /////////////// // General rules: the menclose element takes the size of the enclosed content. // We add a padding when needed. // determine padding & psi nscoord padding = 3 * mRuleThickness; nscoord delta = padding % onePixel; if (delta) padding += onePixel - delta; // round up if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { GetRadicalParameters(fm, StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK, mRadicalRuleThickness, leading, psi); // make sure that the rule appears on on screen if (mRadicalRuleThickness < onePixel) { mRadicalRuleThickness = onePixel; } // adjust clearance psi to get an exact number of pixels -- this // gives a nicer & uniform look on stacked radicals (bug 130282) delta = psi % onePixel; if (delta) { psi += onePixel - delta; // round up } } // Set horizontal parameters if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_CIRCLE)) dx_left = padding; if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_CIRCLE)) dx_right = padding; // Set vertical parameters if (IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) || IsToDraw(NOTATION_UPDIAGONALARROW) || IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_RADICAL) || IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_PHASORANGLE)) { // set a minimal value for the base height bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent); bmBase.descent = std::max(0, bmBase.descent); } mBoundingMetrics.ascent = bmBase.ascent; mBoundingMetrics.descent = bmBase.descent; if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_CIRCLE)) mBoundingMetrics.ascent += padding; if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_CIRCLE)) mBoundingMetrics.descent += padding; /////////////// // phasorangle notation if (IsToDraw(NOTATION_PHASORANGLE)) { nscoord phasorangleWidth = kPhasorangleWidth * mRuleThickness; // Update horizontal parameters dx_left = std::max(dx_left, phasorangleWidth); } /////////////// // updiagonal arrow notation. We need enough space at the top right corner to // draw the arrow head. if (IsToDraw(NOTATION_UPDIAGONALARROW)) { // This is an estimate, see nsDisplayNotation::Paint for the exact head size nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness; // We want that the arrow shaft strikes the menclose content and that the // arrow head does not overlap with that content. Hence we add some space // on the right. We don't add space on the top but only ensure that the // ascent is large enough. dx_right = std::max(dx_right, arrowHeadSize); mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize); } /////////////// // circle notation: we don't want the ellipse to overlap the enclosed // content. Hence, we need to increase the size of the bounding box by a // factor of at least sqrt(2). if (IsToDraw(NOTATION_CIRCLE)) { double ratio = (sqrt(2.0) - 1.0) / 2.0; nscoord padding2; // Update horizontal parameters padding2 = ratio * bmBase.width; dx_left = std::max(dx_left, padding2); dx_right = std::max(dx_right, padding2); // Update vertical parameters padding2 = ratio * (bmBase.ascent + bmBase.descent); mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, bmBase.ascent + padding2); mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, bmBase.descent + padding2); } /////////////// // longdiv notation: if (IsToDraw(NOTATION_LONGDIV)) { if (aWidthOnly) { nscoord longdiv_width = mMathMLChar[mLongDivCharIndex]. GetMaxWidth(PresContext(), aRenderingContext, fontSizeInflation); // Update horizontal parameters dx_left = std::max(dx_left, longdiv_width); } else { // Stretch the parenthesis to the appropriate height if it is not // big enough. nsBoundingMetrics contSize = bmBase; contSize.ascent = mRuleThickness; contSize.descent = bmBase.ascent + bmBase.descent + psi; // height(longdiv) should be >= height(base) + psi + mRuleThickness mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aRenderingContext, fontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL, contSize, bmLongdivChar, NS_STRETCH_LARGER, false); mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar); // Update horizontal parameters dx_left = std::max(dx_left, bmLongdivChar.width); // Update vertical parameters longdivAscent = bmBase.ascent + psi + mRuleThickness; longdivDescent = std::max(bmBase.descent, (bmLongdivChar.ascent + bmLongdivChar.descent - longdivAscent)); mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, longdivAscent); mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, longdivDescent); } } /////////////// // radical notation: if (IsToDraw(NOTATION_RADICAL)) { nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left; if (aWidthOnly) { nscoord radical_width = mMathMLChar[mRadicalCharIndex]. GetMaxWidth(PresContext(), aRenderingContext, fontSizeInflation); // Update horizontal parameters *dx_leading = std::max(*dx_leading, radical_width); } else { // Stretch the radical symbol to the appropriate height if it is not // big enough. nsBoundingMetrics contSize = bmBase; contSize.ascent = mRadicalRuleThickness; contSize.descent = bmBase.ascent + bmBase.descent + psi; // height(radical) should be >= height(base) + psi + mRadicalRuleThickness mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aRenderingContext, fontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL, contSize, bmRadicalChar, NS_STRETCH_LARGER, StyleVisibility()->mDirection); mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar); // Update horizontal parameters *dx_leading = std::max(*dx_leading, bmRadicalChar.width); // Update vertical parameters radicalAscent = bmBase.ascent + psi + mRadicalRuleThickness; radicalDescent = std::max(bmBase.descent, (bmRadicalChar.ascent + bmRadicalChar.descent - radicalAscent)); mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, radicalAscent); mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, radicalDescent); } } /////////////// // if (IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) || (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) { // center the menclose around the content (horizontally) dx_left = dx_right = std::max(dx_left, dx_right); } /////////////// // The maximum size is now computed: set the remaining parameters mBoundingMetrics.width = dx_left + bmBase.width + dx_right; mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing); mBoundingMetrics.rightBearing = std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing); aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent, baseSize.BlockStartAscent())); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + std::max(mBoundingMetrics.descent, baseSize.Height() - baseSize.BlockStartAscent()); if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { nscoord desiredSizeAscent = aDesiredSize.BlockStartAscent(); nscoord desiredSizeDescent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); if (IsToDraw(NOTATION_LONGDIV)) { desiredSizeAscent = std::max(desiredSizeAscent, longdivAscent + leading); desiredSizeDescent = std::max(desiredSizeDescent, longdivDescent + mRuleThickness); } if (IsToDraw(NOTATION_RADICAL)) { desiredSizeAscent = std::max(desiredSizeAscent, radicalAscent + leading); desiredSizeDescent = std::max(desiredSizeDescent, radicalDescent + mRadicalRuleThickness); } aDesiredSize.SetBlockStartAscent(desiredSizeAscent); aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent; } if (IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) || (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) { // center the menclose around the content (vertically) nscoord dy = std::max(aDesiredSize.BlockStartAscent() - bmBase.ascent, aDesiredSize.Height() - aDesiredSize.BlockStartAscent() - bmBase.descent); aDesiredSize.SetBlockStartAscent(bmBase.ascent + dy); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + bmBase.descent + dy; } // Update mBoundingMetrics ascent/descent if (IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) || IsToDraw(NOTATION_UPDIAGONALARROW) || IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX)) mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent(); if (IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) || IsToDraw(NOTATION_UPDIAGONALARROW) || IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX)) mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); // phasorangle notation: // move up from the bottom by the angled line height if (IsToDraw(NOTATION_PHASORANGLE)) mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, 2 * kPhasorangleWidth * mRuleThickness - mBoundingMetrics.descent); aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); if (aPlaceOrigin) { ////////////////// // Set position and size of MathMLChars if (IsToDraw(NOTATION_LONGDIV)) mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left - bmLongdivChar.width, aDesiredSize.BlockStartAscent() - longdivAscent, bmLongdivChar.width, bmLongdivChar.ascent + bmLongdivChar.descent)); if (IsToDraw(NOTATION_RADICAL)) { nscoord dx = (StyleVisibility()->mDirection ? dx_left + bmBase.width : dx_left - bmRadicalChar.width); mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx, aDesiredSize.BlockStartAscent() - radicalAscent, bmRadicalChar.width, bmRadicalChar.ascent + bmRadicalChar.descent)); } mContentWidth = bmBase.width; ////////////////// // Finish reflowing child frames PositionRowChildFrames(dx_left, aDesiredSize.BlockStartAscent()); } return NS_OK; }
void nsMathMLmtableOuterFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { nsAutoString value; // we want to return a table that is anchored according to the align attribute nsTableOuterFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); NS_ASSERTION(aDesiredSize.Height() >= 0, "illegal height for mtable"); NS_ASSERTION(aDesiredSize.Width() >= 0, "illegal width for mtable"); // see if the user has set the align attribute on the <mtable> int32_t rowIndex = 0; eAlign tableAlign = eAlign_axis; mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, value); if (!value.IsEmpty()) { ParseAlignAttribute(value, tableAlign, rowIndex); } // adjustments if there is a specified row from where to anchor the table // (conceptually: when there is no row of reference, picture the table as if // it is wrapped in a single big fictional row at dy = 0, this way of // doing so allows us to have a single code path for all cases). nscoord dy = 0; WritingMode wm = aDesiredSize.GetWritingMode(); nscoord blockSize = aDesiredSize.BSize(wm); nsIFrame* rowFrame = nullptr; if (rowIndex) { rowFrame = GetRowFrameAt(rowIndex); if (rowFrame) { // translate the coordinates to be relative to us and in our writing mode nsIFrame* frame = rowFrame; LogicalRect rect(wm, frame->GetRect(), aReflowState.ComputedSizeAsContainerIfConstrained()); blockSize = rect.BSize(wm); do { dy += rect.BStart(wm); frame = frame->GetParent(); } while (frame != this); } } switch (tableAlign) { case eAlign_top: aDesiredSize.SetBlockStartAscent(dy); break; case eAlign_bottom: aDesiredSize.SetBlockStartAscent(dy + blockSize); break; case eAlign_center: aDesiredSize.SetBlockStartAscent(dy + blockSize / 2); break; case eAlign_baseline: if (rowFrame) { // anchor the table on the baseline of the row of reference nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent(); if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline' aDesiredSize.SetBlockStartAscent(dy + rowAscent); break; } } // in other situations, fallback to center aDesiredSize.SetBlockStartAscent(dy + blockSize / 2); break; case eAlign_axis: default: { // XXX should instead use style data from the row of reference here ? RefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), nsLayoutUtils:: FontSizeInflationFor(this)); nscoord axisHeight; GetAxisHeight(aReflowState.rendContext->GetDrawTarget(), fm, axisHeight); if (rowFrame) { // anchor the table on the axis of the row of reference // XXX fallback to baseline because it is a hard problem // XXX need to fetch the axis of the row; would need rowalign=axis to work better nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent(); if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline' aDesiredSize.SetBlockStartAscent(dy + rowAscent); break; } } // in other situations, fallback to using half of the height aDesiredSize.SetBlockStartAscent(dy + blockSize / 2 + axisHeight); } } mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); // just make-up a bounding metrics mBoundingMetrics = nsBoundingMetrics(); mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent(); mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); mBoundingMetrics.width = aDesiredSize.Width(); mBoundingMetrics.leftBearing = 0; mBoundingMetrics.rightBearing = aDesiredSize.Width(); aDesiredSize.mBoundingMetrics = mBoundingMetrics; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext, nsHTMLReflowMetrics& aButtonDesiredSize, const nsHTMLReflowState& aButtonReflowState, nsIFrame* aFirstKid) { // Buttons have some bonus renderer-determined border/padding, // which occupies part of the button's content-box area: const nsMargin focusPadding = mRenderer.GetAddedButtonBorderAndPadding(); nsSize availSize(aButtonReflowState.ComputedWidth(), NS_INTRINSICSIZE); // Indent the child inside us by the focus border. We must do this separate // from the regular border. availSize.width -= focusPadding.LeftRight(); // See whether out availSize's width is big enough. If it's smaller than our // intrinsic min width, that means that the kid wouldn't really fit; for a // better look in such cases we adjust the available width and our left // offset to allow the kid to spill left into our padding. nscoord xoffset = focusPadding.left + aButtonReflowState.ComputedPhysicalBorderPadding().left; nscoord extrawidth = GetMinWidth(aButtonReflowState.rendContext) - aButtonReflowState.ComputedWidth(); if (extrawidth > 0) { nscoord extraleft = extrawidth / 2; nscoord extraright = extrawidth - extraleft; NS_ASSERTION(extraright >=0, "How'd that happen?"); // Do not allow the extras to be bigger than the relevant padding extraleft = std::min(extraleft, aButtonReflowState.ComputedPhysicalPadding().left); extraright = std::min(extraright, aButtonReflowState.ComputedPhysicalPadding().right); xoffset -= extraleft; availSize.width += extraleft + extraright; } availSize.width = std::max(availSize.width,0); // Give child a clone of the button's reflow state, with height/width reduced // by focusPadding, so that descendants with height:100% don't protrude. nsHTMLReflowState adjustedButtonReflowState = CloneReflowStateWithReducedContentBox(aButtonReflowState, focusPadding); nsHTMLReflowState contentsReflowState(aPresContext, adjustedButtonReflowState, aFirstKid, availSize); nsReflowStatus contentsReflowStatus; nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState.GetWritingMode()); ReflowChild(aFirstKid, aPresContext, contentsDesiredSize, contentsReflowState, xoffset, focusPadding.top + aButtonReflowState.ComputedPhysicalBorderPadding().top, 0, contentsReflowStatus); MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus), "We gave button-contents frame unconstrained available height, " "so it should be complete"); // Compute the button's content-box height: nscoord buttonContentBoxHeight = 0; if (aButtonReflowState.ComputedHeight() != NS_INTRINSICSIZE) { // Button has a fixed height -- that's its content-box height. buttonContentBoxHeight = aButtonReflowState.ComputedHeight(); } else { // Button is intrinsically sized -- it should shrinkwrap the // button-contents' height, plus any focus-padding space: buttonContentBoxHeight = contentsDesiredSize.Height() + focusPadding.TopBottom(); // Make sure we obey min/max-height in the case when we're doing intrinsic // sizing (we get it for free when we have a non-intrinsic // aButtonReflowState.ComputedHeight()). Note that we do this before // adjusting for borderpadding, since mComputedMaxHeight and // mComputedMinHeight are content heights. buttonContentBoxHeight = NS_CSS_MINMAX(buttonContentBoxHeight, aButtonReflowState.ComputedMinHeight(), aButtonReflowState.ComputedMaxHeight()); } // Center child vertically in the button // (technically, inside of the button's focus-padding area) nscoord extraSpace = buttonContentBoxHeight - focusPadding.TopBottom() - contentsDesiredSize.Height(); nscoord yoffset = std::max(0, extraSpace / 2); // Adjust yoffset to be in terms of the button's frame-rect, instead of // its focus-padding rect: yoffset += focusPadding.top + aButtonReflowState.ComputedPhysicalBorderPadding().top; // Place the child FinishReflowChild(aFirstKid, aPresContext, &contentsReflowState, contentsDesiredSize, xoffset, yoffset, 0); // Make sure we have a useful 'ascent' value for the child if (contentsDesiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { contentsDesiredSize.SetTopAscent(aFirstKid->GetBaseline()); } // OK, we're done with the child frame. // Use what we learned to populate the button frame's reflow metrics. // * Button's height & width are content-box size + border-box contribution: aButtonDesiredSize.Width() = aButtonReflowState.ComputedWidth() + aButtonReflowState.ComputedPhysicalBorderPadding().LeftRight(); aButtonDesiredSize.Height() = buttonContentBoxHeight + aButtonReflowState.ComputedPhysicalBorderPadding().TopBottom(); // * Button's ascent is its child's ascent, plus the child's y-offset // within our frame: aButtonDesiredSize.SetTopAscent(contentsDesiredSize.TopAscent() + yoffset); aButtonDesiredSize.SetOverflowAreasToDesiredBounds(); }
void nsVideoFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { 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.FirstChild(); child; child = child->GetNextSibling()) { 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); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, imageFrame, availableSize, aMetrics.Width(), aMetrics.Height()); 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) { nsRefPtr<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); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, child, availableSize, aMetrics.Width(), aMetrics.Height()); 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); }
// NOTE: aDesiredStretchSize is an IN/OUT parameter // On input - it contains our current size // On output - the same size or the new size that we want NS_IMETHODIMP nsMathMLmoFrame::Stretch(nsRenderingContext& aRenderingContext, nsStretchDirection aStretchDirection, nsBoundingMetrics& aContainerSize, nsHTMLReflowMetrics& aDesiredStretchSize) { if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) { NS_WARNING("it is wrong to fire stretch more than once on a frame"); return NS_OK; } mPresentationData.flags |= NS_MATHML_STRETCH_DONE; nsIFrame* firstChild = mFrames.FirstChild(); // get the axis height; float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), fontSizeInflation); nscoord axisHeight, height; GetAxisHeight(aRenderingContext, fm, axisHeight); // get the leading to be left at the top and the bottom of the stretched char // this seems more reliable than using fm->GetLeading() on suspicious fonts nscoord em; GetEmHeight(fm, em); nscoord leading = NSToCoordRound(0.2f * em); // Operators that are stretchy, or those that are to be centered // to cater for fonts that are not math-aware, are handled by the MathMLChar // ('form' is reset if stretch fails -- i.e., we don't bother to stretch next time) bool useMathMLChar = UseMathMLChar(); nsBoundingMetrics charSize; nsBoundingMetrics container = aDesiredStretchSize.mBoundingMetrics; bool isVertical = false; if (((aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) || (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT)) && (mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL)) { isVertical = true; } uint32_t stretchHint = GetStretchHint(mFlags, mPresentationData, isVertical, StyleFont()); if (useMathMLChar) { nsBoundingMetrics initialSize = aDesiredStretchSize.mBoundingMetrics; if (stretchHint != NS_STRETCH_NONE) { container = aContainerSize; // some adjustments if the operator is symmetric and vertical if (isVertical && NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) { // we need to center about the axis nscoord delta = std::max(container.ascent - axisHeight, container.descent + axisHeight); container.ascent = delta + axisHeight; container.descent = delta - axisHeight; // get ready in case we encounter user-desired min-max size delta = std::max(initialSize.ascent - axisHeight, initialSize.descent + axisHeight); initialSize.ascent = delta + axisHeight; initialSize.descent = delta - axisHeight; } // check for user-desired min-max size if (mMaxSize != NS_MATHML_OPERATOR_SIZE_INFINITY && mMaxSize > 0.0f) { // if we are here, there is a user defined maxsize ... //XXX Set stretchHint = NS_STRETCH_NORMAL? to honor the maxsize as close as possible? if (NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags)) { // there is an explicit value like maxsize="20pt" // try to maintain the aspect ratio of the char float aspect = mMaxSize / float(initialSize.ascent + initialSize.descent); container.ascent = std::min(container.ascent, nscoord(initialSize.ascent * aspect)); container.descent = std::min(container.descent, nscoord(initialSize.descent * aspect)); // below we use a type cast instead of a conversion to avoid a VC++ bug // see http://support.microsoft.com/support/kb/articles/Q115/7/05.ASP container.width = std::min(container.width, (nscoord)mMaxSize); } else { // multiplicative value container.ascent = std::min(container.ascent, nscoord(initialSize.ascent * mMaxSize)); container.descent = std::min(container.descent, nscoord(initialSize.descent * mMaxSize)); container.width = std::min(container.width, nscoord(initialSize.width * mMaxSize)); } if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) { // re-adjust to align the char with the bottom of the initial container height = container.ascent + container.descent; container.descent = aContainerSize.descent; container.ascent = height - container.descent; } } if (mMinSize > 0.0f) { // if we are here, there is a user defined minsize ... // always allow the char to stretch in its natural direction, // even if it is different from the caller's direction if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT && aStretchDirection != mEmbellishData.direction) { aStretchDirection = NS_STRETCH_DIRECTION_DEFAULT; // but when we are not honoring the requested direction // we should not use the caller's container size either container = initialSize; } if (NS_MATHML_OPERATOR_MINSIZE_IS_ABSOLUTE(mFlags)) { // there is an explicit value like minsize="20pt" // try to maintain the aspect ratio of the char float aspect = mMinSize / float(initialSize.ascent + initialSize.descent); container.ascent = std::max(container.ascent, nscoord(initialSize.ascent * aspect)); container.descent = std::max(container.descent, nscoord(initialSize.descent * aspect)); container.width = std::max(container.width, (nscoord)mMinSize); } else { // multiplicative value container.ascent = std::max(container.ascent, nscoord(initialSize.ascent * mMinSize)); container.descent = std::max(container.descent, nscoord(initialSize.descent * mMinSize)); container.width = std::max(container.width, nscoord(initialSize.width * mMinSize)); } if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) { // re-adjust to align the char with the bottom of the initial container height = container.ascent + container.descent; container.descent = aContainerSize.descent; container.ascent = height - container.descent; } } } // let the MathMLChar stretch itself... nsresult res = mMathMLChar.Stretch(PresContext(), aRenderingContext, fontSizeInflation, aStretchDirection, container, charSize, stretchHint, StyleVisibility()->mDirection); if (NS_FAILED(res)) { // gracefully handle cases where stretching the char failed (i.e., GetBoundingMetrics failed) // clear our 'form' to behave as if the operator wasn't in the dictionary mFlags &= ~NS_MATHML_OPERATOR_FORM; useMathMLChar = false; } } // Place our children using the default method // This will allow our child text frame to get its DidReflow() nsresult rv = Place(aRenderingContext, true, aDesiredStretchSize); if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { // Make sure the child frames get their DidReflow() calls. DidReflowChildren(mFrames.FirstChild()); } if (useMathMLChar) { // update our bounding metrics... it becomes that of our MathML char mBoundingMetrics = charSize; // if the returned direction is 'unsupported', the char didn't actually change. // So we do the centering only if necessary if (mMathMLChar.GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) { bool largeopOnly = (NS_STRETCH_LARGEOP & stretchHint) != 0 && (NS_STRETCH_VARIABLE_MASK & stretchHint) == 0; if (isVertical || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) { // the desired size returned by mMathMLChar maybe different // from the size of the container. // the mMathMLChar.mRect.y calculation is subtle, watch out!!! height = mBoundingMetrics.ascent + mBoundingMetrics.descent; if (NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags) || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) { // For symmetric and vertical operators, or for operators that are always // centered ('+', '*', etc) we want to center about the axis of the container mBoundingMetrics.descent = height/2 - axisHeight; } else if (!largeopOnly) { // Align the center of the char with the center of the container mBoundingMetrics.descent = height/2 + (container.ascent + container.descent)/2 - container.ascent; } // else align the baselines mBoundingMetrics.ascent = height - mBoundingMetrics.descent; } } } // Fixup for the final height. // On one hand, our stretchy height can sometimes be shorter than surrounding // ASCII chars, e.g., arrow symbols have |mBoundingMetrics.ascent + leading| // that is smaller than the ASCII's ascent, hence when painting the background // later, it won't look uniform along the line. // On the other hand, sometimes we may leave too much gap when our glyph happens // to come from a font with tall glyphs. For example, since CMEX10 has very tall // glyphs, its natural font metrics are large, even if we pick a small glyph // whose size is comparable to the size of a normal ASCII glyph. // So to avoid uneven spacing in either of these two cases, we use the height // of the ASCII font as a reference and try to match it if possible. // special case for accents... keep them short to improve mouse operations... // an accent can only be the non-first child of <mover>, <munder>, <munderover> bool isAccent = NS_MATHML_EMBELLISH_IS_ACCENT(mEmbellishData.flags); if (isAccent) { nsEmbellishData parentData; GetEmbellishDataFrom(GetParent(), parentData); isAccent = (NS_MATHML_EMBELLISH_IS_ACCENTOVER(parentData.flags) || NS_MATHML_EMBELLISH_IS_ACCENTUNDER(parentData.flags)) && parentData.coreFrame != this; } if (isAccent && firstChild) { // see bug 188467 for what is going on here nscoord dy = aDesiredStretchSize.BlockStartAscent() - (mBoundingMetrics.ascent + leading); aDesiredStretchSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading); aDesiredStretchSize.Height() = aDesiredStretchSize.BlockStartAscent() + mBoundingMetrics.descent; firstChild->SetPosition(firstChild->GetPosition() - nsPoint(0, dy)); } else if (useMathMLChar) { nscoord ascent = fm->MaxAscent(); nscoord descent = fm->MaxDescent(); aDesiredStretchSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent + leading, ascent)); aDesiredStretchSize.Height() = aDesiredStretchSize.BlockStartAscent() + std::max(mBoundingMetrics.descent + leading, descent); } aDesiredStretchSize.Width() = mBoundingMetrics.width; aDesiredStretchSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredStretchSize.BlockStartAscent(); // Place our mMathMLChar, its origin is in our coordinate system if (useMathMLChar) { nscoord dy = aDesiredStretchSize.BlockStartAscent() - mBoundingMetrics.ascent; mMathMLChar.SetRect(nsRect(0, dy, charSize.width, charSize.ascent + charSize.descent)); } // Before we leave... there is a last item in the check-list: // If our parent is not embellished, it means we are the outermost embellished // container and so we put the spacing, otherwise we don't include the spacing, // the outermost embellished container will take care of it. if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) { // Account the spacing if we are not an accent with explicit attributes nscoord leadingSpace = mEmbellishData.leadingSpace; if (isAccent && !NS_MATHML_OPERATOR_HAS_LSPACE_ATTR(mFlags)) { leadingSpace = 0; } nscoord trailingSpace = mEmbellishData.trailingSpace; if (isAccent && !NS_MATHML_OPERATOR_HAS_RSPACE_ATTR(mFlags)) { trailingSpace = 0; } mBoundingMetrics.width += leadingSpace + trailingSpace; aDesiredStretchSize.Width() = mBoundingMetrics.width; aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width; nscoord dx = (StyleVisibility()->mDirection ? trailingSpace : leadingSpace); if (dx) { // adjust the offsets mBoundingMetrics.leftBearing += dx; mBoundingMetrics.rightBearing += dx; aDesiredStretchSize.mBoundingMetrics.leftBearing += dx; aDesiredStretchSize.mBoundingMetrics.rightBearing += dx; if (useMathMLChar) { nsRect rect; mMathMLChar.GetRect(rect); mMathMLChar.SetRect(nsRect(rect.x + dx, rect.y, rect.width, rect.height)); } else { nsIFrame* childFrame = firstChild; while (childFrame) { childFrame->SetPosition(childFrame->GetPosition() + nsPoint(dx, 0)); childFrame = childFrame->GetNextSibling(); } } } } // Finished with these: ClearSavedChildMetrics(); // Set our overflow area GatherAndStoreOverflow(&aDesiredStretchSize); // There used to be code here to change the height of the child frame to // change the caret height, but the text frame that manages the caret is now // not a direct child but wrapped in a block frame. See also bug 412033. return NS_OK; }
/* virtual */ nsresult nsMathMLmunderoverFrame::Place(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& 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(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, 0, 0, fontSizeInflation); } else if (mContent->IsMathMLElement( nsGkAtoms::munder_)) { return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, 0, 0, fontSizeInflation); } else { NS_ASSERTION(mContent->IsMathMLElement(nsGkAtoms::mover_), "mContent->NodeInfo()->NameAtom() not recognized"); return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, 0, 0, fontSizeInflation); } } //////////////////////////////////// // 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 (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(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), fontSizeInflation); 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; }
void nsSubDocumentFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d", aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); NS_ASSERTION(aReflowState.ComputedWidth() != NS_UNCONSTRAINEDSIZE, "Shouldn't have unconstrained stuff here " "thanks to the rules of reflow"); NS_ASSERTION(NS_INTRINSICSIZE != aReflowState.ComputedHeight(), "Shouldn't have unconstrained stuff here " "thanks to ComputeAutoSize"); aStatus = NS_FRAME_COMPLETE; NS_ASSERTION(mContent->GetPrimaryFrame() == this, "Shouldn't happen"); // XUL <iframe> or <browser>, or HTML <iframe>, <object> or <embed> aDesiredSize.SetSize(aReflowState.GetWritingMode(), aReflowState.ComputedSizeWithBorderPadding()); // "offset" is the offset of our content area from our frame's // top-left corner. nsPoint offset = nsPoint(aReflowState.ComputedPhysicalBorderPadding().left, aReflowState.ComputedPhysicalBorderPadding().top); if (mInnerView) { const nsMargin& bp = aReflowState.ComputedPhysicalBorderPadding(); nsSize innerSize(aDesiredSize.Width() - bp.LeftRight(), aDesiredSize.Height() - bp.TopBottom()); // Size & position the view according to 'object-fit' & 'object-position'. nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame(); IntrinsicSize intrinsSize; nsSize intrinsRatio; if (subDocRoot) { intrinsSize = subDocRoot->GetIntrinsicSize(); intrinsRatio = subDocRoot->GetIntrinsicRatio(); } nsRect destRect = nsLayoutUtils::ComputeObjectDestRect(nsRect(offset, innerSize), intrinsSize, intrinsRatio, StylePosition()); nsViewManager* vm = mInnerView->GetViewManager(); vm->MoveViewTo(mInnerView, destRect.x, destRect.y); vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), destRect.Size()), true); } aDesiredSize.SetOverflowAreasToDesiredBounds(); if (!ShouldClipSubdocument()) { nsIFrame* subdocRootFrame = GetSubdocumentRootFrame(); if (subdocRootFrame) { aDesiredSize.mOverflowAreas.UnionWith(subdocRootFrame->GetOverflowAreas() + offset); } } FinishAndStoreOverflow(&aDesiredSize); if (!aPresContext->IsPaginated() && !mPostedReflowCallback) { PresContext()->PresShell()->PostReflowCallback(this); mPostedReflowCallback = true; } NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%x", aDesiredSize.Width(), aDesiredSize.Height(), aStatus)); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
// 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, nscoord aScriptSpace) { nsIAtom* tag = aFrame->GetContent()->Tag(); // 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 (tag == nsGkAtoms::mover_) tag = nsGkAtoms::msup_; else if (tag == nsGkAtoms::munder_) tag = nsGkAtoms::msub_; else if (tag == 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(); nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(baseFrame, getter_AddRefs(fm)); aRenderingContext.SetFont(fm); nscoord xHeight = fm->XHeight(); nscoord ruleSize; GetRuleThickness (aRenderingContext, fm, ruleSize); // force the scriptSpace to be at least 1 pixel nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); aScriptSpace = std::max(onePixel, aScriptSpace); ///////////////////////////////////// // first the shift for the subscript // 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); nscoord subScriptShift; 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 // 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 nsPresentationData presentationData; aFrame->GetPresentationData(presentationData); nscoord supScriptShift; 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.SetTopAscent(-0x7FFFFFFF); multiSupSize.SetTopAscent(-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) { nsIAtom* childTag = childFrame->GetContent()->Tag(); if (childTag == nsGkAtoms::mprescripts_) { if (tag != nsGkAtoms::mmultiscripts_) { if (aPlaceOrigin) { aFrame->ReportInvalidChildError(childTag); } 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 (childTag == 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 ( childTag == nsGkAtoms::none) { foundNoneTag = true; } if (isSubScript) { // subscript subScriptFrame = childFrame; GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript); // get the subdrop from the subscript font GetSubDropFromChild (subScriptFrame, subDrop); // parameter v, Rule 18a, App. G, TeXbook minSubScriptShift = bmBase.descent + subDrop; trySubScriptShift = std::max(minSubScriptShift,subScriptShift); multiSubSize.SetTopAscent(std::max(multiSubSize.TopAscent(), subScriptSize.TopAscent())); 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.TopAscent()); if (bmSubScript.width) width = bmSubScript.width + aScriptSpace; rightBearing = bmSubScript.rightBearing; if (tag == nsGkAtoms::msub_) { boundingMetrics.rightBearing = boundingMetrics.width + rightBearing; boundingMetrics.width += width; // get min subscript shift limit from x-height // = h(x) - 4/5 * sigma_5, Rule 18b, App. G, TeXbook nscoord minShiftFromXHeight = (nscoord) (bmSubScript.ascent - (4.0f/5.0f) * xHeight); 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); // parameter u, Rule 18a, App. G, TeXbook minSupScriptShift = bmBase.ascent - supDrop; // get min supscript shift limit from x-height // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook minShiftFromXHeight = NSToCoordRound ((bmSupScript.descent + (1.0f/4.0f) * xHeight)); trySupScriptShift = std::max(minSupScriptShift, std::max(minShiftFromXHeight, supScriptShift)); multiSupSize.SetTopAscent(std::max(multiSupSize.TopAscent(), supScriptSize.TopAscent())); 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.TopAscent()); if (bmSupScript.width) width = std::max(width, bmSupScript.width + aScriptSpace); 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 gap = (trySupScriptShift - bmSupScript.descent) - (bmSubScript.ascent - trySubScriptShift); if (gap < 4.0f * ruleSize) { // adjust trySubScriptShift to get a gap of (4.0 * ruleSize) trySubScriptShift += NSToCoordRound ((4.0f * ruleSize) - gap); } // next we want to ensure that the bottom of the superscript // will be > (4/5) * x-height above baseline gap = NSToCoordRound ((4.0f/5.0f) * xHeight - (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.SetTopAscent( std::max(baseSize.TopAscent(), std::max(multiSubSize.TopAscent() - maxSubScriptShift, multiSupSize.TopAscent() + maxSupScriptShift))); aDesiredSize.Height() = aDesiredSize.TopAscent() + std::max(baseSize.Height() - baseSize.TopAscent(), std::max(multiSubSize.Height() + maxSubScriptShift, multiSupSize.Height() - maxSupScriptShift)); aDesiredSize.Width() = boundingMetrics.width; aDesiredSize.mBoundingMetrics = boundingMetrics; aFrame->SetReference(nsPoint(0, aDesiredSize.TopAscent())); ////////////////// // 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.TopAscent() - baseSize.TopAscent(); FinishReflowChild (baseFrame, aPresContext, baseSize, nullptr, aFrame->MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx), dy, 0); dx += bmBase.width; } else if (prescriptsFrame != childFrame) { // 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.TopAscent() - subScriptSize.TopAscent() + 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.TopAscent() - supScriptSize.TopAscent() - maxSupScriptShift; FinishReflowChild (supScriptFrame, aPresContext, supScriptSize, nullptr, aFrame->MirrorIfRTL(aDesiredSize.Width(), supScriptSize.Width(), x), dy, 0); } dx += width + aScriptSpace; } } childFrame = childFrame->GetNextSibling(); } while (prescriptsFrame != childFrame); } return NS_OK; }
nsresult nsNumberControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsNumberControlFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_ASSERTION(mOuterWrapper, "Outer wrapper div must exist!"); NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(), "nsNumberControlFrame should not have continuations; if it does we " "need to call RegUnregAccessKey only for the first"); NS_ASSERTION(!mFrames.FirstChild() || !mFrames.FirstChild()->GetNextSibling(), "We expect at most one direct child frame"); if (mState & NS_FRAME_FIRST_REFLOW) { nsFormControlFrame::RegUnRegAccessKey(this, true); } // The width of our content box, which is the available width // for our anonymous content: const nscoord contentBoxWidth = aReflowState.ComputedWidth(); nscoord contentBoxHeight = aReflowState.ComputedHeight(); nsIFrame* outerWrapperFrame = mOuterWrapper->GetPrimaryFrame(); if (!outerWrapperFrame) { // display:none? if (contentBoxHeight == NS_INTRINSICSIZE) { contentBoxHeight = 0; } } else { NS_ASSERTION(outerWrapperFrame == mFrames.FirstChild(), "huh?"); nsHTMLReflowMetrics wrappersDesiredSize(aReflowState.GetWritingMode()); nsHTMLReflowState wrapperReflowState(aPresContext, aReflowState, outerWrapperFrame, nsSize(contentBoxWidth, NS_UNCONSTRAINEDSIZE)); // offsets of wrapper frame nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left + wrapperReflowState.ComputedPhysicalMargin().left; nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top + wrapperReflowState.ComputedPhysicalMargin().top; nsReflowStatus childStatus; nsresult rv = ReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, wrapperReflowState, xoffset, yoffset, 0, childStatus); NS_ENSURE_SUCCESS(rv, rv); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus), "We gave our child unconstrained height, so it should be complete"); nscoord wrappersMarginBoxHeight = wrappersDesiredSize.Height() + wrapperReflowState.ComputedPhysicalMargin().TopBottom(); if (contentBoxHeight == NS_INTRINSICSIZE) { // We are intrinsically sized -- we should shrinkwrap the outer wrapper's // height: contentBoxHeight = wrappersMarginBoxHeight; // Make sure we obey min/max-height in the case when we're doing intrinsic // sizing (we get it for free when we have a non-intrinsic // aReflowState.ComputedHeight()). Note that we do this before // adjusting for borderpadding, since mComputedMaxHeight and // mComputedMinHeight are content heights. contentBoxHeight = NS_CSS_MINMAX(contentBoxHeight, aReflowState.ComputedMinHeight(), aReflowState.ComputedMaxHeight()); } // Center child vertically nscoord extraSpace = contentBoxHeight - wrappersMarginBoxHeight; yoffset += std::max(0, extraSpace / 2); // Place the child rv = FinishReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, &wrapperReflowState, xoffset, yoffset, 0); NS_ENSURE_SUCCESS(rv, rv); aDesiredSize.SetTopAscent(wrappersDesiredSize.TopAscent() + outerWrapperFrame->GetPosition().y); } aDesiredSize.Width() = contentBoxWidth + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); aDesiredSize.Height() = contentBoxHeight + aReflowState.ComputedPhysicalBorderPadding().TopBottom(); aDesiredSize.SetOverflowAreasToDesiredBounds(); if (outerWrapperFrame) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, outerWrapperFrame); } FinishAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return NS_OK; }
void nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); nsReflowStatus childStatus; mPresentationData.flags &= ~NS_MATHML_ERROR; aDesiredSize.ClearSize(); aDesiredSize.SetBlockStartAscent(0); nsBoundingMetrics bmSqr, bmBase, bmIndex; DrawTarget* drawTarget = aReflowState.rendContext->GetDrawTarget(); ////////////////// // Reflow Children int32_t count = 0; nsIFrame* baseFrame = nullptr; nsIFrame* indexFrame = nullptr; nsHTMLReflowMetrics baseSize(aReflowState); nsHTMLReflowMetrics indexSize(aReflowState); nsIFrame* childFrame = mFrames.FirstChild(); while (childFrame) { // ask our children to compute their bounding metrics nsHTMLReflowMetrics childDesiredSize(aReflowState, aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS); WritingMode wm = childFrame->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState childReflowState(aPresContext, aReflowState, childFrame, availSize); ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowState, childStatus); //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status"); if (0 == count) { // base baseFrame = childFrame; baseSize = childDesiredSize; bmBase = childDesiredSize.mBoundingMetrics; } else if (1 == count) { // index indexFrame = childFrame; indexSize = childDesiredSize; bmIndex = childDesiredSize.mBoundingMetrics; } count++; childFrame = childFrame->GetNextSibling(); } if (2 != count) { // report an error, encourage people to get their markups in order ReportChildCountError(); ReflowError(drawTarget, aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); // Call DidReflow() for the child frames we successfully did reflow. DidReflowChildren(mFrames.FirstChild(), childFrame); return; } //////////// // Prepare the radical symbol and the overline bar RefPtr<nsFontMetrics> fm; float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), fontSizeInflation); nscoord ruleThickness, leading, psi; GetRadicalParameters(fm, StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK, ruleThickness, leading, psi); // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131) char16_t one = '1'; nsBoundingMetrics bmOne = nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, drawTarget); if (bmOne.ascent > bmBase.ascent) psi += bmOne.ascent - bmBase.ascent; // make sure that the rule appears on on screen nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); if (ruleThickness < onePixel) { ruleThickness = onePixel; } // adjust clearance psi to get an exact number of pixels -- this // gives a nicer & uniform look on stacked radicals (bug 130282) nscoord delta = psi % onePixel; if (delta) psi += onePixel - delta; // round up // Stretch the radical symbol to the appropriate height if it is not big enough. nsBoundingMetrics contSize = bmBase; contSize.descent = bmBase.ascent + bmBase.descent + psi; contSize.ascent = ruleThickness; // height(radical) should be >= height(base) + psi + ruleThickness nsBoundingMetrics radicalSize; mSqrChar.Stretch(aPresContext, drawTarget, fontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL, contSize, radicalSize, NS_STRETCH_LARGER, StyleVisibility()->mDirection); // radicalSize have changed at this point, and should match with // the bounding metrics of the char mSqrChar.GetBoundingMetrics(bmSqr); // Update the desired size for the container (like msqrt, index is not yet included) // the baseline will be that of the base. mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness; mBoundingMetrics.descent = std::max(bmBase.descent, (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent)); mBoundingMetrics.width = bmSqr.width + bmBase.width; mBoundingMetrics.leftBearing = bmSqr.leftBearing; mBoundingMetrics.rightBearing = bmSqr.width + std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + std::max(baseSize.Height() - baseSize.BlockStartAscent(), mBoundingMetrics.descent + ruleThickness); aDesiredSize.Width() = mBoundingMetrics.width; ///////////// // Re-adjust the desired size to include the index. // the index is raised by some fraction of the height // of the radical, see \mroot macro in App. B, TexBook float raiseIndexPercent = 0.6f; gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont(); if (mathFont) { raiseIndexPercent = mathFont->GetMathConstant(gfxFontEntry::RadicalDegreeBottomRaisePercent); } nscoord raiseIndexDelta = NSToCoordRound(raiseIndexPercent * (bmSqr.ascent + bmSqr.descent)); nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical - (bmSqr.ascent + bmSqr.descent) // to bottom of radical + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index nscoord indexClearance = 0; if (mBoundingMetrics.ascent < indexRaisedAscent) { indexClearance = indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index mBoundingMetrics.ascent = indexRaisedAscent; nscoord descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + descent; } nscoord dxIndex, dxSqr; GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr); mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width; mBoundingMetrics.leftBearing = std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing); mBoundingMetrics.rightBearing = dxSqr + bmSqr.width + std::max(bmBase.width, bmBase.rightBearing); aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; GatherAndStoreOverflow(&aDesiredSize); // place the index nscoord dx = dxIndex; nscoord dy = aDesiredSize.BlockStartAscent() - (indexRaisedAscent + indexSize.BlockStartAscent() - bmIndex.ascent); FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr, MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx), dy, 0); // place the radical symbol and the radical bar dx = dxSqr; dy = indexClearance + leading; // leave a leading at the top mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx), dy, bmSqr.width, bmSqr.ascent + bmSqr.descent)); dx += bmSqr.width; mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx), dy, bmBase.width, ruleThickness); // place the base dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent(); FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr, MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx), dy, 0); mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsCanvasFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow"); // Initialize OUT parameter aStatus = NS_FRAME_COMPLETE; nsCanvasFrame* prevCanvasFrame = static_cast<nsCanvasFrame*> (GetPrevInFlow()); if (prevCanvasFrame) { AutoFrameListPtr overflow(aPresContext, prevCanvasFrame->StealOverflowFrames()); if (overflow) { NS_ASSERTION(overflow->OnlyChild(), "must have doc root as canvas frame's only child"); nsContainerFrame::ReparentFrameViewList(*overflow, prevCanvasFrame, this); // Prepend overflow to the our child list. There may already be // children placeholders for fixed-pos elements, which don't get // reflowed but must not be lost until the canvas frame is destroyed. mFrames.InsertFrames(this, nullptr, *overflow); } } // Set our size up front, since some parts of reflow depend on it // being already set. Note that the computed height may be // unconstrained; that's ok. Consumers should watch out for that. SetSize(nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); // Reflow our one and only normal child frame. It's either the root // element's frame or a placeholder for that frame, if the root element // is abs-pos or fixed-pos. We may have additional children which // are placeholders for continuations of fixed-pos content, but those // don't need to be reflowed. The normal child is always comes before // the fixed-pos placeholders, because we insert it at the start // of the child list, above. nsHTMLReflowMetrics kidDesiredSize(aReflowState); if (mFrames.IsEmpty()) { // We have no child frame, so return an empty size aDesiredSize.Width() = aDesiredSize.Height() = 0; } else { nsIFrame* kidFrame = mFrames.FirstChild(); bool kidDirty = (kidFrame->GetStateBits() & NS_FRAME_IS_DIRTY) != 0; nsHTMLReflowState kidReflowState(aPresContext, aReflowState, kidFrame, aReflowState.AvailableSize(kidFrame->GetWritingMode())); if (aReflowState.mFlags.mVResize && (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { // Tell our kid it's being vertically resized too. Bit of a // hack for framesets. kidReflowState.mFlags.mVResize = true; } nsPoint kidPt(kidReflowState.ComputedPhysicalMargin().left, kidReflowState.ComputedPhysicalMargin().top); kidReflowState.ApplyRelativePositioning(&kidPt); // Reflow the frame ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState, kidPt.x, kidPt.y, 0, aStatus); // Complete the reflow and position and size the child frame FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowState, kidPt.x, kidPt.y, 0); if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { nsIFrame* nextFrame = kidFrame->GetNextInFlow(); NS_ASSERTION(nextFrame || aStatus & NS_FRAME_REFLOW_NEXTINFLOW, "If it's incomplete and has no nif yet, it must flag a nif reflow."); if (!nextFrame) { nextFrame = aPresContext->PresShell()->FrameConstructor()-> CreateContinuingFrame(aPresContext, kidFrame, this); SetOverflowFrames(nsFrameList(nextFrame, nextFrame)); // Root overflow containers will be normal children of // the canvas frame, but that's ok because there // aren't any other frames we need to isolate them from // during reflow. } if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { nextFrame->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } } // If the child frame was just inserted, then we're responsible for making sure // it repaints if (kidDirty) { // But we have a new child, which will affect our background, so // invalidate our whole rect. // Note: Even though we request to be sized to our child's size, our // scroll frame ensures that we are always the size of the viewport. // Also note: GetPosition() on a CanvasFrame is always going to return // (0, 0). We only want to invalidate GetRect() since Get*OverflowRect() // could also include overflow to our top and left (out of the viewport) // which doesn't need to be painted. nsIFrame* viewport = PresContext()->GetPresShell()->GetRootFrame(); viewport->InvalidateFrame(); } // Return our desired size. Normally it's what we're told, but // sometimes we can be given an unconstrained height (when a window // is sizing-to-content), and we should compute our desired height. WritingMode wm = aReflowState.GetWritingMode(); LogicalSize finalSize(wm); finalSize.ISize(wm) = aReflowState.ComputedISize(); if (aReflowState.ComputedBSize() == NS_UNCONSTRAINEDSIZE) { finalSize.BSize(wm) = kidFrame->GetLogicalSize(wm).BSize(wm) + kidReflowState.ComputedLogicalMargin().BStartEnd(wm); } else { finalSize.BSize(wm) = aReflowState.ComputedBSize(); } aDesiredSize.SetSize(wm, finalSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); aDesiredSize.mOverflowAreas.UnionWith( kidDesiredSize.mOverflowAreas + kidPt); } if (prevCanvasFrame) { ReflowOverflowContainerChildren(aPresContext, aReflowState, aDesiredSize.mOverflowAreas, 0, aStatus); } FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); NS_FRAME_TRACE_REFLOW_OUT("nsCanvasFrame::Reflow", aStatus); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsInlineFrame::ReflowFrames(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, InlineReflowState& irs, nsHTMLReflowMetrics& aMetrics, nsReflowStatus& aStatus) { aStatus = NS_FRAME_COMPLETE; nsLineLayout* lineLayout = aReflowState.mLineLayout; bool inFirstLine = aReflowState.mLineLayout->GetInFirstLine(); RestyleManager* restyleManager = aPresContext->RestyleManager(); WritingMode frameWM = aReflowState.GetWritingMode(); WritingMode lineWM = aReflowState.mLineLayout->mRootSpan->mWritingMode; LogicalMargin framePadding = aReflowState.ComputedLogicalBorderPadding(); nscoord startEdge = 0; const bool boxDecorationBreakClone = MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak == NS_STYLE_BOX_DECORATION_BREAK_CLONE); // Don't offset by our start borderpadding if we have a prev continuation or // if we're in a part of an {ib} split other than the first one. For // box-decoration-break:clone we always offset our start since all // continuations have border/padding. if ((!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) || boxDecorationBreakClone) { startEdge = framePadding.IStart(frameWM); } nscoord availableISize = aReflowState.AvailableISize(); NS_ASSERTION(availableISize != NS_UNCONSTRAINEDSIZE, "should no longer use available widths"); // Subtract off inline axis border+padding from availableISize availableISize -= startEdge; availableISize -= framePadding.IEnd(frameWM); lineLayout->BeginSpan(this, &aReflowState, startEdge, startEdge + availableISize, &mBaseline); // First reflow our principal children. nsIFrame* frame = mFrames.FirstChild(); bool done = false; while (frame) { // Check if we should lazily set the child frame's parent pointer. if (irs.mSetParentPointer) { bool havePrevBlock = irs.mLineContainer && irs.mLineContainer->GetPrevContinuation(); nsIFrame* child = frame; do { // If our block is the first in flow, then any floats under the pulled // frame must already belong to our block. if (havePrevBlock) { // This has to happen before we update frame's parent; we need to // know frame's ancestry under its old block. // The blockChildren.ContainsFrame check performed by // ReparentFloatsForInlineChild here may be slow, but we can't // easily avoid it because we don't know where 'frame' originally // came from. If we really really have to optimize this we could // cache whether frame->GetParent() is under its containing blocks // overflowList or not. ReparentFloatsForInlineChild(irs.mLineContainer, child, false); } child->SetParent(this); if (inFirstLine) { restyleManager->ReparentStyleContext(child); nsLayoutUtils::MarkDescendantsDirty(child); } // We also need to do the same for |frame|'s next-in-flows that are in // the sibling list. Otherwise, if we reflow |frame| and it's complete // we'll crash when trying to delete its next-in-flow. // This scenario doesn't happen often, but it can happen. nsIFrame* nextSibling = child->GetNextSibling(); child = child->GetNextInFlow(); if (MOZ_UNLIKELY(child)) { while (child != nextSibling && nextSibling) { nextSibling = nextSibling->GetNextSibling(); } if (!nextSibling) { child = nullptr; } } MOZ_ASSERT(!child || mFrames.ContainsFrame(child)); } while (child); // Fix the parent pointer for ::first-letter child frame next-in-flows, // so nsFirstLetterFrame::Reflow can destroy them safely (bug 401042). nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(frame); if (realFrame->GetType() == nsGkAtoms::letterFrame) { nsIFrame* child = realFrame->GetFirstPrincipalChild(); if (child) { NS_ASSERTION(child->GetType() == nsGkAtoms::textFrame, "unexpected frame type"); nsIFrame* nextInFlow = child->GetNextInFlow(); for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) { NS_ASSERTION(nextInFlow->GetType() == nsGkAtoms::textFrame, "unexpected frame type"); if (mFrames.ContainsFrame(nextInFlow)) { nextInFlow->SetParent(this); if (inFirstLine) { restyleManager->ReparentStyleContext(nextInFlow); nsLayoutUtils::MarkDescendantsDirty(nextInFlow); } } else { #ifdef DEBUG // Once we find a next-in-flow that isn't ours none of the // remaining next-in-flows should be either. for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) { NS_ASSERTION(!mFrames.ContainsFrame(nextInFlow), "unexpected letter frame flow"); } #endif break; } } } } } MOZ_ASSERT(frame->GetParent() == this); if (!done) { bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK(); ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus); done = NS_INLINE_IS_BREAK(aStatus) || (!reflowingFirstLetter && NS_FRAME_IS_NOT_COMPLETE(aStatus)); if (done) { if (!irs.mSetParentPointer) { break; } // Keep reparenting the remaining siblings, but don't reflow them. nsFrameList* pushedFrames = GetOverflowFrames(); if (pushedFrames && pushedFrames->FirstChild() == frame) { // Don't bother if |frame| was pushed to our overflow list. break; } } else { irs.mPrevFrame = frame; } } frame = frame->GetNextSibling(); } // Attempt to pull frames from our next-in-flow until we can't if (!done && GetNextInFlow()) { while (true) { bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK(); bool isComplete; if (!frame) { // Could be non-null if we pulled a first-letter frame and // it created a continuation, since we don't push those. frame = PullOneFrame(aPresContext, irs, &isComplete); } #ifdef NOISY_PUSHING printf("%p pulled up %p\n", this, frame); #endif if (nullptr == frame) { if (!isComplete) { aStatus = NS_FRAME_NOT_COMPLETE; } break; } ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus); if (NS_INLINE_IS_BREAK(aStatus) || (!reflowingFirstLetter && NS_FRAME_IS_NOT_COMPLETE(aStatus))) { break; } irs.mPrevFrame = frame; frame = frame->GetNextSibling(); } } NS_ASSERTION(!NS_FRAME_IS_COMPLETE(aStatus) || !GetOverflowFrames(), "We can't be complete AND have overflow frames!"); // If after reflowing our children they take up no area then make // sure that we don't either. // // Note: CSS demands that empty inline elements still affect the // line-height calculations. However, continuations of an inline // that are empty we force to empty so that things like collapsed // whitespace in an inline element don't affect the line-height. aMetrics.ISize(lineWM) = lineLayout->EndSpan(this); // Compute final width. // XXX Note that that the padding start and end are in the frame's // writing mode, but the metrics' inline-size is in the line's // writing mode. This makes sense if the line and frame are both // vertical or both horizontal, but what should happen with // orthogonal inlines? // Make sure to not include our start border and padding if we have a prev // continuation or if we're in a part of an {ib} split other than the first // one. For box-decoration-break:clone we always include our start border // and padding since all continuations have them. if ((!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) || boxDecorationBreakClone) { aMetrics.ISize(lineWM) += framePadding.IStart(frameWM); } /* * We want to only apply the end border and padding if we're the last * continuation and either not in an {ib} split or the last part of it. To * be the last continuation we have to be complete (so that we won't get a * next-in-flow) and have no non-fluid continuations on our continuation * chain. For box-decoration-break:clone we always apply the end border and * padding since all continuations have them. */ if ((NS_FRAME_IS_COMPLETE(aStatus) && !LastInFlow()->GetNextContinuation() && !FrameIsNonLastInIBSplit()) || boxDecorationBreakClone) { aMetrics.ISize(lineWM) += framePadding.IEnd(frameWM); } nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, framePadding, lineWM, frameWM); // For now our overflow area is zero. The real value will be // computed in |nsLineLayout::RelativePositionFrames|. aMetrics.mOverflowAreas.Clear(); #ifdef NOISY_FINAL_SIZE ListTag(stdout); printf(": metrics=%d,%d ascent=%d\n", aMetrics.Width(), aMetrics.Height(), aMetrics.TopAscent()); #endif }