void nsMathMLmpaddedFrame::UpdateValue(int32_t aSign, int32_t aPseudoUnit, const nsCSSValue& aCSSValue, const ReflowOutput& aDesiredSize, nscoord& aValueToUpdate, float aFontSizeInflation) const { nsCSSUnit unit = aCSSValue.GetUnit(); if (NS_MATHML_SIGN_INVALID != aSign && eCSSUnit_Null != unit) { nscoord scaler = 0, amount = 0; if (eCSSUnit_Percent == unit || eCSSUnit_Number == unit) { switch(aPseudoUnit) { case NS_MATHML_PSEUDO_UNIT_WIDTH: scaler = aDesiredSize.Width(); break; case NS_MATHML_PSEUDO_UNIT_HEIGHT: scaler = aDesiredSize.BlockStartAscent(); break; case NS_MATHML_PSEUDO_UNIT_DEPTH: scaler = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); break; default: // if we ever reach here, it would mean something is wrong // somewhere with the setup and/or the caller NS_ERROR("Unexpected Pseudo Unit"); return; } } if (eCSSUnit_Number == unit) amount = NSToCoordRound(float(scaler) * aCSSValue.GetFloatValue()); else if (eCSSUnit_Percent == unit) amount = NSToCoordRound(float(scaler) * aCSSValue.GetPercentValue()); else amount = CalcLength(PresContext(), mStyleContext, aCSSValue, aFontSizeInflation); if (NS_MATHML_SIGN_PLUS == aSign) aValueToUpdate += amount; else if (NS_MATHML_SIGN_MINUS == aSign) aValueToUpdate -= amount; else aValueToUpdate = amount; } }
void nsSimplePageSequenceFrame::SetDesiredSize(ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nscoord aWidth, nscoord aHeight) { // Aim to fill the whole size of the document, not only so we // can act as a background in print preview but also handle overflow // in child page frames correctly. // Use availableWidth so we don't cause a needless horizontal scrollbar. aDesiredSize.Width() = std::max(aReflowInput.AvailableWidth(), nscoord(aWidth * PresContext()->GetPrintPreviewScale())); aDesiredSize.Height() = std::max(aReflowInput.ComputedHeight(), nscoord(aHeight * PresContext()->GetPrintPreviewScale())); }
void nsVideoFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsVideoFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsVideoFrame::Reflow: availSize=%d,%d", aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight())); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; aMetrics.Width() = aReflowInput.ComputedWidth(); aMetrics.Height() = aReflowInput.ComputedHeight(); // stash this away so we can compute our inner area later mBorderPadding = aReflowInput.ComputedPhysicalBorderPadding(); aMetrics.Width() += mBorderPadding.left + mBorderPadding.right; aMetrics.Height() += mBorderPadding.top + mBorderPadding.bottom; // Reflow the child frames. We may have up to two, an image frame // which is the poster, and a box frame, which is the video controls. for (nsIFrame* child : mFrames) { if (child->GetContent() == mPosterImage) { // Reflow the poster frame. nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child); ReflowOutput kidDesiredSize(aReflowInput); WritingMode wm = imageFrame->GetWritingMode(); LogicalSize availableSize = aReflowInput.AvailableSize(wm); LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()). ConvertTo(wm, aMetrics.GetWritingMode()); ReflowInput kidReflowInput(aPresContext, aReflowInput, imageFrame, availableSize, &cbSize); nsRect posterRenderRect; if (ShouldDisplayPoster()) { posterRenderRect = nsRect(nsPoint(mBorderPadding.left, mBorderPadding.top), nsSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight())); } kidReflowInput.SetComputedWidth(posterRenderRect.width); kidReflowInput.SetComputedHeight(posterRenderRect.height); ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowInput, posterRenderRect.x, posterRenderRect.y, 0, aStatus); FinishReflowChild(imageFrame, aPresContext, kidDesiredSize, &kidReflowInput, posterRenderRect.x, posterRenderRect.y, 0); } else if (child->GetContent() == mVideoControls) { // Reflow the video controls frame. nsBoxLayoutState boxState(PresContext(), aReflowInput.mRenderingContext); nsSize size = child->GetSize(); nsBoxFrame::LayoutChildAt(boxState, child, nsRect(mBorderPadding.left, mBorderPadding.top, aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight())); if (child->GetSize() != size) { RefPtr<Runnable> event = new DispatchResizeToControls(child->GetContent()); nsContentUtils::AddScriptRunner(event); } } else if (child->GetContent() == mCaptionDiv) { // Reflow to caption div ReflowOutput kidDesiredSize(aReflowInput); WritingMode wm = child->GetWritingMode(); LogicalSize availableSize = aReflowInput.AvailableSize(wm); LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()). ConvertTo(wm, aMetrics.GetWritingMode()); ReflowInput kidReflowInput(aPresContext, aReflowInput, child, availableSize, &cbSize); nsSize size(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight()); size.width -= kidReflowInput.ComputedPhysicalBorderPadding().LeftRight(); size.height -= kidReflowInput.ComputedPhysicalBorderPadding().TopBottom(); kidReflowInput.SetComputedWidth(std::max(size.width, 0)); kidReflowInput.SetComputedHeight(std::max(size.height, 0)); ReflowChild(child, aPresContext, kidDesiredSize, kidReflowInput, mBorderPadding.left, mBorderPadding.top, 0, aStatus); FinishReflowChild(child, aPresContext, kidDesiredSize, &kidReflowInput, 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, aReflowInput, aMetrics); }
void nsInlineFrame::ReflowFrames(nsPresContext* aPresContext, const ReflowInput& aReflowInput, InlineReflowInput& irs, ReflowOutput& aMetrics, nsReflowStatus& aStatus) { MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); nsLineLayout* lineLayout = aReflowInput.mLineLayout; bool inFirstLine = aReflowInput.mLineLayout->GetInFirstLine(); RestyleManager* restyleManager = aPresContext->RestyleManager(); WritingMode frameWM = aReflowInput.GetWritingMode(); WritingMode lineWM = aReflowInput.mLineLayout->mRootSpan->mWritingMode; LogicalMargin framePadding = aReflowInput.ComputedLogicalBorderPadding(); nscoord startEdge = 0; const bool boxDecorationBreakClone = MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::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 = aReflowInput.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, &aReflowInput, 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) { nsIFrame* child = frame; do { 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->IsLetterFrame()) { nsIFrame* child = realFrame->PrincipalChildList().FirstChild(); if (child) { NS_ASSERTION(child->IsTextFrame(), "unexpected frame type"); nsIFrame* nextInFlow = child->GetNextInFlow(); for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) { NS_ASSERTION(nextInFlow->IsTextFrame(), "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, aReflowInput, irs, frame, aStatus); done = aStatus.IsInlineBreak() || (!reflowingFirstLetter && aStatus.IsIncomplete()); 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.Reset(); aStatus.SetIncomplete(); } break; } ReflowInlineFrame(aPresContext, aReflowInput, irs, frame, aStatus); if (aStatus.IsInlineBreak() || (!reflowingFirstLetter && aStatus.IsIncomplete())) { break; } irs.mPrevFrame = frame; frame = frame->GetNextSibling(); } } NS_ASSERTION(!aStatus.IsComplete() || !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 ((aStatus.IsComplete() && !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 }
/* virtual */ nsresult nsMathMLmpaddedFrame::Place(DrawTarget* aDrawTarget, bool aPlaceOrigin, ReflowOutput& aDesiredSize) { nsresult rv = nsMathMLContainerFrame::Place(aDrawTarget, false, aDesiredSize); if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { DidReflowChildren(PrincipalChildList().FirstChild()); return rv; } nscoord height = aDesiredSize.BlockStartAscent(); nscoord depth = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); // The REC says: // // "The lspace attribute ('leading' space) specifies the horizontal location // of the positioning point of the child content with respect to the // positioning point of the mpadded element. By default they coincide, and // therefore absolute values for lspace have the same effect as relative // values." // // "MathML renderers should ensure that, except for the effects of the // attributes, the relative spacing between the contents of the mpadded // element and surrounding MathML elements would not be modified by replacing // an mpadded element with an mrow element with the same content, even if // linebreaking occurs within the mpadded element." // // (http://www.w3.org/TR/MathML/chapter3.html#presm.mpadded) // // "In those discussions, the terms leading and trailing are used to specify // a side of an object when which side to use depends on the directionality; // ie. leading means left in LTR but right in RTL." // (http://www.w3.org/TR/MathML/chapter3.html#presm.bidi.math) nscoord lspace = 0; // In MathML3, "width" will be the bounding box width and "advancewidth" will // refer "to the horizontal distance between the positioning point of the // mpadded and the positioning point for the following content". MathML2 // doesn't make the distinction. nscoord width = aDesiredSize.Width(); nscoord voffset = 0; int32_t pseudoUnit; nscoord initialWidth = width; float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); // update width pseudoUnit = (mWidthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF) ? NS_MATHML_PSEUDO_UNIT_WIDTH : mWidthPseudoUnit; UpdateValue(mWidthSign, pseudoUnit, mWidth, aDesiredSize, width, fontSizeInflation); width = std::max(0, width); // update "height" (this is the ascent in the terminology of the REC) pseudoUnit = (mHeightPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF) ? NS_MATHML_PSEUDO_UNIT_HEIGHT : mHeightPseudoUnit; UpdateValue(mHeightSign, pseudoUnit, mHeight, aDesiredSize, height, fontSizeInflation); height = std::max(0, height); // update "depth" (this is the descent in the terminology of the REC) pseudoUnit = (mDepthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF) ? NS_MATHML_PSEUDO_UNIT_DEPTH : mDepthPseudoUnit; UpdateValue(mDepthSign, pseudoUnit, mDepth, aDesiredSize, depth, fontSizeInflation); depth = std::max(0, depth); // update lspace if (mLeadingSpacePseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) { pseudoUnit = mLeadingSpacePseudoUnit; UpdateValue(mLeadingSpaceSign, pseudoUnit, mLeadingSpace, aDesiredSize, lspace, fontSizeInflation); } // update voffset if (mVerticalOffsetPseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) { pseudoUnit = mVerticalOffsetPseudoUnit; UpdateValue(mVerticalOffsetSign, pseudoUnit, mVerticalOffset, aDesiredSize, voffset, fontSizeInflation); } // do the padding now that we have everything // The idea here is to maintain the invariant that <mpadded>...</mpadded> (i.e., // with no attributes) looks the same as <mrow>...</mrow>. But when there are // attributes, tweak our metrics and move children to achieve the desired visual // effects. if ((StyleVisibility()->mDirection ? mWidthSign : mLeadingSpaceSign) != NS_MATHML_SIGN_INVALID) { // there was padding on the left. dismiss the left italic correction now // (so that our parent won't correct us) mBoundingMetrics.leftBearing = 0; } if ((StyleVisibility()->mDirection ? mLeadingSpaceSign : mWidthSign) != NS_MATHML_SIGN_INVALID) { // there was padding on the right. dismiss the right italic correction now // (so that our parent won't correct us) mBoundingMetrics.width = width; mBoundingMetrics.rightBearing = mBoundingMetrics.width; } nscoord dx = (StyleVisibility()->mDirection ? width - initialWidth - lspace : lspace); aDesiredSize.SetBlockStartAscent(height); aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.Height() = depth + aDesiredSize.BlockStartAscent(); mBoundingMetrics.ascent = height; mBoundingMetrics.descent = depth; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); if (aPlaceOrigin) { // Finish reflowing child frames, positioning their origins. PositionRowChildFrames(dx, aDesiredSize.BlockStartAscent() - voffset); } return NS_OK; }
/* virtual */ nsresult nsMathMLmencloseFrame::PlaceInternal(DrawTarget* aDrawTarget, bool aPlaceOrigin, ReflowOutput& aDesiredSize, bool aWidthOnly) { /////////////// // Measure the size of our content using the base class to format like an // inferred mrow. ReflowOutput baseSize(aDesiredSize.GetWritingMode()); nsresult rv = nsMathMLContainerFrame::Place(aDrawTarget, false, baseSize); if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { DidReflowChildren(PrincipalChildList().FirstChild()); return rv; } nsBoundingMetrics bmBase = baseSize.mBoundingMetrics; nscoord dx_left = 0, dx_right = 0; nsBoundingMetrics bmLongdivChar, bmRadicalChar; nscoord radicalAscent = 0, radicalDescent = 0; nscoord longdivAscent = 0, longdivDescent = 0; nscoord psi = 0; nscoord leading = 0; /////////////// // Thickness of bars and font metrics nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation); GetRuleThickness(aDrawTarget, fm, mRuleThickness); if (mRuleThickness < onePixel) { mRuleThickness = onePixel; } char16_t one = '1'; nsBoundingMetrics bmOne = nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, aDrawTarget); /////////////// // General rules: the menclose element takes the size of the enclosed content. // We add a padding when needed. // determine padding & psi nscoord padding = 3 * mRuleThickness; nscoord delta = padding % onePixel; if (delta) padding += onePixel - delta; // round up if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { GetRadicalParameters(fm, StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK, mRadicalRuleThickness, leading, psi); // make sure that the rule appears on on screen if (mRadicalRuleThickness < onePixel) { mRadicalRuleThickness = onePixel; } // adjust clearance psi to get an exact number of pixels -- this // gives a nicer & uniform look on stacked radicals (bug 130282) delta = psi % onePixel; if (delta) { psi += onePixel - delta; // round up } } // Set horizontal parameters if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_CIRCLE)) dx_left = padding; if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_CIRCLE)) dx_right = padding; // Set vertical parameters if (IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) || IsToDraw(NOTATION_UPDIAGONALARROW) || IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_RADICAL) || IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_PHASORANGLE)) { // set a minimal value for the base height bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent); bmBase.descent = std::max(0, bmBase.descent); } mBoundingMetrics.ascent = bmBase.ascent; mBoundingMetrics.descent = bmBase.descent; if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_CIRCLE)) mBoundingMetrics.ascent += padding; if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_CIRCLE)) mBoundingMetrics.descent += padding; /////////////// // phasorangle notation if (IsToDraw(NOTATION_PHASORANGLE)) { nscoord phasorangleWidth = kPhasorangleWidth * mRuleThickness; // Update horizontal parameters dx_left = std::max(dx_left, phasorangleWidth); } /////////////// // updiagonal arrow notation. We need enough space at the top right corner to // draw the arrow head. if (IsToDraw(NOTATION_UPDIAGONALARROW)) { // This is an estimate, see nsDisplayNotation::Paint for the exact head size nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness; // We want that the arrow shaft strikes the menclose content and that the // arrow head does not overlap with that content. Hence we add some space // on the right. We don't add space on the top but only ensure that the // ascent is large enough. dx_right = std::max(dx_right, arrowHeadSize); mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize); } /////////////// // circle notation: we don't want the ellipse to overlap the enclosed // content. Hence, we need to increase the size of the bounding box by a // factor of at least sqrt(2). if (IsToDraw(NOTATION_CIRCLE)) { double ratio = (sqrt(2.0) - 1.0) / 2.0; nscoord padding2; // Update horizontal parameters padding2 = ratio * bmBase.width; dx_left = std::max(dx_left, padding2); dx_right = std::max(dx_right, padding2); // Update vertical parameters padding2 = ratio * (bmBase.ascent + bmBase.descent); mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, bmBase.ascent + padding2); mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, bmBase.descent + padding2); } /////////////// // longdiv notation: if (IsToDraw(NOTATION_LONGDIV)) { if (aWidthOnly) { nscoord longdiv_width = mMathMLChar[mLongDivCharIndex]. GetMaxWidth(PresContext(), aDrawTarget, fontSizeInflation); // Update horizontal parameters dx_left = std::max(dx_left, longdiv_width); } else { // Stretch the parenthesis to the appropriate height if it is not // big enough. nsBoundingMetrics contSize = bmBase; contSize.ascent = mRuleThickness; contSize.descent = bmBase.ascent + bmBase.descent + psi; // height(longdiv) should be >= height(base) + psi + mRuleThickness mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aDrawTarget, fontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL, contSize, bmLongdivChar, NS_STRETCH_LARGER, false); mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar); // Update horizontal parameters dx_left = std::max(dx_left, bmLongdivChar.width); // Update vertical parameters longdivAscent = bmBase.ascent + psi + mRuleThickness; longdivDescent = std::max(bmBase.descent, (bmLongdivChar.ascent + bmLongdivChar.descent - longdivAscent)); mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, longdivAscent); mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, longdivDescent); } } /////////////// // radical notation: if (IsToDraw(NOTATION_RADICAL)) { nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left; if (aWidthOnly) { nscoord radical_width = mMathMLChar[mRadicalCharIndex]. GetMaxWidth(PresContext(), aDrawTarget, fontSizeInflation); // Update horizontal parameters *dx_leading = std::max(*dx_leading, radical_width); } else { // Stretch the radical symbol to the appropriate height if it is not // big enough. nsBoundingMetrics contSize = bmBase; contSize.ascent = mRadicalRuleThickness; contSize.descent = bmBase.ascent + bmBase.descent + psi; // height(radical) should be >= height(base) + psi + mRadicalRuleThickness mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aDrawTarget, fontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL, contSize, bmRadicalChar, NS_STRETCH_LARGER, StyleVisibility()->mDirection); mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar); // Update horizontal parameters *dx_leading = std::max(*dx_leading, bmRadicalChar.width); // Update vertical parameters radicalAscent = bmBase.ascent + psi + mRadicalRuleThickness; radicalDescent = std::max(bmBase.descent, (bmRadicalChar.ascent + bmRadicalChar.descent - radicalAscent)); mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, radicalAscent); mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, radicalDescent); } } /////////////// // if (IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) || (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) { // center the menclose around the content (horizontally) dx_left = dx_right = std::max(dx_left, dx_right); } /////////////// // The maximum size is now computed: set the remaining parameters mBoundingMetrics.width = dx_left + bmBase.width + dx_right; mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing); mBoundingMetrics.rightBearing = std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing); aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent, baseSize.BlockStartAscent())); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + std::max(mBoundingMetrics.descent, baseSize.Height() - baseSize.BlockStartAscent()); if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { nscoord desiredSizeAscent = aDesiredSize.BlockStartAscent(); nscoord desiredSizeDescent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); if (IsToDraw(NOTATION_LONGDIV)) { desiredSizeAscent = std::max(desiredSizeAscent, longdivAscent + leading); desiredSizeDescent = std::max(desiredSizeDescent, longdivDescent + mRuleThickness); } if (IsToDraw(NOTATION_RADICAL)) { desiredSizeAscent = std::max(desiredSizeAscent, radicalAscent + leading); desiredSizeDescent = std::max(desiredSizeDescent, radicalDescent + mRadicalRuleThickness); } aDesiredSize.SetBlockStartAscent(desiredSizeAscent); aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent; } if (IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) || (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) { // center the menclose around the content (vertically) nscoord dy = std::max(aDesiredSize.BlockStartAscent() - bmBase.ascent, aDesiredSize.Height() - aDesiredSize.BlockStartAscent() - bmBase.descent); aDesiredSize.SetBlockStartAscent(bmBase.ascent + dy); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + bmBase.descent + dy; } // Update mBoundingMetrics ascent/descent if (IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) || IsToDraw(NOTATION_UPDIAGONALARROW) || IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX)) mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent(); if (IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) || IsToDraw(NOTATION_UPDIAGONALARROW) || IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX)) mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); // phasorangle notation: // move up from the bottom by the angled line height if (IsToDraw(NOTATION_PHASORANGLE)) mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, 2 * kPhasorangleWidth * mRuleThickness - mBoundingMetrics.descent); aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); if (aPlaceOrigin) { ////////////////// // Set position and size of MathMLChars if (IsToDraw(NOTATION_LONGDIV)) mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left - bmLongdivChar.width, aDesiredSize.BlockStartAscent() - longdivAscent, bmLongdivChar.width, bmLongdivChar.ascent + bmLongdivChar.descent)); if (IsToDraw(NOTATION_RADICAL)) { nscoord dx = (StyleVisibility()->mDirection ? dx_left + bmBase.width : dx_left - bmRadicalChar.width); mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx, aDesiredSize.BlockStartAscent() - radicalAscent, bmRadicalChar.width, bmRadicalChar.ascent + bmRadicalChar.descent)); } mContentWidth = bmBase.width; ////////////////// // Finish reflowing child frames PositionRowChildFrames(dx_left, aDesiredSize.BlockStartAscent()); } return NS_OK; }
void nsTableCellFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); if (aReflowInput.mFlags.mSpecialBSizeReflow) { FirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW); } // see if a special bsize reflow needs to occur due to having a pct height nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput); aStatus = NS_FRAME_COMPLETE; WritingMode wm = aReflowInput.GetWritingMode(); LogicalSize availSize(wm, aReflowInput.AvailableISize(), aReflowInput.AvailableBSize()); LogicalMargin borderPadding = aReflowInput.ComputedLogicalPadding(); LogicalMargin border = GetBorderWidth(wm); borderPadding += border; // reduce available space by insets, if we're in a constrained situation availSize.ISize(wm) -= borderPadding.IStartEnd(wm); if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) { availSize.BSize(wm) -= borderPadding.BStartEnd(wm); } // Try to reflow the child into the available space. It might not // fit or might need continuing. if (availSize.BSize(wm) < 0) { availSize.BSize(wm) = 1; } ReflowOutput kidSize(wm, aDesiredSize.mFlags); kidSize.ClearSize(); SetPriorAvailISize(aReflowInput.AvailableISize()); nsIFrame* firstKid = mFrames.FirstChild(); NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame"); nsTableFrame* tableFrame = GetTableFrame(); if (aReflowInput.mFlags.mSpecialBSizeReflow) { const_cast<ReflowInput&>(aReflowInput). SetComputedBSize(BSize(wm) - borderPadding.BStartEnd(wm)); DISPLAY_REFLOW_CHANGE(); } else if (aPresContext->IsPaginated()) { nscoord computedUnpaginatedBSize = CalcUnpaginatedBSize((nsTableCellFrame&)*this, *tableFrame, borderPadding.BStartEnd(wm)); if (computedUnpaginatedBSize > 0) { const_cast<ReflowInput&>(aReflowInput).SetComputedBSize(computedUnpaginatedBSize); DISPLAY_REFLOW_CHANGE(); } } else { SetHasPctOverBSize(false); } WritingMode kidWM = firstKid->GetWritingMode(); ReflowInput kidReflowInput(aPresContext, aReflowInput, firstKid, availSize.ConvertTo(kidWM, wm)); // Don't be a percent height observer if we're in the middle of // special-bsize reflow, in case we get an accidental NotifyPercentBSize() // call (which we shouldn't honor during special-bsize reflow) if (!aReflowInput.mFlags.mSpecialBSizeReflow) { // mPercentBSizeObserver is for children of cells in quirks mode, // but only those than are tables in standards mode. NeedsToObserve // will determine how far this is propagated to descendants. kidReflowInput.mPercentBSizeObserver = this; } // Don't propagate special bsize reflow state to our kids kidReflowInput.mFlags.mSpecialBSizeReflow = false; if (aReflowInput.mFlags.mSpecialBSizeReflow || FirstInFlow()->HasAnyStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) { // We need to force the kid to have mBResize set if we've had a // special reflow in the past, since the non-special reflow needs to // resize back to what it was without the special bsize reflow. kidReflowInput.SetBResize(true); } nsSize containerSize = aReflowInput.ComputedSizeAsContainerIfConstrained(); LogicalPoint kidOrigin(wm, borderPadding.IStart(wm), borderPadding.BStart(wm)); nsRect origRect = firstKid->GetRect(); nsRect origVisualOverflow = firstKid->GetVisualOverflowRect(); bool firstReflow = firstKid->HasAnyStateBits(NS_FRAME_FIRST_REFLOW); ReflowChild(firstKid, aPresContext, kidSize, kidReflowInput, wm, kidOrigin, containerSize, 0, aStatus); if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it //XXX should paginate overflow as overflow, but not in this patch (bug 379349) NS_FRAME_SET_INCOMPLETE(aStatus); printf("Set table cell incomplete %p\n", static_cast<void*>(this)); } // XXXbz is this invalidate actually needed, really? if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) { InvalidateFrameSubtree(); } #ifdef DEBUG DebugCheckChildSize(firstKid, kidSize); #endif // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode // see testcase "emptyCells.html" nsIFrame* prevInFlow = GetPrevInFlow(); bool isEmpty; if (prevInFlow) { isEmpty = static_cast<nsTableCellFrame*>(prevInFlow)->GetContentEmpty(); } else { isEmpty = !CellHasVisibleContent(kidSize.Height(), tableFrame, firstKid); } SetContentEmpty(isEmpty); // Place the child FinishReflowChild(firstKid, aPresContext, kidSize, &kidReflowInput, wm, kidOrigin, containerSize, 0); nsTableFrame::InvalidateTableFrame(firstKid, origRect, origVisualOverflow, firstReflow); // first, compute the bsize which can be set w/o being restricted by // available bsize LogicalSize cellSize(wm); cellSize.BSize(wm) = kidSize.BSize(wm); if (NS_UNCONSTRAINEDSIZE != cellSize.BSize(wm)) { cellSize.BSize(wm) += borderPadding.BStartEnd(wm); } // next determine the cell's isize cellSize.ISize(wm) = kidSize.ISize(wm); // at this point, we've factored in the cell's style attributes // factor in border and padding if (NS_UNCONSTRAINEDSIZE != cellSize.ISize(wm)) { cellSize.ISize(wm) += borderPadding.IStartEnd(wm); } // set the cell's desired size and max element size aDesiredSize.SetSize(wm, cellSize); // the overflow area will be computed when BlockDirAlignChild() gets called if (aReflowInput.mFlags.mSpecialBSizeReflow) { if (aDesiredSize.BSize(wm) > BSize(wm)) { // set a bit indicating that the pct bsize contents exceeded // the height that they could honor in the pass 2 reflow SetHasPctOverBSize(true); } if (NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize()) { aDesiredSize.BSize(wm) = BSize(wm); } } // If our parent is in initial reflow, it'll handle invalidating our // entire overflow rect. if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW) && nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) { InvalidateFrame(); } // remember the desired size for this reflow SetDesiredSize(aDesiredSize); // Any absolutely-positioned children will get reflowed in // nsFrame::FixupPositionedTableParts in another pass, so propagate our // dirtiness to them before our parent clears our dirty bits. PushDirtyBitToAbsoluteFrames(); NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); }
void nsSubDocumentFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d", aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight())); NS_ASSERTION(aReflowInput.ComputedWidth() != NS_UNCONSTRAINEDSIZE, "Shouldn't have unconstrained stuff here " "thanks to the rules of reflow"); NS_ASSERTION(NS_INTRINSICSIZE != aReflowInput.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(aReflowInput.GetWritingMode(), aReflowInput.ComputedSizeWithBorderPadding()); // "offset" is the offset of our content area from our frame's // top-left corner. nsPoint offset = nsPoint(aReflowInput.ComputedPhysicalBorderPadding().left, aReflowInput.ComputedPhysicalBorderPadding().top); if (mInnerView) { const nsMargin& bp = aReflowInput.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, aReflowInput, aDesiredSize); }
void nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); nsReflowStatus childStatus; mPresentationData.flags &= ~NS_MATHML_ERROR; aDesiredSize.ClearSize(); aDesiredSize.SetBlockStartAscent(0); nsBoundingMetrics bmSqr, bmBase, bmIndex; DrawTarget* drawTarget = aReflowInput.mRenderingContext->GetDrawTarget(); ////////////////// // Reflow Children int32_t count = 0; nsIFrame* baseFrame = nullptr; nsIFrame* indexFrame = nullptr; ReflowOutput baseSize(aReflowInput); ReflowOutput indexSize(aReflowInput); nsIFrame* childFrame = mFrames.FirstChild(); while (childFrame) { // ask our children to compute their bounding metrics ReflowOutput childDesiredSize(aReflowInput, aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS); WritingMode wm = childFrame->GetWritingMode(); LogicalSize availSize = aReflowInput.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; ReflowInput childReflowInput(aPresContext, aReflowInput, childFrame, availSize); ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowInput, childStatus); //NS_ASSERTION(childStatus.IsComplete(), "bad status"); if (0 == count) { // base baseFrame = childFrame; baseSize = childDesiredSize; bmBase = childDesiredSize.mBoundingMetrics; } else if (1 == count) { // index indexFrame = childFrame; indexSize = childDesiredSize; bmIndex = childDesiredSize.mBoundingMetrics; } count++; childFrame = childFrame->GetNextSibling(); } if (2 != count) { // report an error, encourage people to get their markups in order ReportChildCountError(); ReflowError(drawTarget, aDesiredSize); aStatus.Reset(); NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); // Call DidReflow() for the child frames we successfully did reflow. DidReflowChildren(mFrames.FirstChild(), childFrame); return; } //////////// // Prepare the radical symbol and the overline bar float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation); nscoord ruleThickness, leading, psi; GetRadicalParameters(fm, StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK, ruleThickness, leading, psi); // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131) char16_t one = '1'; nsBoundingMetrics bmOne = nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, drawTarget); if (bmOne.ascent > bmBase.ascent) psi += bmOne.ascent - bmBase.ascent; // make sure that the rule appears on on screen nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); if (ruleThickness < onePixel) { ruleThickness = onePixel; } // adjust clearance psi to get an exact number of pixels -- this // gives a nicer & uniform look on stacked radicals (bug 130282) nscoord delta = psi % onePixel; if (delta) psi += onePixel - delta; // round up // Stretch the radical symbol to the appropriate height if it is not big enough. nsBoundingMetrics contSize = bmBase; contSize.descent = bmBase.ascent + bmBase.descent + psi; contSize.ascent = ruleThickness; // height(radical) should be >= height(base) + psi + ruleThickness nsBoundingMetrics radicalSize; mSqrChar.Stretch(aPresContext, drawTarget, fontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL, contSize, radicalSize, NS_STRETCH_LARGER, StyleVisibility()->mDirection); // radicalSize have changed at this point, and should match with // the bounding metrics of the char mSqrChar.GetBoundingMetrics(bmSqr); // Update the desired size for the container (like msqrt, index is not yet included) // the baseline will be that of the base. mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness; mBoundingMetrics.descent = std::max(bmBase.descent, (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent)); mBoundingMetrics.width = bmSqr.width + bmBase.width; mBoundingMetrics.leftBearing = bmSqr.leftBearing; mBoundingMetrics.rightBearing = bmSqr.width + std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + std::max(baseSize.Height() - baseSize.BlockStartAscent(), mBoundingMetrics.descent + ruleThickness); aDesiredSize.Width() = mBoundingMetrics.width; ///////////// // Re-adjust the desired size to include the index. // the index is raised by some fraction of the height // of the radical, see \mroot macro in App. B, TexBook float raiseIndexPercent = 0.6f; gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont(); if (mathFont) { raiseIndexPercent = mathFont->MathTable()-> Constant(gfxMathTable::RadicalDegreeBottomRaisePercent); } nscoord raiseIndexDelta = NSToCoordRound(raiseIndexPercent * (bmSqr.ascent + bmSqr.descent)); nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical - (bmSqr.ascent + bmSqr.descent) // to bottom of radical + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index nscoord indexClearance = 0; if (mBoundingMetrics.ascent < indexRaisedAscent) { indexClearance = indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index mBoundingMetrics.ascent = indexRaisedAscent; nscoord descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + descent; } nscoord dxIndex, dxSqr; GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr); mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width; mBoundingMetrics.leftBearing = std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing); mBoundingMetrics.rightBearing = dxSqr + bmSqr.width + std::max(bmBase.width, bmBase.rightBearing); aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; GatherAndStoreOverflow(&aDesiredSize); // place the index nscoord dx = dxIndex; nscoord dy = aDesiredSize.BlockStartAscent() - (indexRaisedAscent + indexSize.BlockStartAscent() - bmIndex.ascent); FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr, MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx), dy, 0); // place the radical symbol and the radical bar dx = dxSqr; dy = indexClearance + leading; // leave a leading at the top mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx), dy, bmSqr.width, bmSqr.ascent + bmSqr.descent)); dx += bmSqr.width; mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx), dy, bmBase.width, ruleThickness); // place the base dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent(); FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr, MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx), dy, 0); mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); aStatus.Reset(); NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); }
/* virtual */ nsresult nsMathMLmunderoverFrame::Place(DrawTarget* aDrawTarget, bool aPlaceOrigin, ReflowOutput& aDesiredSize) { float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) && StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) { //place like sub sup or subsup if (mContent->IsMathMLElement(nsGkAtoms::munderover_)) { return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aDrawTarget, aPlaceOrigin, aDesiredSize, this, 0, 0, fontSizeInflation); } else if (mContent->IsMathMLElement( nsGkAtoms::munder_)) { return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aDrawTarget, aPlaceOrigin, aDesiredSize, this, 0, 0, fontSizeInflation); } else { NS_ASSERTION(mContent->IsMathMLElement(nsGkAtoms::mover_), "mContent->NodeInfo()->NameAtom() not recognized"); return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aDrawTarget, aPlaceOrigin, aDesiredSize, this, 0, 0, fontSizeInflation); } } //////////////////////////////////// // Get the children's desired sizes nsBoundingMetrics bmBase, bmUnder, bmOver; ReflowOutput baseSize(aDesiredSize.GetWritingMode()); ReflowOutput underSize(aDesiredSize.GetWritingMode()); ReflowOutput overSize(aDesiredSize.GetWritingMode()); nsIFrame* overFrame = nullptr; nsIFrame* underFrame = nullptr; nsIFrame* baseFrame = mFrames.FirstChild(); underSize.SetBlockStartAscent(0); overSize.SetBlockStartAscent(0); bool haveError = false; if (baseFrame) { if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_, nsGkAtoms::munderover_)) { underFrame = baseFrame->GetNextSibling(); } else if (mContent->IsMathMLElement(nsGkAtoms::mover_)) { overFrame = baseFrame->GetNextSibling(); } } if (underFrame && mContent->IsMathMLElement(nsGkAtoms::munderover_)) { overFrame = underFrame->GetNextSibling(); } if (mContent->IsMathMLElement(nsGkAtoms::munder_)) { if (!baseFrame || !underFrame || underFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order haveError = true; } } if (mContent->IsMathMLElement(nsGkAtoms::mover_)) { if (!baseFrame || !overFrame || overFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order haveError = true; } } if (mContent->IsMathMLElement(nsGkAtoms::munderover_)) { if (!baseFrame || !underFrame || !overFrame || overFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order haveError = true; } } if (haveError) { if (aPlaceOrigin) { ReportChildCountError(); } return ReflowError(aDrawTarget, aDesiredSize); } GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); if (underFrame) { GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder); } if (overFrame) { GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver); } nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); //////////////////// // Place Children RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation); nscoord xHeight = fm->XHeight(); nscoord oneDevPixel = fm->AppUnitsPerDevPixel(); gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont(); nscoord ruleThickness; GetRuleThickness (aDrawTarget, fm, ruleThickness); nscoord correction = 0; GetItalicCorrection (bmBase, correction); // there are 2 different types of placement depending on // whether we want an accented under or not nscoord underDelta1 = 0; // gap between base and underscript nscoord underDelta2 = 0; // extra space beneath underscript if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) { // Rule 13a, App. G, TeXbook nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy; GetBigOpSpacings (fm, dummy, bigOpSpacing2, dummy, bigOpSpacing4, bigOpSpacing5); if (mathFont) { // XXXfredw The Open Type MATH table has some StretchStack* parameters // that we may use when the base is a stretchy horizontal operator. See // bug 963131. bigOpSpacing2 = mathFont->GetMathConstant(gfxFontEntry::LowerLimitGapMin, oneDevPixel); bigOpSpacing4 = mathFont->GetMathConstant(gfxFontEntry::LowerLimitBaselineDropMin, oneDevPixel); bigOpSpacing5 = 0; } underDelta1 = std::max(bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent)); underDelta2 = bigOpSpacing5; } else { // No corresponding rule in TeXbook - we are on our own here // XXX tune the gap delta between base and underscript // XXX Should we use Rule 10 like \underline does? // XXXfredw Perhaps use the Underbar* parameters of the MATH table. See // bug 963125. underDelta1 = ruleThickness + onePixel/2; underDelta2 = ruleThickness; } // empty under? if (!(bmUnder.ascent + bmUnder.descent)) { underDelta1 = 0; underDelta2 = 0; } nscoord overDelta1 = 0; // gap between base and overscript nscoord overDelta2 = 0; // extra space above overscript if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) { // Rule 13a, App. G, TeXbook // XXXfredw The Open Type MATH table has some StretchStack* parameters // that we may use when the base is a stretchy horizontal operator. See // bug 963131. nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy; GetBigOpSpacings (fm, bigOpSpacing1, dummy, bigOpSpacing3, dummy, bigOpSpacing5); if (mathFont) { // XXXfredw The Open Type MATH table has some StretchStack* parameters // that we may use when the base is a stretchy horizontal operator. See // bug 963131. bigOpSpacing1 = mathFont->GetMathConstant(gfxFontEntry::UpperLimitGapMin, oneDevPixel); bigOpSpacing3 = mathFont->GetMathConstant(gfxFontEntry::UpperLimitBaselineRiseMin, oneDevPixel); bigOpSpacing5 = 0; } overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - bmOver.descent)); overDelta2 = bigOpSpacing5; // XXX This is not a TeX rule... // delta1 (as computed abvove) can become really big when bmOver.descent is // negative, e.g., if the content is &OverBar. In such case, we use the height if (bmOver.descent < 0) overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - (bmOver.ascent + bmOver.descent))); } else { // Rule 12, App. G, TeXbook // We are going to modify this rule to make it more general. // The idea behind Rule 12 in the TeXBook is to keep the accent // as close to the base as possible, while ensuring that the // distance between the *baseline* of the accent char and // the *baseline* of the base is atleast x-height. // The idea is that for normal use, we would like all the accents // on a line to line up atleast x-height above the baseline // if possible. // When the ascent of the base is >= x-height, // the baseline of the accent char is placed just above the base // (specifically, the baseline of the accent char is placed // above the baseline of the base by the ascent of the base). // For ease of implementation, // this assumes that the font-designer designs accents // in such a way that the bottom of the accent is atleast x-height // above its baseline, otherwise there will be collisions // with the base. Also there should be proper padding between // the bottom of the accent char and its baseline. // The above rule may not be obvious from a first // reading of rule 12 in the TeXBook !!! // The mathml <mover> tag can use accent chars that // do not follow this convention. So we modify TeX's rule // so that TeX's rule gets subsumed for accents that follow // TeX's convention, // while also allowing accents that do not follow the convention : // we try to keep the *bottom* of the accent char atleast x-height // from the baseline of the base char. we also slap on an extra // padding between the accent and base chars. overDelta1 = ruleThickness + onePixel/2; nscoord accentBaseHeight = xHeight; if (mathFont) { accentBaseHeight = mathFont->GetMathConstant(gfxFontEntry::AccentBaseHeight, oneDevPixel); } if (bmBase.ascent < accentBaseHeight) { // also ensure at least accentBaseHeight above the baseline of the base overDelta1 += accentBaseHeight - bmBase.ascent; } overDelta2 = ruleThickness; } // empty over? if (!(bmOver.ascent + bmOver.descent)) { overDelta1 = 0; overDelta2 = 0; } nscoord dxBase = 0, dxOver = 0, dxUnder = 0; nsAutoString valueAlign; enum { center, left, right } alignPosition = center; if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, valueAlign)) { if (valueAlign.EqualsLiteral("left")) { alignPosition = left; } else if (valueAlign.EqualsLiteral("right")) { alignPosition = right; } } ////////// // pass 1, do what <mover> does: attach the overscript on the base // Ad-hoc - This is to override fonts which have ready-made _accent_ // glyphs with negative lbearing and rbearing. We want to position // the overscript ourselves nscoord overWidth = bmOver.width; if (!overWidth && (bmOver.rightBearing - bmOver.leftBearing > 0)) { overWidth = bmOver.rightBearing - bmOver.leftBearing; dxOver = -bmOver.leftBearing; } if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) { mBoundingMetrics.width = bmBase.width; if (alignPosition == center) { dxOver += correction; } } else { mBoundingMetrics.width = std::max(bmBase.width, overWidth); if (alignPosition == center) { dxOver += correction/2; } } if (alignPosition == center) { dxOver += (mBoundingMetrics.width - overWidth)/2; dxBase = (mBoundingMetrics.width - bmBase.width)/2; } else if (alignPosition == right) { dxOver += mBoundingMetrics.width - overWidth; dxBase = mBoundingMetrics.width - bmBase.width; } mBoundingMetrics.ascent = bmBase.ascent + overDelta1 + bmOver.ascent + bmOver.descent; mBoundingMetrics.descent = bmBase.descent; mBoundingMetrics.leftBearing = std::min(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing); mBoundingMetrics.rightBearing = std::max(dxBase + bmBase.rightBearing, dxOver + bmOver.rightBearing); ////////// // pass 2, do what <munder> does: attach the underscript on the previous // result. We conceptually view the previous result as an "anynomous base" // from where to attach the underscript. Hence if the underscript is empty, // we should end up like <mover>. If the overscript is empty, we should // end up like <munder>. nsBoundingMetrics bmAnonymousBase = mBoundingMetrics; nscoord ascentAnonymousBase = std::max(mBoundingMetrics.ascent + overDelta2, overSize.BlockStartAscent() + bmOver.descent + overDelta1 + bmBase.ascent); ascentAnonymousBase = std::max(ascentAnonymousBase, baseSize.BlockStartAscent()); // Width of non-spacing marks is zero so use left and right bearing. nscoord underWidth = bmUnder.width; if (!underWidth) { underWidth = bmUnder.rightBearing - bmUnder.leftBearing; dxUnder = -bmUnder.leftBearing; } nscoord maxWidth = std::max(bmAnonymousBase.width, underWidth); if (alignPosition == center && !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) { GetItalicCorrection(bmAnonymousBase, correction); dxUnder += -correction/2; } nscoord dxAnonymousBase = 0; if (alignPosition == center) { dxUnder += (maxWidth - underWidth)/2; dxAnonymousBase = (maxWidth - bmAnonymousBase.width)/2; } else if (alignPosition == right) { dxUnder += maxWidth - underWidth; dxAnonymousBase = maxWidth - bmAnonymousBase.width; } // adjust the offsets of the real base and overscript since their // final offsets should be relative to us... dxOver += dxAnonymousBase; dxBase += dxAnonymousBase; mBoundingMetrics.width = std::max(dxAnonymousBase + bmAnonymousBase.width, dxUnder + bmUnder.width); // At this point, mBoundingMetrics.ascent = bmAnonymousBase.ascent mBoundingMetrics.descent = bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + bmUnder.descent; mBoundingMetrics.leftBearing = std::min(dxAnonymousBase + bmAnonymousBase.leftBearing, dxUnder + bmUnder.leftBearing); mBoundingMetrics.rightBearing = std::max(dxAnonymousBase + bmAnonymousBase.rightBearing, dxUnder + bmUnder.rightBearing); aDesiredSize.SetBlockStartAscent(ascentAnonymousBase); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + std::max(mBoundingMetrics.descent + underDelta2, bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + underSize.Height() - underSize.BlockStartAscent()); aDesiredSize.Height() = std::max(aDesiredSize.Height(), aDesiredSize.BlockStartAscent() + baseSize.Height() - baseSize.BlockStartAscent()); aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); if (aPlaceOrigin) { nscoord dy; // place overscript if (overFrame) { dy = aDesiredSize.BlockStartAscent() - mBoundingMetrics.ascent + bmOver.ascent - overSize.BlockStartAscent(); FinishReflowChild (overFrame, PresContext(), overSize, nullptr, dxOver, dy, 0); } // place base dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent(); FinishReflowChild (baseFrame, PresContext(), baseSize, nullptr, dxBase, dy, 0); // place underscript if (underFrame) { dy = aDesiredSize.BlockStartAscent() + mBoundingMetrics.descent - bmUnder.descent - underSize.BlockStartAscent(); FinishReflowChild (underFrame, PresContext(), underSize, nullptr, dxUnder, dy, 0); } } return NS_OK; }
void nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsSVGOuterSVGFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsSVGOuterSVGFrame::Reflow: availSize=%d,%d", aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight())); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; aDesiredSize.Width() = aReflowInput.ComputedWidth() + aReflowInput.ComputedPhysicalBorderPadding().LeftRight(); aDesiredSize.Height() = aReflowInput.ComputedHeight() + aReflowInput.ComputedPhysicalBorderPadding().TopBottom(); NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages."); SVGSVGElement *svgElem = static_cast<SVGSVGElement*>(mContent); nsSVGOuterSVGAnonChildFrame *anonKid = static_cast<nsSVGOuterSVGAnonChildFrame*>(PrincipalChildList().FirstChild()); 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(aReflowInput.ComputedWidth()), nsPresContext::AppUnitsToFloatCSSPixels(aReflowInput.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 = PrincipalChildList().FirstChild(); anonChild->AddStateBits(NS_FRAME_IS_DIRTY); for (nsIFrame* child : anonChild->PrincipalChildList()) { 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(); MOZ_ASSERT(!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, aReflowInput, aDesiredSize); }
void nsVideoFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsVideoFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsVideoFrame::Reflow: availSize=%d,%d", aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight())); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; const WritingMode myWM = aReflowInput.GetWritingMode(); nscoord contentBoxBSize = aReflowInput.ComputedBSize(); const nscoord borderBoxISize = aReflowInput.ComputedISize() + aReflowInput.ComputedLogicalBorderPadding().IStartEnd(myWM); const bool isBSizeShrinkWrapping = (contentBoxBSize == NS_INTRINSICSIZE); nscoord borderBoxBSize; if (!isBSizeShrinkWrapping) { borderBoxBSize = contentBoxBSize + aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM); } nsMargin borderPadding = aReflowInput.ComputedPhysicalBorderPadding(); // Reflow the child frames. We may have up to three: an image // frame (for the poster image), a container frame for the controls, // and a container frame for the caption. for (nsIFrame* child : mFrames) { nsSize oldChildSize = child->GetSize(); if (child->GetContent() == mPosterImage) { // Reflow the poster frame. nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child); ReflowOutput kidDesiredSize(aReflowInput); WritingMode wm = imageFrame->GetWritingMode(); LogicalSize availableSize = aReflowInput.AvailableSize(wm); LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()). ConvertTo(wm, aMetrics.GetWritingMode()); ReflowInput kidReflowInput(aPresContext, aReflowInput, imageFrame, availableSize, &cbSize); nsRect posterRenderRect; if (ShouldDisplayPoster()) { posterRenderRect = nsRect(nsPoint(borderPadding.left, borderPadding.top), nsSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight())); } kidReflowInput.SetComputedWidth(posterRenderRect.width); kidReflowInput.SetComputedHeight(posterRenderRect.height); ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowInput, posterRenderRect.x, posterRenderRect.y, 0, aStatus); FinishReflowChild(imageFrame, aPresContext, kidDesiredSize, &kidReflowInput, posterRenderRect.x, posterRenderRect.y, 0); // Android still uses XUL media controls & hence needs this XUL-friendly // custom reflow code. This will go away in bug 1310907. #ifdef ANDROID } else if (child->GetContent() == mVideoControls) { // Reflow the video controls frame. nsBoxLayoutState boxState(PresContext(), aReflowInput.mRenderingContext); nsBoxFrame::LayoutChildAt(boxState, child, nsRect(borderPadding.left, borderPadding.top, aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight())); #endif // ANDROID } else if (child->GetContent() == mCaptionDiv || child->GetContent() == mVideoControls) { // Reflow the caption and control bar frames. WritingMode wm = child->GetWritingMode(); LogicalSize availableSize = aReflowInput.ComputedSize(wm); availableSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; ReflowInput kidReflowInput(aPresContext, aReflowInput, child, availableSize); ReflowOutput kidDesiredSize(kidReflowInput); ReflowChild(child, aPresContext, kidDesiredSize, kidReflowInput, borderPadding.left, borderPadding.top, 0, aStatus); if (child->GetContent() == mVideoControls && isBSizeShrinkWrapping) { // Resolve our own BSize based on the controls' size in the same axis. contentBoxBSize = myWM.IsOrthogonalTo(wm) ? kidDesiredSize.ISize(wm) : kidDesiredSize.BSize(wm); } FinishReflowChild(child, aPresContext, kidDesiredSize, &kidReflowInput, borderPadding.left, borderPadding.top, 0); } if (child->GetContent() == mVideoControls && child->GetSize() != oldChildSize) { RefPtr<Runnable> event = new DispatchResizeToControls(child->GetContent()); nsContentUtils::AddScriptRunner(event); } } if (isBSizeShrinkWrapping) { if (contentBoxBSize == NS_INTRINSICSIZE) { // We didn't get a BSize from our intrinsic size/ratio, nor did we // get one from our controls. Just use BSize of 0. contentBoxBSize = 0; } contentBoxBSize = NS_CSS_MINMAX(contentBoxBSize, aReflowInput.ComputedMinBSize(), aReflowInput.ComputedMaxBSize()); borderBoxBSize = contentBoxBSize + aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM); } LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize); aMetrics.SetSize(myWM, logicalDesiredSize); 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, aReflowInput, aMetrics); }
nsresult nsMathMLmfracFrame::PlaceInternal(DrawTarget* aDrawTarget, bool aPlaceOrigin, ReflowOutput& aDesiredSize, bool aWidthOnly) { //////////////////////////////////// // Get the children's desired sizes nsBoundingMetrics bmNum, bmDen; ReflowOutput sizeNum(aDesiredSize.GetWritingMode()); ReflowOutput sizeDen(aDesiredSize.GetWritingMode()); nsIFrame* frameDen = nullptr; nsIFrame* frameNum = mFrames.FirstChild(); if (frameNum) frameDen = frameNum->GetNextSibling(); if (!frameNum || !frameDen || frameDen->GetNextSibling()) { // report an error, encourage people to get their markups in order if (aPlaceOrigin) { ReportChildCountError(); } return ReflowError(aDrawTarget, aDesiredSize); } GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum); GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen); nsPresContext* presContext = PresContext(); nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation); nscoord defaultRuleThickness, axisHeight; nscoord oneDevPixel = fm->AppUnitsPerDevPixel(); gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont(); if (mathFont) { defaultRuleThickness = mathFont->GetMathConstant(gfxFontEntry::FractionRuleThickness, oneDevPixel); } else { GetRuleThickness(aDrawTarget, fm, defaultRuleThickness); } GetAxisHeight(aDrawTarget, fm, axisHeight); bool outermostEmbellished = false; if (mEmbellishData.coreFrame) { nsEmbellishData parentData; GetEmbellishDataFrom(GetParent(), parentData); outermostEmbellished = parentData.coreFrame != mEmbellishData.coreFrame; } // see if the linethickness attribute is there nsAutoString value; mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::linethickness_, value); mLineThickness = CalcLineThickness(presContext, mStyleContext, value, onePixel, defaultRuleThickness, fontSizeInflation); // bevelled attribute mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::bevelled_, value); mIsBevelled = value.EqualsLiteral("true"); bool displayStyle = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK; if (!mIsBevelled) { mLineRect.height = mLineThickness; // by default, leave at least one-pixel padding at either end, and add // lspace & rspace that may come from <mo> if we are an outermost // embellished container (we fetch values from the core since they may use // units that depend on style data, and style changes could have occurred // in the core since our last visit there) nscoord leftSpace = onePixel; nscoord rightSpace = onePixel; if (outermostEmbellished) { nsEmbellishData coreData; GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData); leftSpace += StyleVisibility()->mDirection ? coreData.trailingSpace : coreData.leadingSpace; rightSpace += StyleVisibility()->mDirection ? coreData.leadingSpace : coreData.trailingSpace; } nscoord actualRuleThickness = mLineThickness; ////////////////// // Get shifts nscoord numShift = 0; nscoord denShift = 0; // Rule 15b, App. G, TeXbook nscoord numShift1, numShift2, numShift3; nscoord denShift1, denShift2; GetNumeratorShifts(fm, numShift1, numShift2, numShift3); GetDenominatorShifts(fm, denShift1, denShift2); if (0 == actualRuleThickness) { numShift = displayStyle ? numShift1 : numShift3; denShift = displayStyle ? denShift1 : denShift2; if (mathFont) { numShift = mathFont-> GetMathConstant(displayStyle ? gfxFontEntry::StackTopDisplayStyleShiftUp : gfxFontEntry::StackTopShiftUp, oneDevPixel); denShift = mathFont-> GetMathConstant(displayStyle ? gfxFontEntry::StackBottomDisplayStyleShiftDown : gfxFontEntry::StackBottomShiftDown, oneDevPixel); } } else { numShift = displayStyle ? numShift1 : numShift2; denShift = displayStyle ? denShift1 : denShift2; if (mathFont) { numShift = mathFont-> GetMathConstant(displayStyle ? gfxFontEntry::FractionNumeratorDisplayStyleShiftUp : gfxFontEntry::FractionNumeratorShiftUp, oneDevPixel); denShift = mathFont-> GetMathConstant( displayStyle ? gfxFontEntry::FractionDenominatorDisplayStyleShiftDown : gfxFontEntry::FractionDenominatorShiftDown, oneDevPixel); } } if (0 == actualRuleThickness) { // Rule 15c, App. G, TeXbook // min clearance between numerator and denominator nscoord minClearance = displayStyle ? 7 * defaultRuleThickness : 3 * defaultRuleThickness; if (mathFont) { minClearance = mathFont->GetMathConstant(displayStyle ? gfxFontEntry::StackDisplayStyleGapMin : gfxFontEntry::StackGapMin, oneDevPixel); } // Factor in axis height // http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2 numShift += axisHeight; denShift += axisHeight; nscoord actualClearance = (numShift - bmNum.descent) - (bmDen.ascent - denShift); // actualClearance should be >= minClearance if (actualClearance < minClearance) { nscoord halfGap = (minClearance - actualClearance)/2; numShift += halfGap; denShift += halfGap; } } else { // Rule 15d, App. G, TeXbook // min clearance between numerator or denominator and middle of bar // TeX has a different interpretation of the thickness. // Try $a \above10pt b$ to see. Here is what TeX does: // minClearance = displayStyle ? // 3 * actualRuleThickness : actualRuleThickness; // we slightly depart from TeX here. We use the defaultRuleThickness instead // of the value coming from the linethickness attribute, i.e., we recover what // TeX does if the user hasn't set linethickness. But when the linethickness // is set, we avoid the wide gap problem. nscoord minClearanceNum = displayStyle ? 3 * defaultRuleThickness : defaultRuleThickness + onePixel; nscoord minClearanceDen = minClearanceNum; if (mathFont) { minClearanceNum = mathFont-> GetMathConstant(displayStyle ? gfxFontEntry::FractionNumDisplayStyleGapMin : gfxFontEntry::FractionNumeratorGapMin, oneDevPixel); minClearanceDen = mathFont-> GetMathConstant(displayStyle ? gfxFontEntry::FractionDenomDisplayStyleGapMin : gfxFontEntry::FractionDenominatorGapMin, oneDevPixel); } // adjust numShift to maintain minClearanceNum if needed nscoord actualClearanceNum = (numShift - bmNum.descent) - (axisHeight + actualRuleThickness/2); if (actualClearanceNum < minClearanceNum) { numShift += (minClearanceNum - actualClearanceNum); } // adjust denShift to maintain minClearanceDen if needed nscoord actualClearanceDen = (axisHeight - actualRuleThickness/2) - (bmDen.ascent - denShift); if (actualClearanceDen < minClearanceDen) { denShift += (minClearanceDen - actualClearanceDen); } } ////////////////// // Place Children // XXX Need revisiting the width. TeX uses the exact width // e.g. in $$\huge\frac{\displaystyle\int}{i}$$ nscoord width = std::max(bmNum.width, bmDen.width); nscoord dxNum = leftSpace + (width - sizeNum.Width())/2; nscoord dxDen = leftSpace + (width - sizeDen.Width())/2; width += leftSpace + rightSpace; // see if the numalign attribute is there mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::numalign_, value); if (value.EqualsLiteral("left")) dxNum = leftSpace; else if (value.EqualsLiteral("right")) dxNum = width - rightSpace - sizeNum.Width(); // see if the denomalign attribute is there mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::denomalign_, value); if (value.EqualsLiteral("left")) dxDen = leftSpace; else if (value.EqualsLiteral("right")) dxDen = width - rightSpace - sizeDen.Width(); mBoundingMetrics.rightBearing = std::max(dxNum + bmNum.rightBearing, dxDen + bmDen.rightBearing); if (mBoundingMetrics.rightBearing < width - rightSpace) mBoundingMetrics.rightBearing = width - rightSpace; mBoundingMetrics.leftBearing = std::min(dxNum + bmNum.leftBearing, dxDen + bmDen.leftBearing); if (mBoundingMetrics.leftBearing > leftSpace) mBoundingMetrics.leftBearing = leftSpace; mBoundingMetrics.ascent = bmNum.ascent + numShift; mBoundingMetrics.descent = bmDen.descent + denShift; mBoundingMetrics.width = width; aDesiredSize.SetBlockStartAscent(sizeNum.BlockStartAscent() + numShift); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + sizeDen.Height() - sizeDen.BlockStartAscent() + denShift; aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); if (aPlaceOrigin) { nscoord dy; // place numerator dy = 0; FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dxNum, dy, 0); // place denominator dy = aDesiredSize.Height() - sizeDen.Height(); FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dxDen, dy, 0); // place the fraction bar - dy is top of bar dy = aDesiredSize.BlockStartAscent() - (axisHeight + actualRuleThickness/2); mLineRect.SetRect(leftSpace, dy, width - (leftSpace + rightSpace), actualRuleThickness); } } else { nscoord numShift = 0.0; nscoord denShift = 0.0; nscoord padding = 3 * defaultRuleThickness; nscoord slashRatio = 3; // Define the constant used in the expression of the maximum width nscoord em = fm->EmHeight(); nscoord slashMaxWidthConstant = 2 * em; // For large line thicknesses the minimum slash height is limited to the // largest expected height of a fraction nscoord slashMinHeight = slashRatio * std::min(2 * mLineThickness, slashMaxWidthConstant); nscoord leadingSpace = padding; nscoord trailingSpace = padding; if (outermostEmbellished) { nsEmbellishData coreData; GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData); leadingSpace += coreData.leadingSpace; trailingSpace += coreData.trailingSpace; } nscoord delta; // ___________ // | | / // {|-NUMERATOR-| / // {|___________| S // { L // numShift{ A // ------------------------------------------------------- baseline // S _____________ } denShift // H | |} // / |-DENOMINATOR-|} // / |_____________| // // first, ensure that the top of the numerator is at least as high as the // top of the denominator (and the reverse for the bottoms) delta = std::max(bmDen.ascent - bmNum.ascent, bmNum.descent - bmDen.descent) / 2; if (delta > 0) { numShift += delta; denShift += delta; } if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) { delta = std::min(bmDen.ascent + bmDen.descent, bmNum.ascent + bmNum.descent) / 2; numShift += delta; denShift += delta; } else { nscoord xHeight = fm->XHeight(); numShift += xHeight / 2; denShift += xHeight / 4; } // Set the ascent/descent of our BoundingMetrics. mBoundingMetrics.ascent = bmNum.ascent + numShift; mBoundingMetrics.descent = bmDen.descent + denShift; // At this point the height of the slash is // mBoundingMetrics.ascent + mBoundingMetrics.descent // Ensure that it is greater than slashMinHeight delta = (slashMinHeight - (mBoundingMetrics.ascent + mBoundingMetrics.descent)) / 2; if (delta > 0) { mBoundingMetrics.ascent += delta; mBoundingMetrics.descent += delta; } // Set the width of the slash if (aWidthOnly) { mLineRect.width = mLineThickness + slashMaxWidthConstant; } else { mLineRect.width = mLineThickness + std::min(slashMaxWidthConstant, (mBoundingMetrics.ascent + mBoundingMetrics.descent) / slashRatio); } // Set horizontal bounding metrics if (StyleVisibility()->mDirection) { mBoundingMetrics.leftBearing = trailingSpace + bmDen.leftBearing; mBoundingMetrics.rightBearing = trailingSpace + bmDen.width + mLineRect.width + bmNum.rightBearing; } else { mBoundingMetrics.leftBearing = leadingSpace + bmNum.leftBearing; mBoundingMetrics.rightBearing = leadingSpace + bmNum.width + mLineRect.width + bmDen.rightBearing; } mBoundingMetrics.width = leadingSpace + bmNum.width + mLineRect.width + bmDen.width + trailingSpace; // Set aDesiredSize aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + padding); aDesiredSize.Height() = mBoundingMetrics.ascent + mBoundingMetrics.descent + 2 * padding; aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); if (aPlaceOrigin) { nscoord dx, dy; // place numerator dx = MirrorIfRTL(aDesiredSize.Width(), sizeNum.Width(), leadingSpace); dy = aDesiredSize.BlockStartAscent() - numShift - sizeNum.BlockStartAscent(); FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dx, dy, 0); // place the fraction bar dx = MirrorIfRTL(aDesiredSize.Width(), mLineRect.width, leadingSpace + bmNum.width); dy = aDesiredSize.BlockStartAscent() - mBoundingMetrics.ascent; mLineRect.SetRect(dx, dy, mLineRect.width, aDesiredSize.Height() - 2 * padding); // place denominator dx = MirrorIfRTL(aDesiredSize.Width(), sizeDen.Width(), leadingSpace + bmNum.width + mLineRect.width); dy = aDesiredSize.BlockStartAscent() + denShift - sizeDen.BlockStartAscent(); FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dx, dy, 0); } } return NS_OK; }