void nsSVGForeignObjectFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY), "Should not have been called"); // Only InvalidateAndScheduleBoundsUpdate marks us with NS_FRAME_IS_DIRTY, // so if that bit is still set we still have a resize pending. If we hit // this assertion, then we should get the presShell to skip reflow roots // that have a dirty parent since a reflow is going to come via the // reflow root's parent anyway. NS_ASSERTION(!(GetStateBits() & NS_FRAME_IS_DIRTY), "Reflowing while a resize is pending is wasteful"); // ReflowSVG makes sure mRect is up to date before we're called. NS_ASSERTION(!aReflowState.parentReflowState, "should only get reflow from being reflow root"); NS_ASSERTION(aReflowState.ComputedWidth() == GetSize().width && aReflowState.ComputedHeight() == GetSize().height, "reflow roots should be reflowed at existing size and " "svg.css should ensure we have no padding/border/margin"); DoReflow(); WritingMode wm = aReflowState.GetWritingMode(); LogicalSize finalSize(wm, aReflowState.ComputedISize(), aReflowState.ComputedBSize()); aDesiredSize.SetSize(wm, finalSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); aStatus = NS_FRAME_COMPLETE; }
void nsGridContainerFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); if (IsFrameTreeTooDeep(aReflowState, aDesiredSize, aStatus)) { return; } #ifdef DEBUG SanityCheckAnonymousGridItems(); #endif // DEBUG LogicalMargin bp = aReflowState.ComputedLogicalBorderPadding(); bp.ApplySkipSides(GetLogicalSkipSides()); nscoord contentBSize = GetEffectiveComputedBSize(aReflowState); if (contentBSize == NS_AUTOHEIGHT) { contentBSize = 0; } WritingMode wm = aReflowState.GetWritingMode(); LogicalSize finalSize(wm, aReflowState.ComputedISize() + bp.IStartEnd(wm), contentBSize + bp.BStartEnd(wm)); aDesiredSize.SetSize(wm, finalSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsRangeFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsRangeFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_ASSERTION(mTrackDiv, "::-moz-range-track div must exist!"); NS_ASSERTION(mProgressDiv, "::-moz-range-progress div must exist!"); NS_ASSERTION(mThumbDiv, "::-moz-range-thumb div must exist!"); NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(), "nsRangeFrame should not have continuations; if it does we " "need to call RegUnregAccessKey only for the first."); if (mState & NS_FRAME_FIRST_REFLOW) { nsFormControlFrame::RegUnRegAccessKey(this, true); } WritingMode wm = aReflowState.GetWritingMode(); nscoord computedBSize = aReflowState.ComputedBSize(); if (computedBSize == NS_AUTOHEIGHT) { computedBSize = 0; } LogicalSize finalSize(wm, aReflowState.ComputedISize() + aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm), computedBSize + aReflowState.ComputedLogicalBorderPadding().BStartEnd(wm)); aDesiredSize.SetSize(wm, finalSize); ReflowAnonymousContent(aPresContext, aDesiredSize, aReflowState); aDesiredSize.SetOverflowAreasToDesiredBounds(); nsIFrame* trackFrame = mTrackDiv->GetPrimaryFrame(); if (trackFrame) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, trackFrame); } nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame(); if (rangeProgressFrame) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, rangeProgressFrame); } nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); if (thumbFrame) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, thumbFrame); } FinishAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsGridContainerFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); if (IsFrameTreeTooDeep(aReflowState, aDesiredSize, aStatus)) { return; } #ifdef DEBUG SanityCheckAnonymousGridItems(); #endif // DEBUG LogicalMargin bp = aReflowState.ComputedLogicalBorderPadding(); bp.ApplySkipSides(GetLogicalSkipSides()); const nsStylePosition* stylePos = aReflowState.mStylePosition; InitImplicitNamedAreas(stylePos); GridItemCSSOrderIterator normalFlowIter(this, kPrincipalList); mIsNormalFlowInCSSOrder = normalFlowIter.ItemsAreAlreadyInOrder(); PlaceGridItems(normalFlowIter, stylePos); nsAutoTArray<TrackSize, 32> colSizes; nsAutoTArray<TrackSize, 32> rowSizes; WritingMode wm = aReflowState.GetWritingMode(); const nscoord computedBSize = aReflowState.ComputedBSize(); const nscoord computedISize = aReflowState.ComputedISize(); LogicalSize percentageBasis(wm, computedISize, computedBSize == NS_AUTOHEIGHT ? 0 : computedBSize); CalculateTrackSizes(percentageBasis, stylePos, colSizes, rowSizes); nscoord bSize = 0; if (computedBSize == NS_AUTOHEIGHT) { for (uint32_t i = 0; i < mGridRowEnd - 1; ++i) { bSize += rowSizes[i].mBase; } } else { bSize = computedBSize; } bSize = std::max(bSize - GetConsumedBSize(), 0); LogicalSize desiredSize(wm, computedISize + bp.IStartEnd(wm), bSize + bp.BStartEnd(wm)); aDesiredSize.SetSize(wm, desiredSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); LogicalRect contentArea(wm, bp.IStart(wm), bp.BStart(wm), computedISize, bSize); normalFlowIter.Reset(); ReflowChildren(normalFlowIter, contentArea, colSizes, rowSizes, aDesiredSize, aReflowState, aStatus); FinishAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsTextControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); // make sure that the form registers itself on the initial/first reflow if (mState & NS_FRAME_FIRST_REFLOW) { nsFormControlFrame::RegUnRegAccessKey(this, true); } // set values of reflow's out parameters WritingMode wm = aReflowState.GetWritingMode(); LogicalSize finalSize(wm, aReflowState.ComputedISize() + aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm), aReflowState.ComputedBSize() + aReflowState.ComputedLogicalBorderPadding().BStartEnd(wm)); aDesiredSize.SetSize(wm, finalSize); // computation of the ascent wrt the input height nscoord lineHeight = aReflowState.ComputedBSize(); float inflation = nsLayoutUtils::FontSizeInflationFor(this); if (!IsSingleLineTextControl()) { lineHeight = nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(), NS_AUTOHEIGHT, inflation); } RefPtr<nsFontMetrics> fontMet; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet), inflation); // now adjust for our borders and padding aDesiredSize.SetBlockStartAscent( nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight, wm.IsLineInverted()) + aReflowState.ComputedLogicalBorderPadding().BStart(wm)); // overflow handling aDesiredSize.SetOverflowAreasToDesiredBounds(); // perform reflow on all kids nsIFrame* kid = mFrames.FirstChild(); while (kid) { ReflowTextControlChild(kid, aPresContext, aReflowState, aStatus, aDesiredSize); kid = kid->GetNextSibling(); } // take into account css properties that affect overflow handling FinishAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsHTMLButtonControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsHTMLButtonControlFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_PRECONDITION(aReflowState.ComputedISize() != NS_INTRINSICSIZE, "Should have real computed inline-size by now"); if (mState & NS_FRAME_FIRST_REFLOW) { nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), true); } // Reflow the child nsIFrame* firstKid = mFrames.FirstChild(); MOZ_ASSERT(firstKid, "Button should have a child frame for its contents"); MOZ_ASSERT(!firstKid->GetNextSibling(), "Button should have exactly one child frame"); MOZ_ASSERT(firstKid->StyleContext()->GetPseudo() == nsCSSAnonBoxes::buttonContent, "Button's child frame has unexpected pseudo type!"); // XXXbz Eventually we may want to check-and-bail if // !aReflowState.ShouldReflowAllKids() && // !NS_SUBTREE_DIRTY(firstKid). // We'd need to cache our ascent for that, of course. // Reflow the contents of the button. // (This populates our aDesiredSize, too.) ReflowButtonContents(aPresContext, aDesiredSize, aReflowState, firstKid); ConsiderChildOverflow(aDesiredSize.mOverflowAreas, firstKid); aStatus = NS_FRAME_COMPLETE; FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); // We're always complete and we don't support overflow containers // so we shouldn't have a next-in-flow ever. aStatus = NS_FRAME_COMPLETE; MOZ_ASSERT(!GetNextInFlow()); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
nsColumnSetFrame::ReflowConfig nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState, bool aForceAuto = false, nscoord aFeasibleBSize = NS_INTRINSICSIZE, nscoord aInfeasibleBSize = 0) { nscoord knownFeasibleBSize = aFeasibleBSize; nscoord knownInfeasibleBSize = aInfeasibleBSize; const nsStyleColumn* colStyle = StyleColumn(); nscoord availContentISize = GetAvailableContentISize(aReflowState); if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) { availContentISize = aReflowState.ComputedISize(); } nscoord consumedBSize = GetConsumedBSize(); // The effective computed height is the height of the current continuation // of the column set frame. This should be the same as the computed height // if we have an unconstrained available height. nscoord computedBSize = GetEffectiveComputedBSize(aReflowState, consumedBSize); nscoord colBSize = GetAvailableContentBSize(aReflowState); if (aReflowState.ComputedBSize() != NS_INTRINSICSIZE) { colBSize = aReflowState.ComputedBSize(); } else if (aReflowState.ComputedMaxBSize() != NS_INTRINSICSIZE) { colBSize = std::min(colBSize, aReflowState.ComputedMaxBSize()); } nscoord colGap = GetColumnGap(this, colStyle); int32_t numColumns = colStyle->mColumnCount; // If column-fill is set to 'balance', then we want to balance the columns. const bool isBalancing = colStyle->mColumnFill == NS_STYLE_COLUMN_FILL_BALANCE && !aForceAuto; if (isBalancing) { const uint32_t MAX_NESTED_COLUMN_BALANCING = 2; uint32_t cnt = 0; for (const nsHTMLReflowState* rs = aReflowState.parentReflowState; rs && cnt < MAX_NESTED_COLUMN_BALANCING; rs = rs->parentReflowState) { if (rs->mFlags.mIsColumnBalancing) { ++cnt; } } if (cnt == MAX_NESTED_COLUMN_BALANCING) { numColumns = 1; } } nscoord colISize; // In vertical writing-mode, "column-width" (inline size) will actually be // physical height, but its CSS name is still column-width. if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) { colISize = colStyle->mColumnWidth.GetCoordValue(); NS_ASSERTION(colISize >= 0, "negative column width"); // Reduce column count if necessary to make columns fit in the // available width. Compute max number of columns that fit in // availContentISize, satisfying colGap*(maxColumns - 1) + // colISize*maxColumns <= availContentISize if (availContentISize != NS_INTRINSICSIZE && colGap + colISize > 0 && numColumns > 0) { // This expression uses truncated rounding, which is what we // want int32_t maxColumns = std::min(nscoord(nsStyleColumn::kMaxColumnCount), (availContentISize + colGap) / (colGap + colISize)); numColumns = std::max(1, std::min(numColumns, maxColumns)); } } else if (numColumns > 0 && availContentISize != NS_INTRINSICSIZE) { nscoord iSizeMinusGaps = availContentISize - colGap * (numColumns - 1); colISize = iSizeMinusGaps / numColumns; } else { colISize = NS_INTRINSICSIZE; } // Take care of the situation where there's only one column but it's // still too wide colISize = std::max(1, std::min(colISize, availContentISize)); nscoord expectedISizeLeftOver = 0; if (colISize != NS_INTRINSICSIZE && availContentISize != NS_INTRINSICSIZE) { // distribute leftover space // First, determine how many columns will be showing if the column // count is auto if (numColumns <= 0) { // choose so that colGap*(nominalColumnCount - 1) + // colISize*nominalColumnCount is nearly availContentISize // make sure to round down if (colGap + colISize > 0) { numColumns = (availContentISize + colGap) / (colGap + colISize); // The number of columns should never exceed kMaxColumnCount. numColumns = std::min(nscoord(nsStyleColumn::kMaxColumnCount), numColumns); } if (numColumns <= 0) { numColumns = 1; } } // Compute extra space and divide it among the columns nscoord extraSpace = std::max(0, availContentISize - (colISize * numColumns + colGap * (numColumns - 1))); nscoord extraToColumns = extraSpace / numColumns; colISize += extraToColumns; expectedISizeLeftOver = extraSpace - (extraToColumns * numColumns); } if (isBalancing) { if (numColumns <= 0) { // Hmm, auto column count, column width or available width is unknown, // and balancing is required. Let's just use one column then. numColumns = 1; } colBSize = std::min(mLastBalanceBSize, colBSize); } else { // This is the case when the column-fill property is set to 'auto'. // No balancing, so don't limit the column count numColumns = INT32_MAX; // XXX_jwir3: If a page's height is set to 0, we could continually // create continuations, resulting in an infinite loop, since // no progress is ever made. This is an issue with the spec // (css3-multicol, css3-page, and css3-break) that is // unresolved as of 27 Feb 2013. For the time being, we set this // to have a minimum of 1 css px. Once a resolution is made // on what minimum to have for a page height, we may need to // change this value to match the appropriate spec(s). colBSize = std::max(colBSize, nsPresContext::CSSPixelsToAppUnits(1)); } #ifdef DEBUG_roc printf("*** nsColumnSetFrame::ChooseColumnStrategy: numColumns=%d, colISize=%d," " expectedISizeLeftOver=%d, colBSize=%d, colGap=%d\n", numColumns, colISize, expectedISizeLeftOver, colBSize, colGap); #endif ReflowConfig config = { numColumns, colISize, expectedISizeLeftOver, colGap, colBSize, isBalancing, knownFeasibleBSize, knownInfeasibleBSize, computedBSize, consumedBSize }; return config; }
bool nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus, const ReflowConfig& aConfig, bool aUnboundedLastColumn, nsCollapsingMargin* aBottomMarginCarriedOut, ColumnBalanceData& aColData) { aColData.Reset(); bool allFit = true; bool RTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; bool shrinkingHeightOnly = !NS_SUBTREE_DIRTY(this) && mLastBalanceHeight > aConfig.mColMaxHeight; #ifdef DEBUG_roc printf("*** Doing column reflow pass: mLastBalanceHeight=%d, mColMaxHeight=%d, RTL=%d\n, mBalanceColCount=%d, mColWidth=%d, mColGap=%d\n", mLastBalanceHeight, aConfig.mColMaxHeight, RTL, aConfig.mBalanceColCount, aConfig.mColWidth, aConfig.mColGap); #endif DrainOverflowColumns(); const bool colHeightChanged = mLastBalanceHeight != aConfig.mColMaxHeight; if (colHeightChanged) { mLastBalanceHeight = aConfig.mColMaxHeight; // XXX Seems like this could fire if incremental reflow pushed the column set // down so we reflow incrementally with a different available height. // We need a way to do an incremental reflow and be sure availableHeight // changes are taken account of! Right now I think block frames with absolute // children might exit early. //NS_ASSERTION(aKidReason != eReflowReason_Incremental, // "incremental reflow should not have changed the balance height"); } // get our border and padding nsMargin borderPadding = aReflowState.ComputedPhysicalBorderPadding(); borderPadding.ApplySkipSides(GetSkipSides(&aReflowState)); nsRect contentRect(0, 0, 0, 0); nsOverflowAreas overflowRects; nsIFrame* child = mFrames.FirstChild(); nsPoint childOrigin = nsPoint(borderPadding.left, borderPadding.top); // For RTL, figure out where the last column's left edge should be. Since the // columns might not fill the frame exactly, we need to account for the // slop. Otherwise we'll waste time moving the columns by some tiny // amount unnecessarily. if (RTL) { nscoord availWidth = aReflowState.AvailableWidth(); if (aReflowState.ComputedWidth() != NS_INTRINSICSIZE) { availWidth = aReflowState.ComputedWidth(); } if (availWidth != NS_INTRINSICSIZE) { childOrigin.x += availWidth - aConfig.mColWidth; #ifdef DEBUG_roc printf("*** childOrigin.x = %d\n", childOrigin.x); #endif } } int columnCount = 0; int contentBEnd = 0; bool reflowNext = false; while (child) { // Try to skip reflowing the child. We can't skip if the child is dirty. We also can't // skip if the next column is dirty, because the next column's first line(s) // might be pullable back to this column. We can't skip if it's the last child // because we need to obtain the bottom margin. We can't skip // if this is the last column and we're supposed to assign unbounded // height to it, because that could change the available height from // the last time we reflowed it and we should try to pull all the // content from its next sibling. (Note that it might be the last // column, but not be the last child because the desired number of columns // has changed.) bool skipIncremental = !aReflowState.ShouldReflowAllKids() && !NS_SUBTREE_DIRTY(child) && child->GetNextSibling() && !(aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) && !NS_SUBTREE_DIRTY(child->GetNextSibling()); // If we need to pull up content from the prev-in-flow then this is not just // a height shrink. The prev in flow will have set the dirty bit. // Check the overflow rect YMost instead of just the child's content height. The child // may have overflowing content that cares about the available height boundary. // (It may also have overflowing content that doesn't care about the available height // boundary, but if so, too bad, this optimization is defeated.) // We want scrollable overflow here since this is a calculation that // affects layout. bool skipResizeHeightShrink = shrinkingHeightOnly && child->GetScrollableOverflowRect().YMost() <= aConfig.mColMaxHeight; nscoord childContentBEnd = 0; WritingMode wm = child->GetWritingMode(); if (!reflowNext && (skipIncremental || skipResizeHeightShrink)) { // This child does not need to be reflowed, but we may need to move it MoveChildTo(this, child, childOrigin); // If this is the last frame then make sure we get the right status nsIFrame* kidNext = child->GetNextSibling(); if (kidNext) { aStatus = (kidNext->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ? NS_FRAME_OVERFLOW_INCOMPLETE : NS_FRAME_NOT_COMPLETE; } else { aStatus = mLastFrameStatus; } childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child); #ifdef DEBUG_roc printf("*** Skipping child #%d %p (incremental %d, resize height shrink %d): status = %d\n", columnCount, (void*)child, skipIncremental, skipResizeHeightShrink, aStatus); #endif } else { nsSize physicalSize(aConfig.mColWidth, aConfig.mColMaxHeight); if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) { physicalSize.height = GetAvailableContentHeight(aReflowState); } LogicalSize availSize(wm, physicalSize); LogicalSize computedSize = aReflowState.ComputedSize(wm); if (reflowNext) child->AddStateBits(NS_FRAME_IS_DIRTY); nsHTMLReflowState kidReflowState(PresContext(), aReflowState, child, availSize, availSize.ISize(wm), computedSize.BSize(wm)); kidReflowState.mFlags.mIsTopOfPage = true; kidReflowState.mFlags.mTableIsSplittable = false; kidReflowState.mFlags.mIsColumnBalancing = aConfig.mBalanceColCount < INT32_MAX; // We need to reflow any float placeholders, even if our column height // hasn't changed. kidReflowState.mFlags.mMustReflowPlaceholders = !colHeightChanged; #ifdef DEBUG_roc printf("*** Reflowing child #%d %p: availHeight=%d\n", columnCount, (void*)child,availSize.BSize(wm)); #endif // Note if the column's next in flow is not being changed by this incremental reflow. // This may allow the current column to avoid trying to pull lines from the next column. if (child->GetNextSibling() && !(GetStateBits() & NS_FRAME_IS_DIRTY) && !(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY)) { kidReflowState.mFlags.mNextInFlowUntouched = true; } nsHTMLReflowMetrics kidDesiredSize(wm, aDesiredSize.mFlags); // XXX it would be cool to consult the float manager for the // previous block to figure out the region of floats from the // previous column that extend into this column, and subtract // that region from the new float manager. So you could stick a // really big float in the first column and text in following // columns would flow around it. // Reflow the frame ReflowChild(child, PresContext(), kidDesiredSize, kidReflowState, childOrigin.x + kidReflowState.ComputedPhysicalMargin().left, childOrigin.y + kidReflowState.ComputedPhysicalMargin().top, 0, aStatus); reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0; #ifdef DEBUG_roc printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d CarriedOutBottomMargin=%d\n", columnCount, (void*)child, aStatus, kidDesiredSize.Width(), kidDesiredSize.Height(), kidDesiredSize.mCarriedOutBEndMargin.get()); #endif NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus); *aBottomMarginCarriedOut = kidDesiredSize.mCarriedOutBEndMargin; FinishReflowChild(child, PresContext(), kidDesiredSize, &kidReflowState, childOrigin.x, childOrigin.y, 0); childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child); if (childContentBEnd > aConfig.mColMaxHeight) { allFit = false; } if (childContentBEnd > availSize.BSize(wm)) { aColData.mMaxOverflowingHeight = std::max(childContentBEnd, aColData.mMaxOverflowingHeight); } } contentRect.UnionRect(contentRect, child->GetRect()); ConsiderChildOverflow(overflowRects, child); contentBEnd = std::max(contentBEnd, childContentBEnd); aColData.mLastHeight = childContentBEnd; aColData.mSumHeight += childContentBEnd; // Build a continuation column if necessary nsIFrame* kidNextInFlow = child->GetNextInFlow(); if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)) { NS_ASSERTION(!kidNextInFlow, "next in flow should have been deleted"); child = nullptr; break; } else { ++columnCount; // Make sure that the column has a next-in-flow. If not, we must // create one to hold the overflowing stuff, even if we're just // going to put it on our overflow list and let *our* // next in flow handle it. if (!kidNextInFlow) { NS_ASSERTION(aStatus & NS_FRAME_REFLOW_NEXTINFLOW, "We have to create a continuation, but the block doesn't want us to reflow it?"); // We need to create a continuing column nsresult rv = CreateNextInFlow(child, kidNextInFlow); if (NS_FAILED(rv)) { NS_NOTREACHED("Couldn't create continuation"); child = nullptr; break; } } // Make sure we reflow a next-in-flow when it switches between being // normal or overflow container if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { if (!(kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; reflowNext = true; kidNextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } } else if (kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; reflowNext = true; kidNextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } if ((contentBEnd > aReflowState.ComputedMaxBSize() || contentBEnd > aReflowState.ComputedBSize()) && aConfig.mBalanceColCount < INT32_MAX) { // We overflowed vertically, but have not exceeded the number of // columns. We're going to go into overflow columns now, so balancing // no longer applies. aColData.mHasExcessHeight = true; } if (columnCount >= aConfig.mBalanceColCount) { // No more columns allowed here. Stop. aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; kidNextInFlow->AddStateBits(NS_FRAME_IS_DIRTY); // Move any of our leftover columns to our overflow list. Our // next-in-flow will eventually pick them up. const nsFrameList& continuationColumns = mFrames.RemoveFramesAfter(child); if (continuationColumns.NotEmpty()) { SetOverflowFrames(continuationColumns); } child = nullptr; break; } } if (PresContext()->HasPendingInterrupt()) { // Stop the loop now while |child| still points to the frame that bailed // out. We could keep going here and condition a bunch of the code in // this loop on whether there's an interrupt, or even just keep going and // trying to reflow the blocks (even though we know they'll interrupt // right after their first line), but stopping now is conceptually the // simplest (and probably fastest) thing. break; } // Advance to the next column child = child->GetNextSibling(); if (child) { if (!RTL) { childOrigin.x += aConfig.mColWidth + aConfig.mColGap; } else { childOrigin.x -= aConfig.mColWidth + aConfig.mColGap; } #ifdef DEBUG_roc printf("*** NEXT CHILD ORIGIN.x = %d\n", childOrigin.x); #endif } } if (PresContext()->CheckForInterrupt(this) && (GetStateBits() & NS_FRAME_IS_DIRTY)) { // Mark all our kids starting with |child| dirty // Note that this is a CheckForInterrupt call, not a HasPendingInterrupt, // because we might have interrupted while reflowing |child|, and since // we're about to add a dirty bit to |child| we need to make sure that // |this| is scheduled to have dirty bits marked on it and its ancestors. // Otherwise, when we go to mark dirty bits on |child|'s ancestors we'll // bail out immediately, since it'll already have a dirty bit. for (; child; child = child->GetNextSibling()) { child->AddStateBits(NS_FRAME_IS_DIRTY); } } aColData.mMaxHeight = contentBEnd; contentRect.height = std::max(contentRect.height, contentBEnd); mLastFrameStatus = aStatus; // contentRect included the borderPadding.left,borderPadding.top of the child rects contentRect -= nsPoint(borderPadding.left, borderPadding.top); WritingMode wm = aReflowState.GetWritingMode(); LogicalSize contentSize(wm, nsSize(contentRect.XMost(), contentRect.YMost())); // Apply computed and min/max values // (aConfig members need to be converted from Width/Height to ISize/BSize) if (aConfig.mComputedHeight != NS_INTRINSICSIZE) { if (aReflowState.AvailableHeight() != NS_INTRINSICSIZE) { contentSize.BSize(wm) = std::min(contentSize.BSize(wm), aConfig.mComputedHeight); } else { contentSize.BSize(wm) = aConfig.mComputedHeight; } } else { // We add the "consumed" height back in so that we're applying // constraints to the correct height value, then subtract it again // after we've finished with the min/max calculation. This prevents us from // having a last continuation that is smaller than the min height. but which // has prev-in-flows, trigger a larger height than actually required. contentSize.BSize(wm) = aReflowState.ApplyMinMaxHeight(contentSize.BSize(wm), aConfig.mConsumedHeight); } if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) { contentSize.ISize(wm) = aReflowState.ComputedISize(); } else { contentSize.ISize(wm) = aReflowState.ApplyMinMaxWidth(contentSize.ISize(wm)); } LogicalMargin bp(wm, borderPadding); contentSize.ISize(wm) += bp.IStartEnd(wm); contentSize.BSize(wm) += bp.BStartEnd(wm); aDesiredSize.SetSize(wm, contentSize); aDesiredSize.mOverflowAreas = overflowRects; aDesiredSize.UnionOverflowAreasWithDesiredBounds(); #ifdef DEBUG_roc printf("*** DONE PASS feasible=%d\n", allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)); #endif return allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus); }
void nsFieldSetFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_PRECONDITION(aReflowState.ComputedISize() != NS_INTRINSICSIZE, "Should have a precomputed inline-size!"); // Initialize OUT parameter aStatus = NS_FRAME_COMPLETE; nsOverflowAreas ocBounds; nsReflowStatus ocStatus = NS_FRAME_COMPLETE; if (GetPrevInFlow()) { ReflowOverflowContainerChildren(aPresContext, aReflowState, ocBounds, 0, ocStatus); } //------------ Handle Incremental Reflow ----------------- bool reflowInner; bool reflowLegend; nsIFrame* legend = GetLegend(); nsIFrame* inner = GetInner(); if (aReflowState.ShouldReflowAllKids()) { reflowInner = inner != nullptr; reflowLegend = legend != nullptr; } else { reflowInner = inner && NS_SUBTREE_DIRTY(inner); reflowLegend = legend && NS_SUBTREE_DIRTY(legend); } // We don't allow fieldsets to break vertically. If we did, we'd // need logic here to push and pull overflow frames. // Since we're not applying our padding in this frame, we need to add it here // to compute the available width for our children. WritingMode wm = GetWritingMode(); WritingMode innerWM = inner ? inner->GetWritingMode() : wm; WritingMode legendWM = legend ? legend->GetWritingMode() : wm; LogicalSize innerAvailSize = aReflowState.ComputedSizeWithPadding(innerWM); LogicalSize legendAvailSize = aReflowState.ComputedSizeWithPadding(legendWM); innerAvailSize.BSize(innerWM) = legendAvailSize.BSize(legendWM) = NS_UNCONSTRAINEDSIZE; NS_ASSERTION(!inner || nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext, inner, nsLayoutUtils::MIN_ISIZE) <= innerAvailSize.ISize(innerWM), "Bogus availSize.ISize; should be bigger"); NS_ASSERTION(!legend || nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext, legend, nsLayoutUtils::MIN_ISIZE) <= legendAvailSize.ISize(legendWM), "Bogus availSize.ISize; should be bigger"); // get our border and padding LogicalMargin border = aReflowState.ComputedLogicalBorderPadding() - aReflowState.ComputedLogicalPadding(); // Figure out how big the legend is if there is one. // get the legend's margin LogicalMargin legendMargin(wm); // reflow the legend only if needed Maybe<nsHTMLReflowState> legendReflowState; if (legend) { legendReflowState.emplace(aPresContext, aReflowState, legend, legendAvailSize); } if (reflowLegend) { nsHTMLReflowMetrics legendDesiredSize(aReflowState); // We'll move the legend to its proper place later, so the position // and containerSize passed here are unimportant. const nsSize dummyContainerSize; ReflowChild(legend, aPresContext, legendDesiredSize, *legendReflowState, wm, LogicalPoint(wm), dummyContainerSize, NS_FRAME_NO_MOVE_FRAME, aStatus); #ifdef NOISY_REFLOW printf(" returned (%d, %d)\n", legendDesiredSize.Width(), legendDesiredSize.Height()); #endif // figure out the legend's rectangle legendMargin = legend->GetLogicalUsedMargin(wm); mLegendRect = LogicalRect(wm, 0, 0, legendDesiredSize.ISize(wm) + legendMargin.IStartEnd(wm), legendDesiredSize.BSize(wm) + legendMargin.BStartEnd(wm)); nscoord oldSpace = mLegendSpace; mLegendSpace = 0; if (mLegendRect.BSize(wm) > border.BStart(wm)) { // center the border on the legend mLegendSpace = mLegendRect.BSize(wm) - border.BStart(wm); } else { mLegendRect.BStart(wm) = (border.BStart(wm) - mLegendRect.BSize(wm)) / 2; } // if the legend space changes then we need to reflow the // content area as well. if (mLegendSpace != oldSpace && inner) { reflowInner = true; } FinishReflowChild(legend, aPresContext, legendDesiredSize, legendReflowState.ptr(), wm, LogicalPoint(wm), dummyContainerSize, NS_FRAME_NO_MOVE_FRAME); } else if (!legend) { mLegendRect.SetEmpty(); mLegendSpace = 0; } else { // mLegendSpace and mLegendRect haven't changed, but we need // the used margin when placing the legend. legendMargin = legend->GetLogicalUsedMargin(wm); } // This containerSize is incomplete as yet: it does not include the size // of the |inner| frame itself. nsSize containerSize = (LogicalSize(wm, 0, mLegendSpace) + border.Size(wm)).GetPhysicalSize(wm); // reflow the content frame only if needed if (reflowInner) { nsHTMLReflowState kidReflowState(aPresContext, aReflowState, inner, innerAvailSize, nullptr, nsHTMLReflowState::CALLER_WILL_INIT); // Override computed padding, in case it's percentage padding kidReflowState.Init(aPresContext, nullptr, nullptr, &aReflowState.ComputedPhysicalPadding()); // Our child is "height:100%" but we actually want its height to be reduced // by the amount of content-height the legend is eating up, unless our // height is unconstrained (in which case the child's will be too). if (aReflowState.ComputedBSize() != NS_UNCONSTRAINEDSIZE) { kidReflowState.SetComputedBSize( std::max(0, aReflowState.ComputedBSize() - mLegendSpace)); } if (aReflowState.ComputedMinBSize() > 0) { kidReflowState.ComputedMinBSize() = std::max(0, aReflowState.ComputedMinBSize() - mLegendSpace); } if (aReflowState.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE) { kidReflowState.ComputedMaxBSize() = std::max(0, aReflowState.ComputedMaxBSize() - mLegendSpace); } nsHTMLReflowMetrics kidDesiredSize(kidReflowState, aDesiredSize.mFlags); // Reflow the frame NS_ASSERTION(kidReflowState.ComputedPhysicalMargin() == nsMargin(0,0,0,0), "Margins on anonymous fieldset child not supported!"); LogicalPoint pt(wm, border.IStart(wm), border.BStart(wm) + mLegendSpace); // We don't know the correct containerSize until we have reflowed |inner|, // so we use a dummy value for now; FinishReflowChild will fix the position // if necessary. const nsSize dummyContainerSize; ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowState, wm, pt, dummyContainerSize, 0, aStatus); // Update containerSize to account for size of the inner frame, so that // FinishReflowChild can position it correctly. containerSize += kidDesiredSize.PhysicalSize(); FinishReflowChild(inner, aPresContext, kidDesiredSize, &kidReflowState, wm, pt, containerSize, 0); NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus); } else if (inner) { // |inner| didn't need to be reflowed but we do need to include its size // in containerSize. containerSize += inner->GetSize(); } LogicalRect contentRect(wm); if (inner) { // We don't support margins on inner, so our content rect is just the // inner's border-box. (We don't really care about container size at this // point, as we'll figure out the actual positioning later.) contentRect = inner->GetLogicalRect(wm, containerSize); } // Our content rect must fill up the available width LogicalSize availSize = aReflowState.ComputedSizeWithPadding(wm); if (availSize.ISize(wm) > contentRect.ISize(wm)) { contentRect.ISize(wm) = innerAvailSize.ISize(wm); } if (legend) { // The legend is positioned inline-wards within the inner's content rect // (so that padding on the fieldset affects the legend position). LogicalRect innerContentRect = contentRect; innerContentRect.Deflate(wm, aReflowState.ComputedLogicalPadding()); // If the inner content rect is larger than the legend, we can align the // legend. if (innerContentRect.ISize(wm) > mLegendRect.ISize(wm)) { int32_t align = static_cast<nsLegendFrame*> (legend->GetContentInsertionFrame())->GetAlign(); if (!wm.IsBidiLTR()) { if (align == NS_STYLE_TEXT_ALIGN_LEFT || align == NS_STYLE_TEXT_ALIGN_MOZ_LEFT) { align = NS_STYLE_TEXT_ALIGN_END; } else if (align == NS_STYLE_TEXT_ALIGN_RIGHT || align == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT) { align = NS_STYLE_TEXT_ALIGN_DEFAULT; } } switch (align) { case NS_STYLE_TEXT_ALIGN_END: mLegendRect.IStart(wm) = innerContentRect.IEnd(wm) - mLegendRect.ISize(wm); break; case NS_STYLE_TEXT_ALIGN_CENTER: case NS_STYLE_TEXT_ALIGN_MOZ_CENTER: // Note: rounding removed; there doesn't seem to be any need mLegendRect.IStart(wm) = innerContentRect.IStart(wm) + (innerContentRect.ISize(wm) - mLegendRect.ISize(wm)) / 2; break; default: mLegendRect.IStart(wm) = innerContentRect.IStart(wm); break; } } else { // otherwise make place for the legend mLegendRect.IStart(wm) = innerContentRect.IStart(wm); innerContentRect.ISize(wm) = mLegendRect.ISize(wm); contentRect.ISize(wm) = mLegendRect.ISize(wm) + aReflowState.ComputedLogicalPadding().IStartEnd(wm); } // place the legend LogicalRect actualLegendRect = mLegendRect; actualLegendRect.Deflate(wm, legendMargin); LogicalPoint actualLegendPos(actualLegendRect.Origin(wm)); // Note that legend's writing mode may be different from the fieldset's, // so we need to convert offsets before applying them to it (bug 1134534). LogicalMargin offsets = legendReflowState->ComputedLogicalOffsets(). ConvertTo(wm, legendReflowState->GetWritingMode()); nsHTMLReflowState::ApplyRelativePositioning(legend, wm, offsets, &actualLegendPos, containerSize); legend->SetPosition(wm, actualLegendPos, containerSize); nsContainerFrame::PositionFrameView(legend); nsContainerFrame::PositionChildViews(legend); } // Return our size and our result. LogicalSize finalSize(wm, contentRect.ISize(wm) + border.IStartEnd(wm), mLegendSpace + border.BStartEnd(wm) + (inner ? inner->BSize(wm) : 0)); aDesiredSize.SetSize(wm, finalSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); if (legend) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, legend); } if (inner) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inner); } // Merge overflow container bounds and status. aDesiredSize.mOverflowAreas.UnionWith(ocBounds); NS_MergeReflowStatusInto(&aStatus, ocStatus); FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); InvalidateFrame(); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsCanvasFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); 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.IsVResize() && (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { // Tell our kid it's being vertically resized too. Bit of a // hack for framesets. kidReflowState.SetVResize(true); } WritingMode wm = aReflowState.GetWritingMode(); WritingMode kidWM = kidReflowState.GetWritingMode(); nscoord containerWidth = aReflowState.ComputedWidth(); LogicalMargin margin = kidReflowState.ComputedLogicalMargin(); LogicalPoint kidPt(kidWM, margin.IStart(kidWM), margin.BStart(kidWM)); kidReflowState.ApplyRelativePositioning(&kidPt, containerWidth); // Reflow the frame ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState, kidWM, kidPt, containerWidth, 0, aStatus); // Complete the reflow and position and size the child frame FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowState, kidWM, kidPt, containerWidth, 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. 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 + kidFrame->GetPosition()); } 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); }
nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsPresContext* aPresContext, nsBlockFrame* aFrame, bool aBStartMarginRoot, bool aBEndMarginRoot, bool aBlockNeedsFloatManager, nscoord aConsumedBSize) : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), mContentArea(aReflowState.GetWritingMode()), mPushedFloats(nullptr), mOverflowTracker(nullptr), mBorderPadding(mReflowState.ComputedLogicalBorderPadding()), mPrevBEndMargin(), mLineNumber(0), mFlags(0), mFloatBreakType(NS_STYLE_CLEAR_NONE), mConsumedBSize(aConsumedBSize) { if (!sFloatFragmentsInsideColumnPrefCached) { sFloatFragmentsInsideColumnPrefCached = true; Preferences::AddBoolVarCache(&sFloatFragmentsInsideColumnEnabled, "layout.float-fragments-inside-column.enabled"); } SetFlag(BRS_FLOAT_FRAGMENTS_INSIDE_COLUMN_ENABLED, sFloatFragmentsInsideColumnEnabled); WritingMode wm = aReflowState.GetWritingMode(); SetFlag(BRS_ISFIRSTINFLOW, aFrame->GetPrevInFlow() == nullptr); SetFlag(BRS_ISOVERFLOWCONTAINER, IS_TRUE_OVERFLOW_CONTAINER(aFrame)); nsIFrame::LogicalSides logicalSkipSides = aFrame->GetLogicalSkipSides(&aReflowState); mBorderPadding.ApplySkipSides(logicalSkipSides); // Note that mContainerSize is the physical size, needed to // convert logical block-coordinates in vertical-rl writing mode // (measured from a RHS origin) to physical coordinates within the // containing block. // If aReflowState doesn't have a constrained ComputedWidth(), we set // mContainerSize.width to zero, which means lines will be positioned // (physically) incorrectly; we will fix them up at the end of // nsBlockFrame::Reflow, after we know the total block-size of the // frame. mContainerSize.width = aReflowState.ComputedWidth(); if (mContainerSize.width == NS_UNCONSTRAINEDSIZE) { mContainerSize.width = 0; } mContainerSize.width += mBorderPadding.LeftRight(wm); // For now at least, we don't do that fix-up for mContainerHeight. // It's only used in nsBidiUtils::ReorderFrames for vertical rtl // writing modes, which aren't fully supported for the time being. mContainerSize.height = aReflowState.ComputedHeight() + mBorderPadding.TopBottom(wm); if ((aBStartMarginRoot && !logicalSkipSides.BStart()) || 0 != mBorderPadding.BStart(wm)) { SetFlag(BRS_ISBSTARTMARGINROOT, true); SetFlag(BRS_APPLYBSTARTMARGIN, true); } if ((aBEndMarginRoot && !logicalSkipSides.BEnd()) || 0 != mBorderPadding.BEnd(wm)) { SetFlag(BRS_ISBENDMARGINROOT, true); } if (aBlockNeedsFloatManager) { SetFlag(BRS_FLOAT_MGR, true); } mFloatManager = aReflowState.mFloatManager; NS_ASSERTION(mFloatManager, "FloatManager should be set in nsBlockReflowState" ); if (mFloatManager) { // Save the coordinate system origin for later. mFloatManager->GetTranslation(mFloatManagerI, mFloatManagerB); mFloatManager->PushState(&mFloatManagerStateBefore); // never popped } mReflowStatus = NS_FRAME_COMPLETE; mNextInFlow = static_cast<nsBlockFrame*>(mBlock->GetNextInFlow()); LAYOUT_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowState.ComputedISize(), "have unconstrained width; this should only result " "from very large sizes, not attempts at intrinsic " "width calculation"); mContentArea.ISize(wm) = aReflowState.ComputedISize(); // Compute content area height. Unlike the width, if we have a // specified style height we ignore it since extra content is // managed by the "overflow" property. When we don't have a // specified style height then we may end up limiting our height if // the availableHeight is constrained (this situation occurs when we // are paginated). if (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableBSize()) { // We are in a paginated situation. The bottom edge is just inside // the bottom border and padding. The content area height doesn't // include either border or padding edge. mBEndEdge = aReflowState.AvailableBSize() - mBorderPadding.BEnd(wm); mContentArea.BSize(wm) = std::max(0, mBEndEdge - mBorderPadding.BStart(wm)); } else { // When we are not in a paginated situation then we always use // an constrained height. SetFlag(BRS_UNCONSTRAINEDBSIZE, true); mContentArea.BSize(wm) = mBEndEdge = NS_UNCONSTRAINEDSIZE; } mContentArea.IStart(wm) = mBorderPadding.IStart(wm); mBCoord = mContentArea.BStart(wm) = mBorderPadding.BStart(wm); mPrevChild = nullptr; mCurrentLine = aFrame->end_lines(); mMinLineHeight = aReflowState.CalcLineHeight(); }
void nsPageContentFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsPageContentFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); aStatus = NS_FRAME_COMPLETE; // initialize out parameter if (GetPrevInFlow() && (GetStateBits() & NS_FRAME_FIRST_REFLOW)) { nsresult rv = aPresContext->PresShell()->FrameConstructor() ->ReplicateFixedFrames(this); if (NS_FAILED(rv)) { return; } } // 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. nsSize maxSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); SetSize(maxSize); // A PageContentFrame must always have one child: the canvas frame. // Resize our frame allowing it only to be as big as we are // XXX Pay attention to the page's border and padding... if (mFrames.NotEmpty()) { nsIFrame* frame = mFrames.FirstChild(); WritingMode wm = frame->GetWritingMode(); LogicalSize logicalSize(wm, maxSize); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, frame, logicalSize); kidReflowState.SetComputedBSize(logicalSize.BSize(wm)); // Reflow the page content area ReflowChild(frame, aPresContext, aDesiredSize, kidReflowState, 0, 0, 0, aStatus); // The document element's background should cover the entire canvas, so // take into account the combined area and any space taken up by // absolutely positioned elements nsMargin padding(0,0,0,0); // XXXbz this screws up percentage padding (sets padding to zero // in the percentage padding case) kidReflowState.mStylePadding->GetPadding(padding); // This is for shrink-to-fit, and therefore we want to use the // scrollable overflow, since the purpose of shrink to fit is to // make the content that ought to be reachable (represented by the // scrollable overflow) fit in the page. if (frame->HasOverflowAreas()) { // The background covers the content area and padding area, so check // for children sticking outside the child frame's padding edge nscoord xmost = aDesiredSize.ScrollableOverflow().XMost(); if (xmost > aDesiredSize.Width()) { nscoord widthToFit = xmost + padding.right + kidReflowState.mStyleBorder->GetComputedBorderWidth(NS_SIDE_RIGHT); float ratio = float(maxSize.width) / widthToFit; NS_ASSERTION(ratio >= 0.0 && ratio < 1.0, "invalid shrink-to-fit ratio"); mPD->mShrinkToFitRatio = std::min(mPD->mShrinkToFitRatio, ratio); } } // Place and size the child FinishReflowChild(frame, aPresContext, aDesiredSize, &kidReflowState, 0, 0, 0); NS_ASSERTION(aPresContext->IsDynamic() || !NS_FRAME_IS_FULLY_COMPLETE(aStatus) || !frame->GetNextInFlow(), "bad child flow list"); } // Reflow our fixed frames nsReflowStatus fixedStatus = NS_FRAME_COMPLETE; ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, fixedStatus); NS_ASSERTION(NS_FRAME_IS_COMPLETE(fixedStatus), "fixed frames can be truncated, but not incomplete"); // Return our desired size WritingMode wm = aReflowState.GetWritingMode(); LogicalSize finalSize(wm); finalSize.ISize(wm) = aReflowState.ComputedISize(); if (aReflowState.ComputedBSize() != NS_UNCONSTRAINEDSIZE) { finalSize.BSize(wm) = aReflowState.ComputedBSize(); } aDesiredSize.SetSize(wm, finalSize); FinishAndStoreOverflow(&aDesiredSize); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsNumberControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsNumberControlFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_ASSERTION(mOuterWrapper, "Outer wrapper div must exist!"); NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(), "nsNumberControlFrame should not have continuations; if it does we " "need to call RegUnregAccessKey only for the first"); NS_ASSERTION(!mFrames.FirstChild() || !mFrames.FirstChild()->GetNextSibling(), "We expect at most one direct child frame"); if (mState & NS_FRAME_FIRST_REFLOW) { nsFormControlFrame::RegUnRegAccessKey(this, true); } const WritingMode myWM = aReflowState.GetWritingMode(); // The ISize of our content box, which is the available ISize // for our anonymous content: const nscoord contentBoxISize = aReflowState.ComputedISize(); nscoord contentBoxBSize = aReflowState.ComputedBSize(); // Figure out our border-box sizes as well (by adding borderPadding to // content-box sizes): const nscoord borderBoxISize = contentBoxISize + aReflowState.ComputedLogicalBorderPadding().IStartEnd(myWM); nscoord borderBoxBSize; if (contentBoxBSize != NS_INTRINSICSIZE) { borderBoxBSize = contentBoxBSize + aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM); } // else, we'll figure out borderBoxBSize after we resolve contentBoxBSize. nsIFrame* outerWrapperFrame = mOuterWrapper->GetPrimaryFrame(); if (!outerWrapperFrame) { // display:none? if (contentBoxBSize == NS_INTRINSICSIZE) { contentBoxBSize = 0; borderBoxBSize = aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM); } } else { NS_ASSERTION(outerWrapperFrame == mFrames.FirstChild(), "huh?"); nsHTMLReflowMetrics wrappersDesiredSize(aReflowState); WritingMode wrapperWM = outerWrapperFrame->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wrapperWM); availSize.BSize(wrapperWM) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState wrapperReflowState(aPresContext, aReflowState, outerWrapperFrame, availSize); // Convert wrapper margin into my own writing-mode (in case it differs): LogicalMargin wrapperMargin = wrapperReflowState.ComputedLogicalMargin().ConvertTo(myWM, wrapperWM); // offsets of wrapper frame within this frame: LogicalPoint wrapperOffset(myWM, aReflowState.ComputedLogicalBorderPadding().IStart(myWM) + wrapperMargin.IStart(myWM), aReflowState.ComputedLogicalBorderPadding().BStart(myWM) + wrapperMargin.BStart(myWM)); nsReflowStatus childStatus; // We initially reflow the child with a dummy containerSize; positioning // will be fixed later. const nsSize dummyContainerSize; ReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, wrapperReflowState, myWM, wrapperOffset, dummyContainerSize, 0, childStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus), "We gave our child unconstrained available block-size, " "so it should be complete"); nscoord wrappersMarginBoxBSize = wrappersDesiredSize.BSize(myWM) + wrapperMargin.BStartEnd(myWM); if (contentBoxBSize == NS_INTRINSICSIZE) { // We are intrinsically sized -- we should shrinkwrap the outer wrapper's // block-size: contentBoxBSize = wrappersMarginBoxBSize; // Make sure we obey min/max-bsize in the case when we're doing intrinsic // sizing (we get it for free when we have a non-intrinsic // aReflowState.ComputedBSize()). Note that we do this before // adjusting for borderpadding, since ComputedMaxBSize and // ComputedMinBSize are content heights. contentBoxBSize = NS_CSS_MINMAX(contentBoxBSize, aReflowState.ComputedMinBSize(), aReflowState.ComputedMaxBSize()); borderBoxBSize = contentBoxBSize + aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM); } // Center child in block axis nscoord extraSpace = contentBoxBSize - wrappersMarginBoxBSize; wrapperOffset.B(myWM) += std::max(0, extraSpace / 2); // Needed in FinishReflowChild, for logical-to-physical conversion: nsSize borderBoxSize = LogicalSize(myWM, borderBoxISize, borderBoxBSize). GetPhysicalSize(myWM); // Place the child FinishReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, &wrapperReflowState, myWM, wrapperOffset, borderBoxSize, 0); nsSize contentBoxSize = LogicalSize(myWM, contentBoxISize, contentBoxBSize). GetPhysicalSize(myWM); aDesiredSize.SetBlockStartAscent( wrappersDesiredSize.BlockStartAscent() + outerWrapperFrame->BStart(aReflowState.GetWritingMode(), contentBoxSize)); } LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize); aDesiredSize.SetSize(myWM, logicalDesiredSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); if (outerWrapperFrame) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, outerWrapperFrame); } FinishAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext, nsHTMLReflowMetrics& aButtonDesiredSize, const nsHTMLReflowState& aButtonReflowState, nsIFrame* aFirstKid) { WritingMode wm = GetWritingMode(); LogicalSize availSize = aButtonReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_INTRINSICSIZE; // Buttons have some bonus renderer-determined border/padding, // which occupies part of the button's content-box area: LogicalMargin focusPadding = LogicalMargin(wm, mRenderer.GetAddedButtonBorderAndPadding()); // See whether out availSize's inline-size is big enough. If it's // smaller than our intrinsic min iSize, that means that the kid // wouldn't really fit. In that case, we overflow into our internal // focuspadding (which other browsers don't have) so that there's a // little more space for it. // Note that GetMinISize includes the focusPadding. nscoord IOverflow = GetMinISize(aButtonReflowState.rendContext) - aButtonReflowState.ComputedISize(); nscoord IFocusPadding = focusPadding.IStartEnd(wm); nscoord focusPaddingReduction = std::min(IFocusPadding, std::max(IOverflow, 0)); if (focusPaddingReduction > 0) { nscoord startReduction = focusPadding.IStart(wm); if (focusPaddingReduction != IFocusPadding) { startReduction = NSToCoordRound(startReduction * (float(focusPaddingReduction) / float(IFocusPadding))); } focusPadding.IStart(wm) -= startReduction; focusPadding.IEnd(wm) -= focusPaddingReduction - startReduction; } // shorthand for a value we need to use in a bunch of places const LogicalMargin& clbp = aButtonReflowState.ComputedLogicalBorderPadding(); // Indent the child inside us by the focus border. We must do this separate // from the regular border. availSize.ISize(wm) -= focusPadding.IStartEnd(wm); LogicalPoint childPos(wm); childPos.I(wm) = focusPadding.IStart(wm) + clbp.IStart(wm); availSize.ISize(wm) = std::max(availSize.ISize(wm), 0); // Give child a clone of the button's reflow state, with height/width reduced // by focusPadding, so that descendants with height:100% don't protrude. nsHTMLReflowState adjustedButtonReflowState = CloneReflowStateWithReducedContentBox(aButtonReflowState, focusPadding.GetPhysicalMargin(wm)); nsHTMLReflowState contentsReflowState(aPresContext, adjustedButtonReflowState, aFirstKid, availSize); nsReflowStatus contentsReflowStatus; nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState); childPos.B(wm) = 0; // This will be set properly later, after reflowing the // child to determine its size. // We just pass a dummy containerSize here, as the child will be // repositioned later by FinishReflowChild. nsSize dummyContainerSize; ReflowChild(aFirstKid, aPresContext, contentsDesiredSize, contentsReflowState, wm, childPos, dummyContainerSize, 0, contentsReflowStatus); MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus), "We gave button-contents frame unconstrained available height, " "so it should be complete"); // Compute the button's content-box size: LogicalSize buttonContentBox(wm); if (aButtonReflowState.ComputedBSize() != NS_INTRINSICSIZE) { // Button has a fixed block-size -- that's its content-box bSize. buttonContentBox.BSize(wm) = aButtonReflowState.ComputedBSize(); } else { // Button is intrinsically sized -- it should shrinkwrap the // button-contents' bSize, plus any focus-padding space: buttonContentBox.BSize(wm) = contentsDesiredSize.BSize(wm) + focusPadding.BStartEnd(wm); // Make sure we obey min/max-bSize in the case when we're doing intrinsic // sizing (we get it for free when we have a non-intrinsic // aButtonReflowState.ComputedBSize()). Note that we do this before // adjusting for borderpadding, since mComputedMaxBSize and // mComputedMinBSize are content bSizes. buttonContentBox.BSize(wm) = NS_CSS_MINMAX(buttonContentBox.BSize(wm), aButtonReflowState.ComputedMinBSize(), aButtonReflowState.ComputedMaxBSize()); } if (aButtonReflowState.ComputedISize() != NS_INTRINSICSIZE) { buttonContentBox.ISize(wm) = aButtonReflowState.ComputedISize(); } else { buttonContentBox.ISize(wm) = contentsDesiredSize.ISize(wm) + focusPadding.IStartEnd(wm); buttonContentBox.ISize(wm) = NS_CSS_MINMAX(buttonContentBox.ISize(wm), aButtonReflowState.ComputedMinISize(), aButtonReflowState.ComputedMaxISize()); } // Center child in the block-direction in the button // (technically, inside of the button's focus-padding area) nscoord extraSpace = buttonContentBox.BSize(wm) - focusPadding.BStartEnd(wm) - contentsDesiredSize.BSize(wm); childPos.B(wm) = std::max(0, extraSpace / 2); // Adjust childPos.B() to be in terms of the button's frame-rect, instead of // its focus-padding rect: childPos.B(wm) += focusPadding.BStart(wm) + clbp.BStart(wm); nsSize containerSize = (buttonContentBox + clbp.Size(wm)).GetPhysicalSize(wm); // Place the child FinishReflowChild(aFirstKid, aPresContext, contentsDesiredSize, &contentsReflowState, wm, childPos, containerSize, 0); // Make sure we have a useful 'ascent' value for the child if (contentsDesiredSize.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { WritingMode wm = aButtonReflowState.GetWritingMode(); contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm)); } // OK, we're done with the child frame. // Use what we learned to populate the button frame's reflow metrics. // * Button's height & width are content-box size + border-box contribution: aButtonDesiredSize.SetSize(wm, LogicalSize(wm, aButtonReflowState.ComputedISize() + clbp.IStartEnd(wm), buttonContentBox.BSize(wm) + clbp.BStartEnd(wm))); // * Button's ascent is its child's ascent, plus the child's block-offset // within our frame... unless it's orthogonal, in which case we'll use the // contents inline-size as an approximation for now. // XXX is there a better strategy? should we include border-padding? if (aButtonDesiredSize.GetWritingMode().IsOrthogonalTo(wm)) { aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.ISize(wm)); } else { aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.BlockStartAscent() + childPos.B(wm)); } aButtonDesiredSize.SetOverflowAreasToDesiredBounds(); }
void nsColumnSetFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); // Don't support interruption in columns nsPresContext::InterruptPreventer noInterrupts(aPresContext); DO_GLOBAL_REFLOW_COUNT("nsColumnSetFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); // Initialize OUT parameter aStatus = NS_FRAME_COMPLETE; // Our children depend on our block-size if we have a fixed block-size. if (aReflowState.ComputedBSize() != NS_AUTOHEIGHT) { AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); } else { RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); } #ifdef DEBUG nsFrameList::Enumerator oc(GetChildList(kOverflowContainersList)); for (; !oc.AtEnd(); oc.Next()) { MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(oc.get())); } nsFrameList::Enumerator eoc(GetChildList(kExcessOverflowContainersList)); for (; !eoc.AtEnd(); eoc.Next()) { MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(eoc.get())); } #endif nsOverflowAreas ocBounds; nsReflowStatus ocStatus = NS_FRAME_COMPLETE; if (GetPrevInFlow()) { ReflowOverflowContainerChildren(aPresContext, aReflowState, ocBounds, 0, ocStatus); } //------------ Handle Incremental Reflow ----------------- // If inline size is unconstrained, set aForceAuto to true to allow // the columns to expand in the inline direction. (This typically // happens in orthogonal flows where the inline direction is the // container's block direction). ReflowConfig config = ChooseColumnStrategy(aReflowState, aReflowState.ComputedISize() == NS_UNCONSTRAINEDSIZE); // If balancing, then we allow the last column to grow to unbounded // height during the first reflow. This gives us a way to estimate // what the average column height should be, because we can measure // the heights of all the columns and sum them up. But don't do this // if we have a next in flow because we don't want to suck all its // content back here and then have to push it out again! nsIFrame* nextInFlow = GetNextInFlow(); bool unboundedLastColumn = config.mIsBalancing && !nextInFlow; nsCollapsingMargin carriedOutBottomMargin; ColumnBalanceData colData; colData.mHasExcessBSize = false; bool feasible = ReflowColumns(aDesiredSize, aReflowState, aStatus, config, unboundedLastColumn, &carriedOutBottomMargin, colData); // If we're not balancing, then we're already done, since we should have // reflown all of our children, and there is no need for a binary search to // determine proper column height. if (config.mIsBalancing && !aPresContext->HasPendingInterrupt()) { FindBestBalanceBSize(aReflowState, aPresContext, config, colData, aDesiredSize, carriedOutBottomMargin, unboundedLastColumn, feasible, aStatus); } if (aPresContext->HasPendingInterrupt() && aReflowState.AvailableBSize() == NS_UNCONSTRAINEDSIZE) { // In this situation, we might be lying about our reflow status, because // our last kid (the one that got interrupted) was incomplete. Fix that. aStatus = NS_FRAME_COMPLETE; } NS_ASSERTION(NS_FRAME_IS_FULLY_COMPLETE(aStatus) || aReflowState.AvailableBSize() != NS_UNCONSTRAINEDSIZE, "Column set should be complete if the available block-size is unconstrained"); // Merge overflow container bounds and status. aDesiredSize.mOverflowAreas.UnionWith(ocBounds); NS_MergeReflowStatusInto(&aStatus, ocStatus); FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus, false); aDesiredSize.mCarriedOutBEndMargin = carriedOutBottomMargin; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext, nsHTMLReflowMetrics& aButtonDesiredSize, const nsHTMLReflowState& aButtonReflowState, nsIFrame* aFirstKid) { WritingMode wm = GetWritingMode(); bool isVertical = wm.IsVertical(); LogicalSize availSize = aButtonReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_INTRINSICSIZE; // Buttons have some bonus renderer-determined border/padding, // which occupies part of the button's content-box area: const LogicalMargin focusPadding = LogicalMargin(wm, mRenderer.GetAddedButtonBorderAndPadding()); // shorthand for a value we need to use in a bunch of places const LogicalMargin& clbp = aButtonReflowState.ComputedLogicalBorderPadding(); // Indent the child inside us by the focus border. We must do this separate // from the regular border. availSize.ISize(wm) -= focusPadding.IStartEnd(wm); // See whether out availSize's inline-size is big enough. If it's smaller than // our intrinsic min iSize, that means that the kid wouldn't really fit; for a // better look in such cases we adjust the available iSize and our inline-start // offset to allow the kid to spill start-wards into our padding. nscoord ioffset = focusPadding.IStart(wm) + clbp.IStart(wm); nscoord extraISize = GetMinISize(aButtonReflowState.rendContext) - aButtonReflowState.ComputedISize(); if (extraISize > 0) { nscoord extraIStart = extraISize / 2; nscoord extraIEnd = extraISize - extraIStart; NS_ASSERTION(extraIEnd >=0, "How'd that happen?"); // Do not allow the extras to be bigger than the relevant padding const LogicalMargin& padding = aButtonReflowState.ComputedLogicalPadding(); extraIStart = std::min(extraIStart, padding.IStart(wm)); extraIEnd = std::min(extraIEnd, padding.IEnd(wm)); ioffset -= extraIStart; availSize.ISize(wm) = availSize.ISize(wm) + extraIStart + extraIEnd; } availSize.ISize(wm) = std::max(availSize.ISize(wm), 0); // Give child a clone of the button's reflow state, with height/width reduced // by focusPadding, so that descendants with height:100% don't protrude. nsHTMLReflowState adjustedButtonReflowState = CloneReflowStateWithReducedContentBox(aButtonReflowState, focusPadding.GetPhysicalMargin(wm)); nsHTMLReflowState contentsReflowState(aPresContext, adjustedButtonReflowState, aFirstKid, availSize); nsReflowStatus contentsReflowStatus; nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState); nscoord boffset = focusPadding.BStart(wm) + clbp.BStart(wm); ReflowChild(aFirstKid, aPresContext, contentsDesiredSize, contentsReflowState, isVertical ? boffset : ioffset, isVertical ? ioffset : boffset, 0, contentsReflowStatus); MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus), "We gave button-contents frame unconstrained available height, " "so it should be complete"); // Compute the button's content-box height: nscoord buttonContentBoxBSize = 0; if (aButtonReflowState.ComputedBSize() != NS_INTRINSICSIZE) { // Button has a fixed block-size -- that's its content-box bSize. buttonContentBoxBSize = aButtonReflowState.ComputedBSize(); } else { // Button is intrinsically sized -- it should shrinkwrap the // button-contents' bSize, plus any focus-padding space: buttonContentBoxBSize = contentsDesiredSize.BSize(wm) + focusPadding.BStartEnd(wm); // Make sure we obey min/max-bSize in the case when we're doing intrinsic // sizing (we get it for free when we have a non-intrinsic // aButtonReflowState.ComputedBSize()). Note that we do this before // adjusting for borderpadding, since mComputedMaxBSize and // mComputedMinBSize are content bSizes. buttonContentBoxBSize = NS_CSS_MINMAX(buttonContentBoxBSize, aButtonReflowState.ComputedMinBSize(), aButtonReflowState.ComputedMaxBSize()); } // Center child in the block-direction in the button // (technically, inside of the button's focus-padding area) nscoord extraSpace = buttonContentBoxBSize - focusPadding.BStartEnd(wm) - contentsDesiredSize.BSize(wm); boffset = std::max(0, extraSpace / 2); // Adjust boffset to be in terms of the button's frame-rect, instead of // its focus-padding rect: boffset += focusPadding.BStart(wm) + clbp.BStart(wm); // Place the child FinishReflowChild(aFirstKid, aPresContext, contentsDesiredSize, &contentsReflowState, isVertical ? boffset : ioffset, isVertical ? ioffset : boffset, 0); // Make sure we have a useful 'ascent' value for the child if (contentsDesiredSize.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { WritingMode wm = aButtonReflowState.GetWritingMode(); contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm)); } // OK, we're done with the child frame. // Use what we learned to populate the button frame's reflow metrics. // * Button's height & width are content-box size + border-box contribution: aButtonDesiredSize.SetSize(wm, LogicalSize(wm, aButtonReflowState.ComputedISize() + clbp.IStartEnd(wm), buttonContentBoxBSize + clbp.BStartEnd(wm))); // * Button's ascent is its child's ascent, plus the child's block-offset // within our frame... unless it's orthogonal, in which case we'll use the // contents inline-size as an approximation for now. // XXX is there a better strategy? should we include border-padding? if (aButtonDesiredSize.GetWritingMode().IsOrthogonalTo(wm)) { aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.ISize(wm)); } else { aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.BlockStartAscent() + boffset); } aButtonDesiredSize.SetOverflowAreasToDesiredBounds(); }
nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsPresContext* aPresContext, nsBlockFrame* aFrame, bool aBStartMarginRoot, bool aBEndMarginRoot, bool aBlockNeedsFloatManager, nscoord aConsumedBSize) : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), mContentArea(aReflowState.GetWritingMode()), mPushedFloats(nullptr), mOverflowTracker(nullptr), mBorderPadding(mReflowState.ComputedLogicalBorderPadding()), mPrevBEndMargin(), mLineNumber(0), mFlags(0), mFloatBreakType(NS_STYLE_CLEAR_NONE), mConsumedBSize(aConsumedBSize) { WritingMode wm = aReflowState.GetWritingMode(); SetFlag(BRS_ISFIRSTINFLOW, aFrame->GetPrevInFlow() == nullptr); SetFlag(BRS_ISOVERFLOWCONTAINER, IS_TRUE_OVERFLOW_CONTAINER(aFrame)); nsIFrame::LogicalSides logicalSkipSides = aFrame->GetLogicalSkipSides(&aReflowState); mBorderPadding.ApplySkipSides(logicalSkipSides); // Note that mContainerWidth is the physical width! mContainerWidth = aReflowState.ComputedWidth() + mBorderPadding.LeftRight(wm); if ((aBStartMarginRoot && !logicalSkipSides.BStart()) || 0 != mBorderPadding.BStart(wm)) { SetFlag(BRS_ISBSTARTMARGINROOT, true); SetFlag(BRS_APPLYBSTARTMARGIN, true); } if ((aBEndMarginRoot && !logicalSkipSides.BEnd()) || 0 != mBorderPadding.BEnd(wm)) { SetFlag(BRS_ISBENDMARGINROOT, true); } if (aBlockNeedsFloatManager) { SetFlag(BRS_FLOAT_MGR, true); } mFloatManager = aReflowState.mFloatManager; NS_ASSERTION(mFloatManager, "FloatManager should be set in nsBlockReflowState" ); if (mFloatManager) { // Save the coordinate system origin for later. mFloatManager->GetTranslation(mFloatManagerX, mFloatManagerY); mFloatManager->PushState(&mFloatManagerStateBefore); // never popped } mReflowStatus = NS_FRAME_COMPLETE; mNextInFlow = static_cast<nsBlockFrame*>(mBlock->GetNextInFlow()); NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowState.ComputedISize(), "have unconstrained width; this should only result from " "very large sizes, not attempts at intrinsic width " "calculation"); mContentArea.ISize(wm) = aReflowState.ComputedISize(); // Compute content area height. Unlike the width, if we have a // specified style height we ignore it since extra content is // managed by the "overflow" property. When we don't have a // specified style height then we may end up limiting our height if // the availableHeight is constrained (this situation occurs when we // are paginated). if (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableBSize()) { // We are in a paginated situation. The bottom edge is just inside // the bottom border and padding. The content area height doesn't // include either border or padding edge. mBEndEdge = aReflowState.AvailableBSize() - mBorderPadding.BEnd(wm); mContentArea.BSize(wm) = std::max(0, mBEndEdge - mBorderPadding.BStart(wm)); } else { // When we are not in a paginated situation then we always use // an constrained height. SetFlag(BRS_UNCONSTRAINEDBSIZE, true); mContentArea.BSize(wm) = mBEndEdge = NS_UNCONSTRAINEDSIZE; } mContentArea.IStart(wm) = mBorderPadding.IStart(wm); mBCoord = mContentArea.BStart(wm) = mBorderPadding.BStart(wm); mPrevChild = nullptr; mCurrentLine = aFrame->end_lines(); mMinLineHeight = aReflowState.CalcLineHeight(); }