void nsMathMLmtableOuterFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { nsAutoString value; // we want to return a table that is anchored according to the align attribute nsTableOuterFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); NS_ASSERTION(aDesiredSize.Height() >= 0, "illegal height for mtable"); NS_ASSERTION(aDesiredSize.Width() >= 0, "illegal width for mtable"); // see if the user has set the align attribute on the <mtable> int32_t rowIndex = 0; eAlign tableAlign = eAlign_axis; mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, value); if (!value.IsEmpty()) { ParseAlignAttribute(value, tableAlign, rowIndex); } // adjustments if there is a specified row from where to anchor the table // (conceptually: when there is no row of reference, picture the table as if // it is wrapped in a single big fictional row at dy = 0, this way of // doing so allows us to have a single code path for all cases). nscoord dy = 0; WritingMode wm = aDesiredSize.GetWritingMode(); nscoord blockSize = aDesiredSize.BSize(wm); nsIFrame* rowFrame = nullptr; if (rowIndex) { rowFrame = GetRowFrameAt(rowIndex); if (rowFrame) { // translate the coordinates to be relative to us and in our writing mode nsIFrame* frame = rowFrame; LogicalRect rect(wm, frame->GetRect(), aReflowState.ComputedSizeAsContainerIfConstrained()); blockSize = rect.BSize(wm); do { dy += rect.BStart(wm); frame = frame->GetParent(); } while (frame != this); } } switch (tableAlign) { case eAlign_top: aDesiredSize.SetBlockStartAscent(dy); break; case eAlign_bottom: aDesiredSize.SetBlockStartAscent(dy + blockSize); break; case eAlign_center: aDesiredSize.SetBlockStartAscent(dy + blockSize / 2); break; case eAlign_baseline: if (rowFrame) { // anchor the table on the baseline of the row of reference nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent(); if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline' aDesiredSize.SetBlockStartAscent(dy + rowAscent); break; } } // in other situations, fallback to center aDesiredSize.SetBlockStartAscent(dy + blockSize / 2); break; case eAlign_axis: default: { // XXX should instead use style data from the row of reference here ? RefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), nsLayoutUtils:: FontSizeInflationFor(this)); nscoord axisHeight; GetAxisHeight(aReflowState.rendContext->GetDrawTarget(), fm, axisHeight); if (rowFrame) { // anchor the table on the axis of the row of reference // XXX fallback to baseline because it is a hard problem // XXX need to fetch the axis of the row; would need rowalign=axis to work better nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent(); if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline' aDesiredSize.SetBlockStartAscent(dy + rowAscent); break; } } // in other situations, fallback to using half of the height aDesiredSize.SetBlockStartAscent(dy + blockSize / 2 + axisHeight); } } mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); // just make-up a bounding metrics mBoundingMetrics = nsBoundingMetrics(); mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent(); mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); mBoundingMetrics.width = aDesiredSize.Width(); mBoundingMetrics.leftBearing = 0; mBoundingMetrics.rightBearing = aDesiredSize.Width(); aDesiredSize.mBoundingMetrics = mBoundingMetrics; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
void nsBlockReflowContext::ReflowBlock(const LogicalRect& aSpace, bool aApplyBStartMargin, nsCollapsingMargin& aPrevMargin, nscoord aClearance, bool aIsAdjacentWithBStart, nsLineBox* aLine, nsHTMLReflowState& aFrameRS, nsReflowStatus& aFrameReflowStatus, nsBlockReflowState& aState) { mFrame = aFrameRS.frame; mWritingMode = aState.mReflowState.GetWritingMode(); mContainerWidth = aState.mContainerWidth; mSpace = aSpace; if (!aIsAdjacentWithBStart) { aFrameRS.mFlags.mIsTopOfPage = false; // make sure this is cleared } if (aApplyBStartMargin) { mBStartMargin = aPrevMargin; #ifdef NOISY_BLOCKDIR_MARGINS nsFrame::ListTag(stdout, mOuterReflowState.frame); printf(": reflowing "); nsFrame::ListTag(stdout, mFrame); printf(" margin => %d, clearance => %d\n", mBStartMargin.get(), aClearance); #endif // Adjust the available block size if it's constrained so that the // child frame doesn't think it can reflow into its margin area. if (NS_UNCONSTRAINEDSIZE != aFrameRS.AvailableBSize()) { aFrameRS.AvailableBSize() -= mBStartMargin.get() + aClearance; } } LogicalPoint tPt(mWritingMode); // The values of x and y do not matter for floats, so don't bother // calculating them. Floats are guaranteed to have their own float // manager, so tI and tB don't matter. mICoord and mBCoord don't // matter becacuse they are only used in PlaceBlock, which is not used // for floats. if (aLine) { // Compute inline/block coordinate where reflow will begin. Use the // rules from 10.3.3 to determine what to apply. At this point in the // reflow auto inline-start/end margins will have a zero value. WritingMode frameWM = aFrameRS.GetWritingMode(); mICoord = tPt.I(mWritingMode) = mSpace.IStart(mWritingMode) + aFrameRS.ComputedLogicalMargin().ConvertTo(mWritingMode, frameWM).IStart(mWritingMode); mBCoord = tPt.B(mWritingMode) = mSpace.BStart(mWritingMode) + mBStartMargin.get() + aClearance; if ((mFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR) == 0) aFrameRS.mBlockDelta = mOuterReflowState.mBlockDelta + mBCoord - aLine->BStart(); } // Let frame know that we are reflowing it mFrame->WillReflow(mPresContext); #ifdef DEBUG mMetrics.ISize(mWritingMode) = nscoord(0xdeadbeef); mMetrics.BSize(mWritingMode) = nscoord(0xdeadbeef); #endif WritingMode oldWM = mOuterReflowState.mFloatManager->Translate(mWritingMode, tPt); mFrame->Reflow(mPresContext, mMetrics, aFrameRS, aFrameReflowStatus); mOuterReflowState.mFloatManager->Untranslate(oldWM, tPt); #ifdef DEBUG if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) { if (CRAZY_SIZE(mMetrics.ISize(mWritingMode)) || CRAZY_SIZE(mMetrics.BSize(mWritingMode))) { printf("nsBlockReflowContext: "); nsFrame::ListTag(stdout, mFrame); printf(" metrics=%d,%d!\n", mMetrics.ISize(mWritingMode), mMetrics.BSize(mWritingMode)); } if ((mMetrics.ISize(mWritingMode) == nscoord(0xdeadbeef)) || (mMetrics.BSize(mWritingMode) == nscoord(0xdeadbeef))) { printf("nsBlockReflowContext: "); nsFrame::ListTag(stdout, mFrame); printf(" didn't set i/b %d,%d!\n", mMetrics.ISize(mWritingMode), mMetrics.BSize(mWritingMode)); } } #endif if (!mFrame->HasOverflowAreas()) { mMetrics.SetOverflowAreasToDesiredBounds(); } if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus) || (mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { // If frame is complete and has a next-in-flow, we need to delete // them now. Do not do this when a break-before is signaled because // the frame is going to get reflowed again (and may end up wanting // a next-in-flow where it ends up), unless it is an out of flow frame. if (NS_FRAME_IS_FULLY_COMPLETE(aFrameReflowStatus)) { nsIFrame* kidNextInFlow = mFrame->GetNextInFlow(); if (nullptr != kidNextInFlow) { // Remove all of the childs next-in-flows. Make sure that we ask // the right parent to do the removal (it's possible that the // parent is not this because we are executing pullup code). // Floats will eventually be removed via nsBlockFrame::RemoveFloat // which detaches the placeholder from the float. nsOverflowContinuationTracker::AutoFinish fini(aState.mOverflowTracker, mFrame); kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true); } } } }
bool nsBlockReflowContext::ComputeCollapsedBStartMargin(const nsHTMLReflowState& aRS, nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame, bool* aMayNeedRetry, bool* aBlockIsEmpty) { WritingMode wm = aRS.GetWritingMode(); WritingMode parentWM = mMetrics.GetWritingMode(); // Include block-start element of frame's margin aMargin->Include(aRS.ComputedLogicalMargin().ConvertTo(parentWM, wm).BStart(parentWM)); // The inclusion of the block-end margin when empty is done by the caller // since it doesn't need to be done by the top-level (non-recursive) // caller. #ifdef NOISY_BLOCKDIR_MARGINS nsFrame::ListTag(stdout, aRS.frame); printf(": %d => %d\n", aRS.ComputedLogicalMargin().BStart(wm), aMargin->get()); #endif bool dirtiedLine = false; bool setBlockIsEmpty = false; // Calculate the frame's generational block-start-margin from its child // blocks. Note that if the frame has a non-zero block-start-border or // block-start-padding then this step is skipped because it will be a margin // root. It is also skipped if the frame is a margin root for other // reasons. nsIFrame* frame = DescendIntoBlockLevelFrame(aRS.frame); nsPresContext* prescontext = frame->PresContext(); nsBlockFrame* block = nullptr; if (0 == aRS.ComputedLogicalBorderPadding().BStart(wm)) { block = nsLayoutUtils::GetAsBlock(frame); if (block) { bool bStartMarginRoot, unused; block->IsMarginRoot(&bStartMarginRoot, &unused); if (bStartMarginRoot) { block = nullptr; } } } // iterate not just through the lines of 'block' but also its // overflow lines and the normal and overflow lines of its next in // flows. Note that this will traverse some frames more than once: // for example, if A contains B and A->nextinflow contains // B->nextinflow, we'll traverse B->nextinflow twice. But this is // OK because our traversal is idempotent. for ( ;block; block = static_cast<nsBlockFrame*>(block->GetNextInFlow())) { for (int overflowLines = 0; overflowLines <= 1; ++overflowLines) { nsBlockFrame::line_iterator line; nsBlockFrame::line_iterator line_end; bool anyLines = true; if (overflowLines) { nsBlockFrame::FrameLines* frames = block->GetOverflowLines(); nsLineList* lines = frames ? &frames->mLines : nullptr; if (!lines) { anyLines = false; } else { line = lines->begin(); line_end = lines->end(); } } else { line = block->begin_lines(); line_end = block->end_lines(); } for (; anyLines && line != line_end; ++line) { if (!aClearanceFrame && line->HasClearance()) { // If we don't have a clearance frame, then we're computing // the collapsed margin in the first pass, assuming that all // lines have no clearance. So clear their clearance flags. line->ClearHasClearance(); line->MarkDirty(); dirtiedLine = true; } bool isEmpty; if (line->IsInline()) { isEmpty = line->IsEmpty(); } else { nsIFrame* kid = line->mFirstChild; if (kid == aClearanceFrame) { line->SetHasClearance(); line->MarkDirty(); dirtiedLine = true; goto done; } // Here is where we recur. Now that we have determined that a // generational collapse is required we need to compute the // child blocks margin and so in so that we can look into // it. For its margins to be computed we need to have a reflow // state for it. // We may have to construct an extra reflow state here if // we drilled down through a block wrapper. At the moment // we can only drill down one level so we only have to support // one extra reflow state. const nsHTMLReflowState* outerReflowState = &aRS; if (frame != aRS.frame) { NS_ASSERTION(frame->GetParent() == aRS.frame, "Can only drill through one level of block wrapper"); LogicalSize availSpace = aRS.ComputedSize(frame->GetWritingMode()); outerReflowState = new nsHTMLReflowState(prescontext, aRS, frame, availSpace); } { LogicalSize availSpace = outerReflowState->ComputedSize(kid->GetWritingMode()); nsHTMLReflowState innerReflowState(prescontext, *outerReflowState, kid, availSpace); // Record that we're being optimistic by assuming the kid // has no clearance if (kid->StyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) { *aMayNeedRetry = true; } if (ComputeCollapsedBStartMargin(innerReflowState, aMargin, aClearanceFrame, aMayNeedRetry, &isEmpty)) { line->MarkDirty(); dirtiedLine = true; } if (isEmpty) { WritingMode innerWM = innerReflowState.GetWritingMode(); LogicalMargin innerMargin = innerReflowState.ComputedLogicalMargin().ConvertTo(parentWM, innerWM); aMargin->Include(innerMargin.BEnd(parentWM)); } } if (outerReflowState != &aRS) { delete const_cast<nsHTMLReflowState*>(outerReflowState); } } if (!isEmpty) { if (!setBlockIsEmpty && aBlockIsEmpty) { setBlockIsEmpty = true; *aBlockIsEmpty = false; } goto done; } } if (!setBlockIsEmpty && aBlockIsEmpty) { // The first time we reach here is when this is the first block // and we have processed all its normal lines. setBlockIsEmpty = true; // All lines are empty, or we wouldn't be here! *aBlockIsEmpty = aRS.frame->IsSelfEmpty(); } } } done: if (!setBlockIsEmpty && aBlockIsEmpty) { *aBlockIsEmpty = aRS.frame->IsEmpty(); } #ifdef NOISY_BLOCKDIR_MARGINS nsFrame::ListTag(stdout, aRS.frame); printf(": => %d\n", aMargin->get()); #endif return dirtiedLine; }
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 = GetStyleVisibility()->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(); if (mLastBalanceHeight != aConfig.mColMaxHeight) { 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 const nsMargin &borderPadding = aReflowState.mComputedBorderPadding; 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. nscoord targetX = borderPadding.left; if (RTL) { nscoord availWidth = aReflowState.availableWidth; if (aReflowState.ComputedWidth() != NS_INTRINSICSIZE) { availWidth = aReflowState.ComputedWidth(); } if (availWidth != NS_INTRINSICSIZE) { childOrigin.x += availWidth - aConfig.mColWidth; targetX += aConfig.mExpectedWidthLeftOver; #ifdef DEBUG_roc printf("*** childOrigin.x = %d\n", childOrigin.x); #endif } } int columnCount = 0; int contentBottom = 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 childContentBottom = 0; 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; } childContentBottom = nsLayoutUtils::CalculateContentBottom(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 availSize(aConfig.mColWidth, aConfig.mColMaxHeight); if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) { availSize.height = GetAvailableContentHeight(aReflowState); } if (reflowNext) child->AddStateBits(NS_FRAME_IS_DIRTY); nsHTMLReflowState kidReflowState(PresContext(), aReflowState, child, availSize, availSize.width, aReflowState.ComputedHeight()); kidReflowState.mFlags.mIsTopOfPage = true; kidReflowState.mFlags.mTableIsSplittable = false; kidReflowState.mFlags.mIsColumnBalancing = aConfig.mBalanceColCount < PR_INT32_MAX; #ifdef DEBUG_roc printf("*** Reflowing child #%d %p: availHeight=%d\n", columnCount, (void*)child,availSize.height); #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(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.mComputedMargin.left, childOrigin.y + kidReflowState.mComputedMargin.top, 0, aStatus); reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0; #ifdef DEBUG_roc printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d\n", columnCount, (void*)child, aStatus, kidDesiredSize.width, kidDesiredSize.height); #endif NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus); *aBottomMarginCarriedOut = kidDesiredSize.mCarriedOutBottomMargin; FinishReflowChild(child, PresContext(), &kidReflowState, kidDesiredSize, childOrigin.x, childOrigin.y, 0); childContentBottom = nsLayoutUtils::CalculateContentBottom(child); if (childContentBottom > aConfig.mColMaxHeight) { allFit = false; } if (childContentBottom > availSize.height) { aColData.mMaxOverflowingHeight = NS_MAX(childContentBottom, aColData.mMaxOverflowingHeight); } } contentRect.UnionRect(contentRect, child->GetRect()); ConsiderChildOverflow(overflowRects, child); contentBottom = NS_MAX(contentBottom, childContentBottom); aColData.mLastHeight = childContentBottom; aColData.mSumHeight += childContentBottom; // 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 = nsnull; 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(PresContext(), child, kidNextInFlow); if (NS_FAILED(rv)) { NS_NOTREACHED("Couldn't create continuation"); child = nsnull; 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 (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(PresContext(), continuationColumns); } child = nsnull; 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); } } // If we're doing RTL, we need to make sure our last column is at the left-hand side of the frame. if (RTL && childOrigin.x != targetX) { overflowRects.Clear(); contentRect = nsRect(0, 0, 0, 0); PRInt32 deltaX = targetX - childOrigin.x; #ifdef DEBUG_roc printf("*** CHILDORIGIN.x = %d, targetX = %d, DELTAX = %d\n", childOrigin.x, targetX, deltaX); #endif for (child = mFrames.FirstChild(); child; child = child->GetNextSibling()) { MoveChildTo(this, child, child->GetPosition() + nsPoint(deltaX, 0)); ConsiderChildOverflow(overflowRects, child); contentRect.UnionRect(contentRect, child->GetRect()); } } aColData.mMaxHeight = contentBottom; contentRect.height = NS_MAX(contentRect.height, contentBottom); mLastFrameStatus = aStatus; // contentRect included the borderPadding.left,borderPadding.top of the child rects contentRect -= nsPoint(borderPadding.left, borderPadding.top); nsSize contentSize = nsSize(contentRect.XMost(), contentRect.YMost()); // Apply computed and min/max values if (aReflowState.ComputedHeight() != NS_INTRINSICSIZE) { contentSize.height = aReflowState.ComputedHeight(); } else { if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxHeight) { contentSize.height = NS_MIN(aReflowState.mComputedMaxHeight, contentSize.height); } if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinHeight) { contentSize.height = NS_MAX(aReflowState.mComputedMinHeight, contentSize.height); } } if (aReflowState.ComputedWidth() != NS_INTRINSICSIZE) { contentSize.width = aReflowState.ComputedWidth(); } else { if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { contentSize.width = NS_MIN(aReflowState.mComputedMaxWidth, contentSize.width); } if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) { contentSize.width = NS_MAX(aReflowState.mComputedMinWidth, contentSize.width); } } aDesiredSize.height = borderPadding.top + contentSize.height + borderPadding.bottom; aDesiredSize.width = contentSize.width + borderPadding.left + borderPadding.right; 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); }
NS_IMETHODIMP nsMathMLmfencedFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { nsresult rv; aDesiredSize.Width() = aDesiredSize.Height() = 0; aDesiredSize.SetTopAscent(0); aDesiredSize.mBoundingMetrics = nsBoundingMetrics(); int32_t i; const nsStyleFont* font = StyleFont(); nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); aReflowState.rendContext->SetFont(fm); nscoord axisHeight, em; GetAxisHeight(*aReflowState.rendContext, fm, axisHeight); GetEmHeight(fm, em); // leading to be left at the top and the bottom of stretched chars nscoord leading = NSToCoordRound(0.2f * em); ///////////// // Reflow children // Asking each child to cache its bounding metrics // Note that we don't use the base method nsMathMLContainerFrame::Reflow() // because we want to stretch our fences, separators and stretchy frames using // the *same* initial aDesiredSize.mBoundingMetrics. If we were to use the base // method here, our stretchy frames will be stretched and placed, and we may // end up stretching our fences/separators with a different aDesiredSize. // XXX The above decision was revisited in bug 121748 and this code can be // refactored to use nsMathMLContainerFrame::Reflow() at some stage. nsReflowStatus childStatus; nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE); nsIFrame* firstChild = GetFirstPrincipalChild(); nsIFrame* childFrame = firstChild; nscoord ascent = 0, descent = 0; if (firstChild || mOpenChar || mCloseChar || mSeparatorsCount > 0) { // We use the ASCII metrics to get our minimum height. This way, // if we have borders or a background, they will fit better with // other elements on the line. ascent = fm->MaxAscent(); descent = fm->MaxDescent(); } while (childFrame) { nsHTMLReflowMetrics childDesiredSize(aReflowState.GetWritingMode(), aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS); nsHTMLReflowState childReflowState(aPresContext, aReflowState, childFrame, availSize); rv = ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowState, childStatus); //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status"); if (NS_FAILED(rv)) { // Call DidReflow() for the child frames we successfully did reflow. DidReflowChildren(firstChild, childFrame); return rv; } SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, childDesiredSize.mBoundingMetrics); nscoord childDescent = childDesiredSize.Height() - childDesiredSize.TopAscent(); if (descent < childDescent) descent = childDescent; if (ascent < childDesiredSize.TopAscent()) ascent = childDesiredSize.TopAscent(); childFrame = childFrame->GetNextSibling(); } ///////////// // Ask stretchy children to stretch themselves nsBoundingMetrics containerSize; nsStretchDirection stretchDir = NS_STRETCH_DIRECTION_VERTICAL; GetPreferredStretchSize(*aReflowState.rendContext, 0, /* i.e., without embellishments */ stretchDir, containerSize); childFrame = firstChild; while (childFrame) { nsIMathMLFrame* mathmlChild = do_QueryFrame(childFrame); if (mathmlChild) { nsHTMLReflowMetrics childDesiredSize(aReflowState.GetWritingMode()); // retrieve the metrics that was stored at the previous pass GetReflowAndBoundingMetricsFor(childFrame, childDesiredSize, childDesiredSize.mBoundingMetrics); mathmlChild->Stretch(*aReflowState.rendContext, stretchDir, containerSize, childDesiredSize); // store the updated metrics SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, childDesiredSize.mBoundingMetrics); nscoord childDescent = childDesiredSize.Height() - childDesiredSize.TopAscent(); if (descent < childDescent) descent = childDescent; if (ascent < childDesiredSize.TopAscent()) ascent = childDesiredSize.TopAscent(); } childFrame = childFrame->GetNextSibling(); } // bug 121748: for surrounding fences & separators, use a size that covers everything GetPreferredStretchSize(*aReflowState.rendContext, STRETCH_CONSIDER_EMBELLISHMENTS, stretchDir, containerSize); ////////////////////////////////////////// // Prepare the opening fence, separators, and closing fence, and // adjust the origin of children. // we need to center around the axis nscoord delta = std::max(containerSize.ascent - axisHeight, containerSize.descent + axisHeight); containerSize.ascent = delta + axisHeight; containerSize.descent = delta - axisHeight; bool isRTL = StyleVisibility()->mDirection; ///////////////// // opening fence ... ReflowChar(aPresContext, *aReflowState.rendContext, mOpenChar, NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel, axisHeight, leading, em, containerSize, ascent, descent, isRTL); ///////////////// // separators ... for (i = 0; i < mSeparatorsCount; i++) { ReflowChar(aPresContext, *aReflowState.rendContext, &mSeparatorsChar[i], NS_MATHML_OPERATOR_FORM_INFIX, font->mScriptLevel, axisHeight, leading, em, containerSize, ascent, descent, isRTL); } ///////////////// // closing fence ... ReflowChar(aPresContext, *aReflowState.rendContext, mCloseChar, NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel, axisHeight, leading, em, containerSize, ascent, descent, isRTL); ////////////////// // Adjust the origins of each child. // and update our bounding metrics i = 0; nscoord dx = 0; nsBoundingMetrics bm; bool firstTime = true; nsMathMLChar *leftChar, *rightChar; if (isRTL) { leftChar = mCloseChar; rightChar = mOpenChar; } else { leftChar = mOpenChar; rightChar = mCloseChar; } if (leftChar) { PlaceChar(leftChar, ascent, bm, dx); aDesiredSize.mBoundingMetrics = bm; firstTime = false; } if (isRTL) { childFrame = this->GetLastChild(nsIFrame::kPrincipalList); } else { childFrame = firstChild; } while (childFrame) { nsHTMLReflowMetrics childSize(aReflowState.GetWritingMode()); GetReflowAndBoundingMetricsFor(childFrame, childSize, bm); if (firstTime) { firstTime = false; aDesiredSize.mBoundingMetrics = bm; } else aDesiredSize.mBoundingMetrics += bm; FinishReflowChild(childFrame, aPresContext, childSize, nullptr, dx, ascent - childSize.TopAscent(), 0); dx += childSize.Width(); if (i < mSeparatorsCount) { PlaceChar(&mSeparatorsChar[isRTL ? mSeparatorsCount - 1 - i : i], ascent, bm, dx); aDesiredSize.mBoundingMetrics += bm; } i++; if (isRTL) { childFrame = childFrame->GetPrevSibling(); } else { childFrame = childFrame->GetNextSibling(); } } if (rightChar) { PlaceChar(rightChar, ascent, bm, dx); if (firstTime) aDesiredSize.mBoundingMetrics = bm; else aDesiredSize.mBoundingMetrics += bm; } aDesiredSize.Width() = aDesiredSize.mBoundingMetrics.width; aDesiredSize.Height() = ascent + descent; aDesiredSize.SetTopAscent(ascent); SetBoundingMetrics(aDesiredSize.mBoundingMetrics); SetReference(nsPoint(0, aDesiredSize.TopAscent())); // see if we should fix the spacing FixInterFrameSpacing(aDesiredSize); // Finished with these: ClearSavedChildMetrics(); // Set our overflow area GatherAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return NS_OK; }
void nsRangeFrame::ReflowAnonymousContent(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState) { // The width/height of our content box, which is the available width/height // for our anonymous content: nscoord rangeFrameContentBoxWidth = aReflowState.ComputedWidth(); nscoord rangeFrameContentBoxHeight = aReflowState.ComputedHeight(); if (rangeFrameContentBoxHeight == NS_AUTOHEIGHT) { rangeFrameContentBoxHeight = 0; } nsIFrame* trackFrame = mTrackDiv->GetPrimaryFrame(); if (trackFrame) { // display:none? // Position the track: // The idea here is that we allow content authors to style the width, // height, border and padding of the track, but we ignore margin and // positioning properties and do the positioning ourself to keep the center // of the track's border box on the center of the nsRangeFrame's content // box. WritingMode wm = trackFrame->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState trackReflowState(aPresContext, aReflowState, trackFrame, availSize); // Find the x/y position of the track frame such that it will be positioned // as described above. These coordinates are with respect to the // nsRangeFrame's border-box. nscoord trackX = rangeFrameContentBoxWidth / 2; nscoord trackY = rangeFrameContentBoxHeight / 2; // Account for the track's border and padding (we ignore its margin): trackX -= trackReflowState.ComputedPhysicalBorderPadding().left + trackReflowState.ComputedWidth() / 2; trackY -= trackReflowState.ComputedPhysicalBorderPadding().top + trackReflowState.ComputedHeight() / 2; // Make relative to our border box instead of our content box: trackX += aReflowState.ComputedPhysicalBorderPadding().left; trackY += aReflowState.ComputedPhysicalBorderPadding().top; nsReflowStatus frameStatus; nsHTMLReflowMetrics trackDesiredSize(aReflowState); ReflowChild(trackFrame, aPresContext, trackDesiredSize, trackReflowState, trackX, trackY, 0, frameStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(frameStatus), "We gave our child unconstrained height, so it should be complete"); FinishReflowChild(trackFrame, aPresContext, trackDesiredSize, &trackReflowState, trackX, trackY, 0); } nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); if (thumbFrame) { // display:none? WritingMode wm = thumbFrame->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState thumbReflowState(aPresContext, aReflowState, thumbFrame, availSize); // Where we position the thumb depends on its size, so we first reflow // the thumb at {0,0} to obtain its size, then position it afterwards. nsReflowStatus frameStatus; nsHTMLReflowMetrics thumbDesiredSize(aReflowState); ReflowChild(thumbFrame, aPresContext, thumbDesiredSize, thumbReflowState, 0, 0, 0, frameStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(frameStatus), "We gave our child unconstrained height, so it should be complete"); FinishReflowChild(thumbFrame, aPresContext, thumbDesiredSize, &thumbReflowState, 0, 0, 0); DoUpdateThumbPosition(thumbFrame, nsSize(aDesiredSize.Width(), aDesiredSize.Height())); } nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame(); if (rangeProgressFrame) { // display:none? WritingMode wm = rangeProgressFrame->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState progressReflowState(aPresContext, aReflowState, rangeProgressFrame, availSize); // We first reflow the range-progress frame at {0,0} to obtain its // unadjusted dimensions, then we adjust it to so that the appropriate edge // ends at the thumb. nsReflowStatus frameStatus; nsHTMLReflowMetrics progressDesiredSize(aReflowState); ReflowChild(rangeProgressFrame, aPresContext, progressDesiredSize, progressReflowState, 0, 0, 0, frameStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(frameStatus), "We gave our child unconstrained height, so it should be complete"); FinishReflowChild(rangeProgressFrame, aPresContext, progressDesiredSize, &progressReflowState, 0, 0, 0); DoUpdateRangeProgressFrame(rangeProgressFrame, nsSize(aDesiredSize.Width(), aDesiredSize.Height())); } }
void nsSubDocumentFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d", aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); aStatus = NS_FRAME_COMPLETE; NS_ASSERTION(mContent->GetPrimaryFrame() == this, "Shouldn't happen"); // XUL <iframe> or <browser>, or HTML <iframe>, <object> or <embed> nsLeafFrame::DoReflow(aPresContext, aDesiredSize, aReflowState, aStatus); // "offset" is the offset of our content area from our frame's // top-left corner. nsPoint offset = nsPoint(aReflowState.ComputedPhysicalBorderPadding().left, aReflowState.ComputedPhysicalBorderPadding().top); if (mInnerView) { const nsMargin& bp = aReflowState.ComputedPhysicalBorderPadding(); nsSize innerSize(aDesiredSize.Width() - bp.LeftRight(), aDesiredSize.Height() - bp.TopBottom()); // Size & position the view according to 'object-fit' & 'object-position'. nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame(); IntrinsicSize intrinsSize; nsSize intrinsRatio; if (subDocRoot) { intrinsSize = subDocRoot->GetIntrinsicSize(); intrinsRatio = subDocRoot->GetIntrinsicRatio(); } nsRect destRect = nsLayoutUtils::ComputeObjectDestRect(nsRect(offset, innerSize), intrinsSize, intrinsRatio, StylePosition()); nsViewManager* vm = mInnerView->GetViewManager(); vm->MoveViewTo(mInnerView, destRect.x, destRect.y); vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), destRect.Size()), true); } aDesiredSize.SetOverflowAreasToDesiredBounds(); if (!ShouldClipSubdocument()) { nsIFrame* subdocRootFrame = GetSubdocumentRootFrame(); if (subdocRootFrame) { aDesiredSize.mOverflowAreas.UnionWith(subdocRootFrame->GetOverflowAreas() + offset); } } FinishAndStoreOverflow(&aDesiredSize); if (!aPresContext->IsPaginated() && !mPostedReflowCallback) { PresContext()->PresShell()->PostReflowCallback(this); mPostedReflowCallback = true; } NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%x", aDesiredSize.Width(), aDesiredSize.Height(), aStatus)); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
NS_IMETHODIMP ViewportFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("ViewportFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_FRAME_TRACE_REFLOW_IN("ViewportFrame::Reflow"); // Initialize OUT parameters aStatus = NS_FRAME_COMPLETE; // Because |Reflow| sets mComputedHeight on the child to // availableHeight. AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); // 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 the main content first so that the placeholders of the // fixed-position frames will be in the right places on an initial // reflow. nscoord kidHeight = 0; nsresult rv = NS_OK; if (mFrames.NotEmpty()) { // Deal with a non-incremental reflow or an incremental reflow // targeted at our one-and-only principal child frame. if (aReflowState.ShouldReflowAllKids() || aReflowState.mFlags.mVResize || NS_SUBTREE_DIRTY(mFrames.FirstChild())) { // Reflow our one-and-only principal child frame nsIFrame* kidFrame = mFrames.FirstChild(); nsHTMLReflowMetrics kidDesiredSize; nsSize availableSpace(aReflowState.availableWidth, aReflowState.availableHeight); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, kidFrame, availableSpace); // Reflow the frame kidReflowState.SetComputedHeight(aReflowState.ComputedHeight()); rv = ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState, 0, 0, 0, aStatus); kidHeight = kidDesiredSize.height; FinishReflowChild(kidFrame, aPresContext, nsnull, kidDesiredSize, 0, 0, 0); } else { kidHeight = mFrames.FirstChild()->GetSize().height; } } NS_ASSERTION(aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE, "shouldn't happen anymore"); // Return the max size as our desired size aDesiredSize.width = aReflowState.availableWidth; // Being flowed initially at an unconstrained height means we should // return our child's intrinsic size. aDesiredSize.height = aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE ? aReflowState.ComputedHeight() : kidHeight; // Make a copy of the reflow state and change the computed width and height // to reflect the available space for the fixed items nsHTMLReflowState reflowState(aReflowState); nsPoint offset = AdjustReflowStateForScrollbars(&reflowState); #ifdef DEBUG NS_ASSERTION(mFixedContainer.GetChildList().IsEmpty() || (offset.x == 0 && offset.y == 0), "We don't handle correct positioning of fixed frames with " "scrollbars in odd positions"); #endif // Just reflow all the fixed-pos frames. rv = mFixedContainer.Reflow(this, aPresContext, reflowState, aStatus, reflowState.ComputedWidth(), reflowState.ComputedHeight(), PR_FALSE, PR_TRUE, PR_TRUE, // XXX could be optimized nsnull /* ignore overflow */); // If we were dirty then do a repaint if (GetStateBits() & NS_FRAME_IS_DIRTY) { nsRect damageRect(0, 0, aDesiredSize.width, aDesiredSize.height); Invalidate(damageRect); } // XXX Should we do something to clip our children to this? aDesiredSize.SetOverflowAreasToDesiredBounds(); NS_FRAME_TRACE_REFLOW_OUT("ViewportFrame::Reflow", aStatus); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return rv; }
void nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsSVGOuterSVGFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsSVGOuterSVGFrame::Reflow: availSize=%d,%d", aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; aDesiredSize.Width() = aReflowState.ComputedWidth() + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); aDesiredSize.Height() = aReflowState.ComputedHeight() + aReflowState.ComputedPhysicalBorderPadding().TopBottom(); NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages."); SVGSVGElement *svgElem = static_cast<SVGSVGElement*>(mContent); nsSVGOuterSVGAnonChildFrame *anonKid = static_cast<nsSVGOuterSVGAnonChildFrame*>(GetFirstPrincipalChild()); if (mState & NS_FRAME_FIRST_REFLOW) { // Initialize svgElem->UpdateHasChildrenOnlyTransform(); } // If our SVG viewport has changed, update our content and notify. // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace svgFloatSize newViewportSize( nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedWidth()), nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedHeight())); svgFloatSize oldViewportSize = svgElem->GetViewportSize(); uint32_t changeBits = 0; if (newViewportSize != oldViewportSize) { // When our viewport size changes, we may need to update the overflow rects // of our child frames. This is the case if: // // * We have a real/synthetic viewBox (a children-only transform), since // the viewBox transform will change as the viewport dimensions change. // // * We do not have a real/synthetic viewBox, but the last time we // reflowed (or the last time UpdateOverflow() was called) we did. // // We only handle the former case here, in which case we mark all our child // frames as dirty so that we reflow them below and update their overflow // rects. // // In the latter case, updating of overflow rects is handled for removal of // real viewBox (the viewBox attribute) in AttributeChanged. Synthetic // viewBox "removal" (e.g. a document references the same SVG via both an // <svg:image> and then as a CSS background image (a synthetic viewBox is // used when painting the former, but not when painting the latter)) is // handled in SVGSVGElement::FlushImageTransformInvalidation. // if (svgElem->HasViewBoxOrSyntheticViewBox()) { nsIFrame* anonChild = GetFirstPrincipalChild(); anonChild->AddStateBits(NS_FRAME_IS_DIRTY); for (nsIFrame* child = anonChild->GetFirstPrincipalChild(); child; child = child->GetNextSibling()) { child->AddStateBits(NS_FRAME_IS_DIRTY); } } changeBits |= COORD_CONTEXT_CHANGED; svgElem->SetViewportSize(newViewportSize); } if (mFullZoom != PresContext()->GetFullZoom()) { changeBits |= FULL_ZOOM_CHANGED; mFullZoom = PresContext()->GetFullZoom(); } if (changeBits) { NotifyViewportOrTransformChanged(changeBits); } mViewportInitialized = true; // Now that we've marked the necessary children as dirty, call // ReflowSVG() or ReflowSVGNonDisplayText() on them, depending // on whether we are non-display. mCallingReflowSVG = true; if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) { ReflowSVGNonDisplayText(this); } else { // Update the mRects and visual overflow rects of all our descendants, // including our anonymous wrapper kid: anonKid->AddStateBits(mState & NS_FRAME_IS_DIRTY); anonKid->ReflowSVG(); NS_ABORT_IF_FALSE(!anonKid->GetNextSibling(), "We should have one anonymous child frame wrapping our real children"); } mCallingReflowSVG = false; // Set our anonymous kid's offset from our border box: anonKid->SetPosition(GetContentRectRelativeToSelf().TopLeft()); // Including our size in our overflow rects regardless of the value of // 'background', 'border', etc. makes sure that we usually (when we clip to // our content area) don't have to keep changing our overflow rects as our // descendants move about (see perf comment below). Including our size in our // scrollable overflow rect also makes sure that we scroll if we're too big // for our viewport. // // <svg> never allows scrolling to anything outside its mRect (only panning), // so we must always keep our scrollable overflow set to our size. // // With regards to visual overflow, we always clip root-<svg> (see our // BuildDisplayList method) regardless of the value of the 'overflow' // property since that is per-spec, even for the initial 'visible' value. For // that reason there's no point in adding descendant visual overflow to our // own when this frame is for a root-<svg>. That said, there's also a very // good performance reason for us wanting to avoid doing so. If we did, then // the frame's overflow would often change as descendants that are partially // or fully outside its rect moved (think animation on/off screen), and that // would cause us to do a full NS_FRAME_IS_DIRTY reflow and repaint of the // entire document tree each such move (see bug 875175). // // So it's only non-root outer-<svg> that has the visual overflow of its // descendants added to its own. (Note that the default user-agent style // sheet makes 'hidden' the default value for :not(root(svg)), so usually // FinishAndStoreOverflow will still clip this back to the frame's rect.) // // WARNING!! Keep UpdateBounds below in sync with whatever we do for our // overflow rects here! (Again, see bug 875175.) // aDesiredSize.SetOverflowAreasToDesiredBounds(); if (!mIsRootContent) { aDesiredSize.mOverflowAreas.VisualOverflow().UnionRect( aDesiredSize.mOverflowAreas.VisualOverflow(), anonKid->GetVisualOverflowRect() + anonKid->GetPosition()); } FinishAndStoreOverflow(&aDesiredSize); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsSVGOuterSVGFrame::Reflow: size=%d,%d", aDesiredSize.Width(), aDesiredSize.Height())); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
PRBool nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS, nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame, PRBool* aMayNeedRetry, PRBool* aBlockIsEmpty) { // Include frame's top margin aMargin->Include(aRS.mComputedMargin.top); // The inclusion of the bottom margin when empty is done by the caller // since it doesn't need to be done by the top-level (non-recursive) // caller. #ifdef NOISY_VERTICAL_MARGINS nsFrame::ListTag(stdout, aRS.frame); printf(": %d => %d\n", aRS.mComputedMargin.top, aMargin->get()); #endif PRBool dirtiedLine = PR_FALSE; PRBool setBlockIsEmpty = PR_FALSE; // Calculate the frame's generational top-margin from its child // blocks. Note that if the frame has a non-zero top-border or // top-padding then this step is skipped because it will be a margin // root. It is also skipped if the frame is a margin root for other // reasons. void* bf; nsIFrame* frame = DescendIntoBlockLevelFrame(aRS.frame); nsPresContext* prescontext = frame->PresContext(); if (0 == aRS.mComputedBorderPadding.top && NS_SUCCEEDED(frame->QueryInterface(kBlockFrameCID, &bf)) && !nsBlockFrame::BlockIsMarginRoot(frame)) { // iterate not just through the lines of 'block' but also its // overflow lines and the normal and overflow lines of its next in // flows. Note that this will traverse some frames more than once: // for example, if A contains B and A->nextinflow contains // B->nextinflow, we'll traverse B->nextinflow twice. But this is // OK because our traversal is idempotent. for (nsBlockFrame* block = static_cast<nsBlockFrame*>(frame); block; block = static_cast<nsBlockFrame*>(block->GetNextInFlow())) { for (PRBool overflowLines = PR_FALSE; overflowLines <= PR_TRUE; ++overflowLines) { nsBlockFrame::line_iterator line; nsBlockFrame::line_iterator line_end; PRBool anyLines = PR_TRUE; if (overflowLines) { nsLineList* lines = block->GetOverflowLines(); if (!lines) { anyLines = PR_FALSE; } else { line = lines->begin(); line_end = lines->end(); } } else { line = block->begin_lines(); line_end = block->end_lines(); } for (; anyLines && line != line_end; ++line) { if (!aClearanceFrame && line->HasClearance()) { // If we don't have a clearance frame, then we're computing // the collapsed margin in the first pass, assuming that all // lines have no clearance. So clear their clearance flags. line->ClearHasClearance(); line->MarkDirty(); dirtiedLine = PR_TRUE; } PRBool isEmpty; if (line->IsInline()) { isEmpty = line->IsEmpty(); } else { nsIFrame* kid = line->mFirstChild; if (kid == aClearanceFrame) { line->SetHasClearance(); line->MarkDirty(); dirtiedLine = PR_TRUE; goto done; } // Here is where we recur. Now that we have determined that a // generational collapse is required we need to compute the // child blocks margin and so in so that we can look into // it. For its margins to be computed we need to have a reflow // state for it. // We may have to construct an extra reflow state here if // we drilled down through a block wrapper. At the moment // we can only drill down one level so we only have to support // one extra reflow state. const nsHTMLReflowState* outerReflowState = &aRS; if (frame != aRS.frame) { NS_ASSERTION(frame->GetParent() == aRS.frame, "Can only drill through one level of block wrapper"); nsSize availSpace(aRS.ComputedWidth(), aRS.ComputedHeight()); outerReflowState = new nsHTMLReflowState(prescontext, aRS, frame, availSpace); if (!outerReflowState) goto done; } { nsSize availSpace(outerReflowState->ComputedWidth(), outerReflowState->ComputedHeight()); nsHTMLReflowState innerReflowState(prescontext, *outerReflowState, kid, availSpace); // Record that we're being optimistic by assuming the kid // has no clearance if (kid->GetStyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) { *aMayNeedRetry = PR_TRUE; } if (ComputeCollapsedTopMargin(innerReflowState, aMargin, aClearanceFrame, aMayNeedRetry, &isEmpty)) { line->MarkDirty(); dirtiedLine = PR_TRUE; } if (isEmpty) aMargin->Include(innerReflowState.mComputedMargin.bottom); } if (outerReflowState != &aRS) { delete const_cast<nsHTMLReflowState*>(outerReflowState); } } if (!isEmpty) { if (!setBlockIsEmpty && aBlockIsEmpty) { setBlockIsEmpty = PR_TRUE; *aBlockIsEmpty = PR_FALSE; } goto done; } } if (!setBlockIsEmpty && aBlockIsEmpty) { // The first time we reach here is when this is the first block // and we have processed all its normal lines. setBlockIsEmpty = PR_TRUE; // All lines are empty, or we wouldn't be here! *aBlockIsEmpty = aRS.frame->IsSelfEmpty(); } } } done: ; } if (!setBlockIsEmpty && aBlockIsEmpty) { *aBlockIsEmpty = aRS.frame->IsEmpty(); } #ifdef NOISY_VERTICAL_MARGINS nsFrame::ListTag(stdout, aRS.frame); printf(": => %d\n", aMargin->get()); #endif return dirtiedLine; }
nsresult nsInlineFrame::ReflowFrames(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, InlineReflowState& irs, nsHTMLReflowMetrics& aMetrics, nsReflowStatus& aStatus) { nsresult rv = NS_OK; aStatus = NS_FRAME_COMPLETE; nsLineLayout* lineLayout = aReflowState.mLineLayout; bool inFirstLine = aReflowState.mLineLayout->GetInFirstLine(); RestyleManager* restyleManager = aPresContext->RestyleManager(); bool ltr = (NS_STYLE_DIRECTION_LTR == aReflowState.mStyleVisibility->mDirection); nscoord leftEdge = 0; // Don't offset by our start borderpadding if we have a prev continuation or // if we're in a part of an {ib} split other than the first one. if (!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) { leftEdge = ltr ? aReflowState.ComputedPhysicalBorderPadding().left : aReflowState.ComputedPhysicalBorderPadding().right; } nscoord availableWidth = aReflowState.AvailableWidth(); NS_ASSERTION(availableWidth != NS_UNCONSTRAINEDSIZE, "should no longer use available widths"); // Subtract off left and right border+padding from availableWidth availableWidth -= leftEdge; availableWidth -= ltr ? aReflowState.ComputedPhysicalBorderPadding().right : aReflowState.ComputedPhysicalBorderPadding().left; lineLayout->BeginSpan(this, &aReflowState, leftEdge, leftEdge + availableWidth, &mBaseline); // First reflow our principal children. nsIFrame* frame = mFrames.FirstChild(); bool done = false; while (frame) { // Check if we should lazily set the child frame's parent pointer. if (irs.mSetParentPointer) { bool havePrevBlock = irs.mLineContainer && irs.mLineContainer->GetPrevContinuation(); nsIFrame* child = frame; do { // If our block is the first in flow, then any floats under the pulled // frame must already belong to our block. if (havePrevBlock) { // This has to happen before we update frame's parent; we need to // know frame's ancestry under its old block. // The blockChildren.ContainsFrame check performed by // ReparentFloatsForInlineChild here may be slow, but we can't // easily avoid it because we don't know where 'frame' originally // came from. If we really really have to optimize this we could // cache whether frame->GetParent() is under its containing blocks // overflowList or not. ReparentFloatsForInlineChild(irs.mLineContainer, child, false); } child->SetParent(this); if (inFirstLine) { restyleManager->ReparentStyleContext(child); } // We also need to do the same for |frame|'s next-in-flows that are in // the sibling list. Otherwise, if we reflow |frame| and it's complete // we'll crash when trying to delete its next-in-flow. // This scenario doesn't happen often, but it can happen. nsIFrame* nextSibling = child->GetNextSibling(); child = child->GetNextInFlow(); if (MOZ_UNLIKELY(child)) { while (child != nextSibling && nextSibling) { nextSibling = nextSibling->GetNextSibling(); } if (!nextSibling) { child = nullptr; } } MOZ_ASSERT(!child || mFrames.ContainsFrame(child)); } while (child); // Fix the parent pointer for ::first-letter child frame next-in-flows, // so nsFirstLetterFrame::Reflow can destroy them safely (bug 401042). nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(frame); if (realFrame->GetType() == nsGkAtoms::letterFrame) { nsIFrame* child = realFrame->GetFirstPrincipalChild(); if (child) { NS_ASSERTION(child->GetType() == nsGkAtoms::textFrame, "unexpected frame type"); nsIFrame* nextInFlow = child->GetNextInFlow(); for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) { NS_ASSERTION(nextInFlow->GetType() == nsGkAtoms::textFrame, "unexpected frame type"); if (mFrames.ContainsFrame(nextInFlow)) { nextInFlow->SetParent(this); if (inFirstLine) { restyleManager->ReparentStyleContext(nextInFlow); } } else { #ifdef DEBUG // Once we find a next-in-flow that isn't ours none of the // remaining next-in-flows should be either. for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) { NS_ASSERTION(!mFrames.ContainsFrame(nextInFlow), "unexpected letter frame flow"); } #endif break; } } } } } MOZ_ASSERT(frame->GetParent() == this); if (!done) { bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK(); rv = ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus); done = NS_FAILED(rv) || NS_INLINE_IS_BREAK(aStatus) || (!reflowingFirstLetter && NS_FRAME_IS_NOT_COMPLETE(aStatus)); if (done) { if (!irs.mSetParentPointer) { break; } // Keep reparenting the remaining siblings, but don't reflow them. nsFrameList* pushedFrames = GetOverflowFrames(); if (pushedFrames && pushedFrames->FirstChild() == frame) { // Don't bother if |frame| was pushed to our overflow list. break; } } else { irs.mPrevFrame = frame; } } frame = frame->GetNextSibling(); } // Attempt to pull frames from our next-in-flow until we can't if (!done && GetNextInFlow()) { while (true) { bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK(); bool isComplete; if (!frame) { // Could be non-null if we pulled a first-letter frame and // it created a continuation, since we don't push those. frame = PullOneFrame(aPresContext, irs, &isComplete); } #ifdef NOISY_PUSHING printf("%p pulled up %p\n", this, frame); #endif if (nullptr == frame) { if (!isComplete) { aStatus = NS_FRAME_NOT_COMPLETE; } break; } rv = ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus); if (NS_FAILED(rv) || NS_INLINE_IS_BREAK(aStatus) || (!reflowingFirstLetter && NS_FRAME_IS_NOT_COMPLETE(aStatus))) { break; } irs.mPrevFrame = frame; frame = frame->GetNextSibling(); } } NS_ASSERTION(!NS_FRAME_IS_COMPLETE(aStatus) || !GetOverflowFrames(), "We can't be complete AND have overflow frames!"); // If after reflowing our children they take up no area then make // sure that we don't either. // // Note: CSS demands that empty inline elements still affect the // line-height calculations. However, continuations of an inline // that are empty we force to empty so that things like collapsed // whitespace in an inline element don't affect the line-height. aMetrics.Width() = lineLayout->EndSpan(this); // Compute final width. // Make sure to not include our start border and padding if we have a prev // continuation or if we're in a part of an {ib} split other than the first // one. if (!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) { aMetrics.Width() += ltr ? aReflowState.ComputedPhysicalBorderPadding().left : aReflowState.ComputedPhysicalBorderPadding().right; } /* * We want to only apply the end border and padding if we're the last * continuation and either not in an {ib} split or the last part of it. To * be the last continuation we have to be complete (so that we won't get a * next-in-flow) and have no non-fluid continuations on our continuation * chain. */ if (NS_FRAME_IS_COMPLETE(aStatus) && !LastInFlow()->GetNextContinuation() && !FrameIsNonLastInIBSplit()) { aMetrics.Width() += ltr ? aReflowState.ComputedPhysicalBorderPadding().right : aReflowState.ComputedPhysicalBorderPadding().left; } nsRefPtr<nsFontMetrics> fm; float inflation = nsLayoutUtils::FontSizeInflationFor(this); nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), inflation); aReflowState.rendContext->SetFont(fm); if (fm) { // Compute final height of the frame. // // Do things the standard css2 way -- though it's hard to find it // in the css2 spec! It's actually found in the css1 spec section // 4.4 (you will have to read between the lines to really see // it). // // The height of our box is the sum of our font size plus the top // and bottom border and padding. The height of children do not // affect our height. aMetrics.SetTopAscent(fm->MaxAscent()); aMetrics.Height() = fm->MaxHeight(); } else { NS_WARNING("Cannot get font metrics - defaulting sizes to 0"); aMetrics.SetTopAscent(aMetrics.Height() = 0); } aMetrics.SetTopAscent(aMetrics.TopAscent() + aReflowState.ComputedPhysicalBorderPadding().top); aMetrics.Height() += aReflowState.ComputedPhysicalBorderPadding().top + aReflowState.ComputedPhysicalBorderPadding().bottom; // For now our overflow area is zero. The real value will be // computed in |nsLineLayout::RelativePositionFrames|. aMetrics.mOverflowAreas.Clear(); #ifdef NOISY_FINAL_SIZE ListTag(stdout); printf(": metrics=%d,%d ascent=%d\n", aMetrics.Width(), aMetrics.Height(), aMetrics.TopAscent()); #endif return rv; }
NS_IMETHODIMP nsVideoFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsVideoFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsVideoFrame::Reflow: availSize=%d,%d", aReflowState.availableWidth, aReflowState.availableHeight)); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; aMetrics.width = aReflowState.ComputedWidth(); aMetrics.height = aReflowState.ComputedHeight(); // stash this away so we can compute our inner area later mBorderPadding = aReflowState.mComputedBorderPadding; aMetrics.width += mBorderPadding.left + mBorderPadding.right; aMetrics.height += mBorderPadding.top + mBorderPadding.bottom; // Reflow the child frames. We may have up to two, an image frame // which is the poster, and a box frame, which is the video controls. for (nsIFrame *child = mFrames.FirstChild(); child; child = child->GetNextSibling()) { if (child->GetContent() == mPosterImage) { // Reflow the poster frame. nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child); nsHTMLReflowMetrics kidDesiredSize; nsSize availableSize = nsSize(aReflowState.availableWidth, aReflowState.availableHeight); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, imageFrame, availableSize, aMetrics.width, aMetrics.height); uint32_t posterHeight, posterWidth; nsSize scaledPosterSize(0, 0); nsSize computedArea(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); nsPoint posterTopLeft(0, 0); nsCOMPtr<nsIDOMHTMLImageElement> posterImage = do_QueryInterface(mPosterImage); NS_ENSURE_TRUE(posterImage, NS_ERROR_FAILURE); posterImage->GetNaturalHeight(&posterHeight); posterImage->GetNaturalWidth(&posterWidth); if (ShouldDisplayPoster() && posterHeight && posterWidth) { gfxFloat scale = std::min(static_cast<float>(computedArea.width)/nsPresContext::CSSPixelsToAppUnits(static_cast<float>(posterWidth)), static_cast<float>(computedArea.height)/nsPresContext::CSSPixelsToAppUnits(static_cast<float>(posterHeight))); gfxSize scaledRatio = gfxSize(scale*posterWidth, scale*posterHeight); scaledPosterSize.width = nsPresContext::CSSPixelsToAppUnits(static_cast<float>(scaledRatio.width)); scaledPosterSize.height = nsPresContext::CSSPixelsToAppUnits(static_cast<int32_t>(scaledRatio.height)); } kidReflowState.SetComputedWidth(scaledPosterSize.width); kidReflowState.SetComputedHeight(scaledPosterSize.height); posterTopLeft.x = ((computedArea.width - scaledPosterSize.width) / 2) + mBorderPadding.left; posterTopLeft.y = ((computedArea.height - scaledPosterSize.height) / 2) + mBorderPadding.top; ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowState, posterTopLeft.x, posterTopLeft.y, 0, aStatus); FinishReflowChild(imageFrame, aPresContext, &kidReflowState, kidDesiredSize, posterTopLeft.x, posterTopLeft.y, 0); } else if (child->GetContent() == mVideoControls) { // Reflow the video controls frame. nsBoxLayoutState boxState(PresContext(), aReflowState.rendContext); nsSize size = child->GetSize(); nsBoxFrame::LayoutChildAt(boxState, child, nsRect(mBorderPadding.left, mBorderPadding.top, aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); if (child->GetSize() != size) { nsRefPtr<nsRunnable> event = new DispatchResizeToControls(child->GetContent()); nsContentUtils::AddScriptRunner(event); } } else if (child->GetContent() == mCaptionDiv) { // Reflow to caption div nsHTMLReflowMetrics kidDesiredSize; nsSize availableSize = nsSize(aReflowState.availableWidth, aReflowState.availableHeight); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, child, availableSize, aMetrics.width, aMetrics.height); nsSize size(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); size.width -= kidReflowState.mComputedBorderPadding.LeftRight(); size.height -= kidReflowState.mComputedBorderPadding.TopBottom(); kidReflowState.SetComputedWidth(std::max(size.width, 0)); kidReflowState.SetComputedHeight(std::max(size.height, 0)); ReflowChild(child, aPresContext, kidDesiredSize, kidReflowState, mBorderPadding.left, mBorderPadding.top, 0, aStatus); FinishReflowChild(child, aPresContext, &kidReflowState, kidDesiredSize, mBorderPadding.left, mBorderPadding.top, 0); } } aMetrics.SetOverflowAreasToDesiredBounds(); FinishAndStoreOverflow(&aMetrics); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsVideoFrame::Reflow: size=%d,%d", aMetrics.width, aMetrics.height)); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); return NS_OK; }
NS_IMETHODIMP 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 nsresult rv = NS_OK; if (GetPrevInFlow() && (GetStateBits() & NS_FRAME_FIRST_REFLOW)) { nsresult rv = aPresContext->PresShell()->FrameConstructor() ->ReplicateFixedFrames(this); NS_ENSURE_SUCCESS(rv, rv); } // 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(); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, frame, maxSize); kidReflowState.SetComputedHeight(maxSize.height); mPD->mPageContentSize = maxSize.width; // Reflow the page content area rv = ReflowChild(frame, aPresContext, aDesiredSize, kidReflowState, 0, 0, 0, aStatus); NS_ENSURE_SUCCESS(rv, rv); // 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) { mPD->mPageContentXMost = xmost + kidReflowState.mStyleBorder->GetComputedBorderWidth(NS_SIDE_RIGHT) + padding.right; } } // Place and size the child FinishReflowChild(frame, aPresContext, &kidReflowState, aDesiredSize, 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 aDesiredSize.width = aReflowState.ComputedWidth(); if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE) { aDesiredSize.height = aReflowState.ComputedHeight(); } FinishAndStoreOverflow(&aDesiredSize); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return NS_OK; }
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); ReflowChild(legend, aPresContext, legendDesiredSize, *legendReflowState, wm, LogicalPoint(wm), 0, 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; } // We'll move the legend to its proper place later. FinishReflowChild(legend, aPresContext, legendDesiredSize, legendReflowState.ptr(), wm, LogicalPoint(wm), 0, 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); } nscoord containerWidth = (wm.IsVertical() ? mLegendSpace : 0) + border.LeftRight(wm); // reflow the content frame only if needed if (reflowInner) { nsHTMLReflowState kidReflowState(aPresContext, aReflowState, inner, innerAvailSize, -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); // Override computed padding, in case it's percentage padding kidReflowState.Init(aPresContext, -1, -1, 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); ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowState, wm, pt, containerWidth, 0, aStatus); // update the container width after reflowing the inner frame FinishReflowChild(inner, aPresContext, kidDesiredSize, &kidReflowState, wm, pt, containerWidth + kidDesiredSize.Width(), 0); NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus); } if (inner) { containerWidth += inner->GetSize().width; } 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 care about container-width at this point, // as we'll figure out the actual positioning later. contentRect = inner->GetLogicalRect(wm, containerWidth); } // 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, containerWidth); legend->SetPosition(wm, actualLegendPos, containerWidth); 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); }
/* virtual */ void nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsRubyBaseContainerFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); aStatus = NS_FRAME_COMPLETE; if (!aReflowState.mLineLayout) { NS_ASSERTION( aReflowState.mLineLayout, "No line layout provided to RubyBaseContainerFrame reflow method."); return; } MoveOverflowToChildList(); // Ask text containers to drain overflows AutoRubyTextContainerArray textContainers(this); const uint32_t rtcCount = textContainers.Length(); for (uint32_t i = 0; i < rtcCount; i++) { textContainers[i]->MoveOverflowToChildList(); } WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode(); LogicalSize availSize(lineWM, aReflowState.AvailableISize(), aReflowState.AvailableBSize()); // We have a reflow state and a line layout for each RTC. // They are conceptually the state of the RTCs, but we don't actually // reflow those RTCs in this code. These two arrays are holders of // the reflow states and line layouts. // Since there are pointers refer to reflow states and line layouts, // it is necessary to guarantee that they won't be moved. For this // reason, they are wrapped in UniquePtr here. nsAutoTArray<UniquePtr<nsHTMLReflowState>, RTC_ARRAY_SIZE> reflowStates; nsAutoTArray<UniquePtr<nsLineLayout>, RTC_ARRAY_SIZE> lineLayouts; reflowStates.SetCapacity(rtcCount); lineLayouts.SetCapacity(rtcCount); // Begin the line layout for each ruby text container in advance. bool hasSpan = false; for (uint32_t i = 0; i < rtcCount; i++) { nsRubyTextContainerFrame* textContainer = textContainers[i]; if (textContainer->IsSpanContainer()) { hasSpan = true; } nsHTMLReflowState* reflowState = new nsHTMLReflowState( aPresContext, *aReflowState.parentReflowState, textContainer, availSize.ConvertTo(textContainer->GetWritingMode(), lineWM)); reflowStates.AppendElement(reflowState); nsLineLayout* lineLayout = new nsLineLayout(aPresContext, reflowState->mFloatManager, reflowState, nullptr, aReflowState.mLineLayout); lineLayout->SetSuppressLineWrap(true); lineLayouts.AppendElement(lineLayout); // Line number is useless for ruby text // XXX nullptr here may cause problem, see comments for // nsLineLayout::mBlockRS and nsLineLayout::AddFloat lineLayout->Init(nullptr, reflowState->CalcLineHeight(), -1); reflowState->mLineLayout = lineLayout; // Border and padding are suppressed on ruby text containers. // If the writing mode is vertical-rl, the horizontal position of // rt frames will be updated when reflowing this text container, // hence leave container size 0 here for now. lineLayout->BeginLineReflow(0, 0, reflowState->ComputedISize(), NS_UNCONSTRAINEDSIZE, false, false, lineWM, nsSize(0, 0)); lineLayout->AttachRootFrameToBaseLineLayout(); } aReflowState.mLineLayout->BeginSpan(this, &aReflowState, 0, aReflowState.AvailableISize(), &mBaseline); bool allowInitialLineBreak, allowLineBreak; GetIsLineBreakAllowed(this, aReflowState.mLineLayout->LineIsBreakable(), &allowInitialLineBreak, &allowLineBreak); nscoord isize = 0; // Reflow columns excluding any span ReflowState reflowState = { allowInitialLineBreak, allowLineBreak && !hasSpan, textContainers, aReflowState, reflowStates }; isize = ReflowColumns(reflowState, aStatus); DebugOnly<nscoord> lineSpanSize = aReflowState.mLineLayout->EndSpan(this); aDesiredSize.ISize(lineWM) = isize; // When there are no frames inside the ruby base container, EndSpan // will return 0. However, in this case, the actual width of the // container could be non-zero because of non-empty ruby annotations. MOZ_ASSERT(NS_INLINE_IS_BREAK(aStatus) || isize == lineSpanSize || mFrames.IsEmpty()); // If there exists any span, the columns must either be completely // reflowed, or be not reflowed at all. MOZ_ASSERT(NS_INLINE_IS_BREAK_BEFORE(aStatus) || NS_FRAME_IS_COMPLETE(aStatus) || !hasSpan); if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) && NS_FRAME_IS_COMPLETE(aStatus) && hasSpan) { // Reflow spans ReflowState reflowState = { false, false, textContainers, aReflowState, reflowStates }; nscoord spanISize = ReflowSpans(reflowState); isize = std::max(isize, spanISize); if (isize > aReflowState.AvailableISize() && aReflowState.mLineLayout->HasOptionalBreakPosition()) { aStatus = NS_INLINE_LINE_BREAK_BEFORE(); } } for (uint32_t i = 0; i < rtcCount; i++) { // It happens before the ruby text container is reflowed, and that // when it is reflowed, it will just use this size. nsRubyTextContainerFrame* textContainer = textContainers[i]; nsLineLayout* lineLayout = lineLayouts[i].get(); RubyUtils::ClearReservedISize(textContainer); nscoord rtcISize = lineLayout->GetCurrentICoord(); // Only span containers and containers with collapsed annotations // need reserving isize. For normal ruby text containers, their // children will be expanded properly. We only need to expand their // own size. if (!textContainer->IsSpanContainer()) { rtcISize = isize; } else if (isize > rtcISize) { RubyUtils::SetReservedISize(textContainer, isize - rtcISize); } lineLayout->VerticalAlignLine(); textContainer->SetISize(rtcISize); lineLayout->EndLineReflow(); } // Border and padding are suppressed on ruby base container, // create a fake borderPadding for setting BSize. WritingMode frameWM = aReflowState.GetWritingMode(); LogicalMargin borderPadding(frameWM); nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, borderPadding, lineWM, frameWM); }
void nsFirstLetterFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aReflowStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus); // Grab overflow list DrainOverflowFrames(aPresContext); nsIFrame* kid = mFrames.FirstChild(); // Setup reflow state for our child WritingMode wm = aReflowState.GetWritingMode(); LogicalSize availSize = aReflowState.AvailableSize(); const LogicalMargin& bp = aReflowState.ComputedLogicalBorderPadding(); NS_ASSERTION(availSize.ISize(wm) != NS_UNCONSTRAINEDSIZE, "should no longer use unconstrained inline size"); availSize.ISize(wm) -= bp.IStartEnd(wm); if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) { availSize.BSize(wm) -= bp.BStartEnd(wm); } WritingMode lineWM = aMetrics.GetWritingMode(); nsHTMLReflowMetrics kidMetrics(lineWM); // Reflow the child if (!aReflowState.mLineLayout) { // When there is no lineLayout provided, we provide our own. The // only time that the first-letter-frame is not reflowing in a // line context is when its floating. WritingMode kidWritingMode = GetWritingMode(kid); LogicalSize kidAvailSize = availSize.ConvertTo(kidWritingMode, wm); nsHTMLReflowState rs(aPresContext, aReflowState, kid, kidAvailSize); nsLineLayout ll(aPresContext, nullptr, &aReflowState, nullptr, nullptr); ll.BeginLineReflow(bp.IStart(wm), bp.BStart(wm), availSize.ISize(wm), NS_UNCONSTRAINEDSIZE, false, true, kidWritingMode, nsSize(aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); rs.mLineLayout = ≪ ll.SetInFirstLetter(true); ll.SetFirstLetterStyleOK(true); kid->Reflow(aPresContext, kidMetrics, rs, aReflowStatus); ll.EndLineReflow(); ll.SetInFirstLetter(false); // In the floating first-letter case, we need to set this ourselves; // nsLineLayout::BeginSpan will set it in the other case mBaseline = kidMetrics.BlockStartAscent(); // Place and size the child and update the output metrics LogicalSize convertedSize = kidMetrics.Size(lineWM).ConvertTo(wm, lineWM); kid->SetRect(nsRect(bp.IStart(wm), bp.BStart(wm), convertedSize.ISize(wm), convertedSize.BSize(wm))); kid->FinishAndStoreOverflow(&kidMetrics); kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED); convertedSize.ISize(wm) += bp.IStartEnd(wm); convertedSize.BSize(wm) += bp.BStartEnd(wm); aMetrics.SetSize(wm, convertedSize); aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() + bp.BStart(wm)); // Ensure that the overflow rect contains the child textframe's // overflow rect. // Note that if this is floating, the overline/underline drawable // area is in the overflow rect of the child textframe. aMetrics.UnionOverflowAreasWithDesiredBounds(); ConsiderChildOverflow(aMetrics.mOverflowAreas, kid); FinishAndStoreOverflow(&aMetrics); } else { // Pretend we are a span and reflow the child frame nsLineLayout* ll = aReflowState.mLineLayout; bool pushedFrame; ll->SetInFirstLetter( mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter); ll->BeginSpan(this, &aReflowState, bp.IStart(wm), availSize.ISize(wm), &mBaseline); ll->ReflowFrame(kid, aReflowStatus, &kidMetrics, pushedFrame); NS_ASSERTION(lineWM.IsVertical() == wm.IsVertical(), "we're assuming we can mix sizes between lineWM and wm " "since we shouldn't have orthogonal writing modes within " "a line."); aMetrics.ISize(lineWM) = ll->EndSpan(this) + bp.IStartEnd(wm); ll->SetInFirstLetter(false); nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, bp, lineWM, wm); } if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) { // Create a continuation or remove existing continuations based on // the reflow completion status. if (NS_FRAME_IS_COMPLETE(aReflowStatus)) { if (aReflowState.mLineLayout) { aReflowState.mLineLayout->SetFirstLetterStyleOK(false); } nsIFrame* kidNextInFlow = kid->GetNextInFlow(); if (kidNextInFlow) { // Remove all of the childs next-in-flows kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true); } } else { // Create a continuation for the child frame if it doesn't already // have one. if (!IsFloating()) { CreateNextInFlow(kid); // And then push it to our overflow list const nsFrameList& overflow = mFrames.RemoveFramesAfter(kid); if (overflow.NotEmpty()) { SetOverflowFrames(overflow); } } else if (!kid->GetNextInFlow()) { // For floating first letter frames (if a continuation wasn't already // created for us) we need to put the continuation with the rest of the // text that the first letter frame was made out of. nsIFrame* continuation; CreateContinuationForFloatingParent(aPresContext, kid, &continuation, true); } } } NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowState, aMetrics); }
NS_IMETHODIMP nsVideoFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsVideoFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsVideoFrame::Reflow: availSize=%d,%d", aReflowState.availableWidth, aReflowState.availableHeight)); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; aMetrics.width = aReflowState.ComputedWidth(); aMetrics.height = aReflowState.ComputedHeight(); // stash this away so we can compute our inner area later mBorderPadding = aReflowState.mComputedBorderPadding; aMetrics.width += mBorderPadding.left + mBorderPadding.right; aMetrics.height += mBorderPadding.top + mBorderPadding.bottom; // Reflow the child frames. We may have up to two, an image frame // which is the poster, and a box frame, which is the video controls. for (nsIFrame *child = mFrames.FirstChild(); child; child = child->GetNextSibling()) { if (child->GetType() == nsGkAtoms::imageFrame) { // Reflow the poster frame. nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child); nsHTMLReflowMetrics kidDesiredSize; nsSize availableSize = nsSize(aReflowState.availableWidth, aReflowState.availableHeight); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, imageFrame, availableSize, aMetrics.width, aMetrics.height); if (ShouldDisplayPoster()) { kidReflowState.SetComputedWidth(aReflowState.ComputedWidth()); kidReflowState.SetComputedHeight(aReflowState.ComputedHeight()); } else { kidReflowState.SetComputedWidth(0); kidReflowState.SetComputedHeight(0); } ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowState, mBorderPadding.left, mBorderPadding.top, 0, aStatus); FinishReflowChild(imageFrame, aPresContext, &kidReflowState, kidDesiredSize, mBorderPadding.left, mBorderPadding.top, 0); } else if (child->GetType() == nsGkAtoms::boxFrame) { // Reflow the video controls frame. nsBoxLayoutState boxState(PresContext(), aReflowState.rendContext); nsBoxFrame::LayoutChildAt(boxState, child, nsRect(mBorderPadding.left, mBorderPadding.top, aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); } } aMetrics.mOverflowArea.SetRect(0, 0, aMetrics.width, aMetrics.height); FinishAndStoreOverflow(&aMetrics); if (mRect.width != aMetrics.width || mRect.height != aMetrics.height) { Invalidate(nsRect(0, 0, mRect.width, mRect.height)); } NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsVideoFrame::Reflow: size=%d,%d", aMetrics.width, aMetrics.height)); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); return NS_OK; }
nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsPresContext* aPresContext, nsBlockFrame* aFrame, bool aTopMarginRoot, bool aBottomMarginRoot, bool aBlockNeedsFloatManager, nscoord aConsumedHeight) : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), mPushedFloats(nullptr), mOverflowTracker(nullptr), mPrevBottomMargin(), mLineNumber(0), mFlags(0), mFloatBreakType(NS_STYLE_CLEAR_NONE), mConsumedHeight(aConsumedHeight) { SetFlag(BRS_ISFIRSTINFLOW, aFrame->GetPrevInFlow() == nullptr); SetFlag(BRS_ISOVERFLOWCONTAINER, IS_TRUE_OVERFLOW_CONTAINER(aFrame)); const nsMargin& borderPadding = BorderPadding(); if (aTopMarginRoot || 0 != aReflowState.ComputedPhysicalBorderPadding().top) { SetFlag(BRS_ISTOPMARGINROOT, true); } if (aBottomMarginRoot || 0 != aReflowState.ComputedPhysicalBorderPadding().bottom) { SetFlag(BRS_ISBOTTOMMARGINROOT, true); } if (GetFlag(BRS_ISTOPMARGINROOT)) { SetFlag(BRS_APPLYTOPMARGIN, 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.ComputedWidth(), "have unconstrained width; this should only result from " "very large sizes, not attempts at intrinsic width " "calculation"); mContentArea.width = aReflowState.ComputedWidth(); // 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.AvailableHeight()) { // 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. mBottomEdge = aReflowState.AvailableHeight() - borderPadding.bottom; mContentArea.height = std::max(0, mBottomEdge - borderPadding.top); } else { // When we are not in a paginated situation then we always use // an constrained height. SetFlag(BRS_UNCONSTRAINEDHEIGHT, true); mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE; } mContentArea.x = borderPadding.left; mY = mContentArea.y = borderPadding.top; mPrevChild = nullptr; mCurrentLine = aFrame->end_lines(); mMinLineHeight = aReflowState.CalcLineHeight(); }
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(); }
NS_IMETHODIMP nsComboboxControlFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { // Constraints we try to satisfy: // 1) Default width of button is the vertical scrollbar size // 2) If the width of button is bigger than our width, set width of // button to 0. // 3) Default height of button is height of display area // 4) Width of display area is whatever is left over from our width after // allocating width for the button. // 5) Height of display area is GetHeightOfARow() on the // mListControlFrame. if (!mDisplayFrame || !mButtonFrame || !mDropdownFrame) { NS_ERROR("Why did the frame constructor allow this to happen? Fix it!!"); return NS_ERROR_UNEXPECTED; } // Make sure the displayed text is the same as the selected option, bug 297389. PRInt32 selectedIndex; nsAutoString selectedOptionText; if (!mDroppedDown) { selectedIndex = mListControlFrame->GetSelectedIndex(); } else { // In dropped down mode the "selected index" is the hovered menu item, // we want the last selected item which is |mDisplayedIndex| in this case. selectedIndex = mDisplayedIndex; } if (selectedIndex != -1) { mListControlFrame->GetOptionText(selectedIndex, selectedOptionText); } if (mDisplayedOptionText != selectedOptionText) { RedisplayText(selectedIndex); } // First reflow our dropdown so that we know how tall we should be. ReflowDropdown(aPresContext, aReflowState); // Get the width of the vertical scrollbar. That will be the width of the // dropdown button. nscoord buttonWidth; const nsStyleDisplay *disp = GetStyleDisplay(); if (IsThemed(disp) && !aPresContext->GetTheme()->ThemeNeedsComboboxDropmarker()) { buttonWidth = 0; } else { nsIScrollableFrame* scrollable = do_QueryFrame(mListControlFrame); NS_ASSERTION(scrollable, "List must be a scrollable frame"); buttonWidth = scrollable->GetDesiredScrollbarSizes(PresContext(), aReflowState.rendContext).LeftRight(); if (buttonWidth > aReflowState.ComputedWidth()) { buttonWidth = 0; } } mDisplayWidth = aReflowState.ComputedWidth() - buttonWidth; nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); NS_ENSURE_SUCCESS(rv, rv); // Now set the correct width and height on our button. The width we need to // set always, the height only if we had an auto height. nsRect buttonRect = mButtonFrame->GetRect(); // If we have a non-intrinsic computed height, our kids should have sized // themselves properly on their own. if (aReflowState.ComputedHeight() == NS_INTRINSICSIZE) { // The display frame is going to be the right height and width at this // point. Use its height as the button height. nsRect displayRect = mDisplayFrame->GetRect(); buttonRect.height = displayRect.height; buttonRect.y = displayRect.y; } #ifdef DEBUG else { nscoord buttonHeight = mButtonFrame->GetSize().height; nscoord displayHeight = mDisplayFrame->GetSize().height; // The button and display area should be equal heights, unless the computed // height on the combobox is too small to fit their borders and padding. NS_ASSERTION(buttonHeight == displayHeight || (aReflowState.ComputedHeight() < buttonHeight && buttonHeight == mButtonFrame->GetUsedBorderAndPadding().TopBottom()) || (aReflowState.ComputedHeight() < displayHeight && displayHeight == mDisplayFrame->GetUsedBorderAndPadding().TopBottom()), "Different heights?"); } #endif if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { // Make sure the right edge of the button frame stays where it is now buttonRect.x -= buttonWidth - buttonRect.width; } buttonRect.width = buttonWidth; mButtonFrame->SetRect(buttonRect); return rv; }
nsColumnSetFrame::ReflowConfig nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState) { const nsStyleColumn* colStyle = GetStyleColumn(); nscoord availContentWidth = GetAvailableContentWidth(aReflowState); if (aReflowState.ComputedWidth() != NS_INTRINSICSIZE) { availContentWidth = aReflowState.ComputedWidth(); } nscoord colHeight = GetAvailableContentHeight(aReflowState); if (aReflowState.ComputedHeight() != NS_INTRINSICSIZE) { colHeight = aReflowState.ComputedHeight(); } nscoord colGap = GetColumnGap(this, colStyle); PRInt32 numColumns = colStyle->mColumnCount; const PRUint32 MAX_NESTED_COLUMN_BALANCING = 2; PRUint32 cnt = 1; 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 colWidth; if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) { colWidth = colStyle->mColumnWidth.GetCoordValue(); NS_ASSERTION(colWidth >= 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 // availContentWidth, satisfying colGap*(maxColumns - 1) + // colWidth*maxColumns <= availContentWidth if (availContentWidth != NS_INTRINSICSIZE && colGap + colWidth > 0 && numColumns > 0) { // This expression uses truncated rounding, which is what we // want PRInt32 maxColumns = (availContentWidth + colGap)/(colGap + colWidth); numColumns = NS_MAX(1, NS_MIN(numColumns, maxColumns)); } } else if (numColumns > 0 && availContentWidth != NS_INTRINSICSIZE) { nscoord widthMinusGaps = availContentWidth - colGap*(numColumns - 1); colWidth = widthMinusGaps/numColumns; } else { colWidth = NS_INTRINSICSIZE; } // Take care of the situation where there's only one column but it's // still too wide colWidth = NS_MAX(1, NS_MIN(colWidth, availContentWidth)); nscoord expectedWidthLeftOver = 0; if (colWidth != NS_INTRINSICSIZE && availContentWidth != 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) + // colWidth*nominalColumnCount is nearly availContentWidth // make sure to round down if (colGap + colWidth > 0) { numColumns = (availContentWidth + colGap)/(colGap + colWidth); } if (numColumns <= 0) { numColumns = 1; } } // Compute extra space and divide it among the columns nscoord extraSpace = NS_MAX(0, availContentWidth - (colWidth*numColumns + colGap*(numColumns - 1))); nscoord extraToColumns = extraSpace/numColumns; colWidth += extraToColumns; expectedWidthLeftOver = extraSpace - (extraToColumns*numColumns); } // NOTE that the non-balancing behavior for non-auto computed height // is not in the CSS3 columns draft as of 18 January 2001 if (aReflowState.ComputedHeight() == NS_INTRINSICSIZE) { // Balancing! 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; } colHeight = NS_MIN(mLastBalanceHeight, GetAvailableContentHeight(aReflowState)); } else { // No balancing, so don't limit the column count numColumns = PR_INT32_MAX; } #ifdef DEBUG_roc printf("*** nsColumnSetFrame::ChooseColumnStrategy: numColumns=%d, colWidth=%d, expectedWidthLeftOver=%d, colHeight=%d, colGap=%d\n", numColumns, colWidth, expectedWidthLeftOver, colHeight, colGap); #endif ReflowConfig config = { numColumns, colWidth, expectedWidthLeftOver, colGap, colHeight }; return config; }
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); }
NS_IMETHODIMP nsColumnSetFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { // 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 height if we have a fixed height. if (aReflowState.ComputedHeight() != NS_AUTOHEIGHT) { NS_ASSERTION(aReflowState.ComputedHeight() != NS_INTRINSICSIZE, "Unexpected mComputedHeight"); AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); } else { RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); } //------------ Handle Incremental Reflow ----------------- ReflowConfig config = ChooseColumnStrategy(aReflowState); bool isBalancing = config.mBalanceColCount < PR_INT32_MAX; // 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 = isBalancing && !nextInFlow; nsCollapsingMargin carriedOutBottomMargin; ColumnBalanceData colData; bool feasible = ReflowChildren(aDesiredSize, aReflowState, aStatus, config, unboundedLastColumn, &carriedOutBottomMargin, colData); if (isBalancing && !aPresContext->HasPendingInterrupt()) { nscoord availableContentHeight = GetAvailableContentHeight(aReflowState); // Termination of the algorithm below is guaranteed because // knownFeasibleHeight - knownInfeasibleHeight decreases in every // iteration. nscoord knownFeasibleHeight = NS_INTRINSICSIZE; nscoord knownInfeasibleHeight = 0; // We set this flag when we detect that we may contain a frame // that can break anywhere (thus foiling the linear decrease-by-one // search) bool maybeContinuousBreakingDetected = false; while (!aPresContext->HasPendingInterrupt()) { nscoord lastKnownFeasibleHeight = knownFeasibleHeight; // Record what we learned from the last reflow if (feasible) { // maxHeight is feasible. Also, mLastBalanceHeight is feasible. knownFeasibleHeight = NS_MIN(knownFeasibleHeight, colData.mMaxHeight); knownFeasibleHeight = NS_MIN(knownFeasibleHeight, mLastBalanceHeight); // Furthermore, no height less than the height of the last // column can ever be feasible. (We might be able to reduce the // height of a non-last column by moving content to a later column, // but we can't do that with the last column.) if (mFrames.GetLength() == config.mBalanceColCount) { knownInfeasibleHeight = NS_MAX(knownInfeasibleHeight, colData.mLastHeight - 1); } } else { knownInfeasibleHeight = NS_MAX(knownInfeasibleHeight, mLastBalanceHeight); // If a column didn't fit in its available height, then its current // height must be the minimum height for unbreakable content in // the column, and therefore no smaller height can be feasible. knownInfeasibleHeight = NS_MAX(knownInfeasibleHeight, colData.mMaxOverflowingHeight - 1); if (unboundedLastColumn) { // The last column is unbounded, so all content got reflowed, so the // mColMaxHeight is feasible. knownFeasibleHeight = NS_MIN(knownFeasibleHeight, colData.mMaxHeight); } } #ifdef DEBUG_roc printf("*** nsColumnSetFrame::Reflow balancing knownInfeasible=%d knownFeasible=%d\n", knownInfeasibleHeight, knownFeasibleHeight); #endif if (knownInfeasibleHeight >= knownFeasibleHeight - 1) { // knownFeasibleHeight is where we want to be break; } if (knownInfeasibleHeight >= availableContentHeight) { break; } if (lastKnownFeasibleHeight - knownFeasibleHeight == 1) { // We decreased the feasible height by one twip only. This could // indicate that there is a continuously breakable child frame // that we are crawling through. maybeContinuousBreakingDetected = true; } nscoord nextGuess = (knownFeasibleHeight + knownInfeasibleHeight)/2; // The constant of 600 twips is arbitrary. It's about two line-heights. if (knownFeasibleHeight - nextGuess < 600 && !maybeContinuousBreakingDetected) { // We're close to our target, so just try shrinking just the // minimum amount that will cause one of our columns to break // differently. nextGuess = knownFeasibleHeight - 1; } else if (unboundedLastColumn) { // Make a guess by dividing that into N columns. Add some slop // to try to make it on the feasible side. The constant of // 600 twips is arbitrary. It's about two line-heights. nextGuess = colData.mSumHeight/config.mBalanceColCount + 600; // Sanitize it nextGuess = clamped(nextGuess, knownInfeasibleHeight + 1, knownFeasibleHeight - 1); } else if (knownFeasibleHeight == NS_INTRINSICSIZE) { // This can happen when we had a next-in-flow so we didn't // want to do an unbounded height measuring step. Let's just increase // from the infeasible height by some reasonable amount. nextGuess = knownInfeasibleHeight*2 + 600; } // Don't bother guessing more than our height constraint. nextGuess = NS_MIN(availableContentHeight, nextGuess); #ifdef DEBUG_roc printf("*** nsColumnSetFrame::Reflow balancing choosing next guess=%d\n", nextGuess); #endif config.mColMaxHeight = nextGuess; unboundedLastColumn = false; AddStateBits(NS_FRAME_IS_DIRTY); feasible = ReflowChildren(aDesiredSize, aReflowState, aStatus, config, false, &carriedOutBottomMargin, colData); } if (!feasible && !aPresContext->HasPendingInterrupt()) { // We may need to reflow one more time at the feasible height to // get a valid layout. bool skip = false; if (knownInfeasibleHeight >= availableContentHeight) { config.mColMaxHeight = availableContentHeight; if (mLastBalanceHeight == availableContentHeight) { skip = true; } } else { config.mColMaxHeight = knownFeasibleHeight; } if (!skip) { // If our height is unconstrained, make sure that the last column is // allowed to have arbitrary height here, even though we were balancing. // Otherwise we'd have to split, and it's not clear what we'd do with // that. AddStateBits(NS_FRAME_IS_DIRTY); ReflowChildren(aDesiredSize, aReflowState, aStatus, config, availableContentHeight == NS_UNCONSTRAINEDSIZE, &carriedOutBottomMargin, colData); } } } if (aPresContext->HasPendingInterrupt() && aReflowState.availableHeight == 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; } CheckInvalidateSizeChange(aDesiredSize); // XXXjwir3: This call should be replaced with FinishWithAbsoluteFrames // when bug 724978 is fixed and nsColumnSetFrame is a full absolute // container. FinishAndStoreOverflow(&aDesiredSize); aDesiredSize.mCarriedOutBottomMargin = carriedOutBottomMargin; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); NS_ASSERTION(NS_FRAME_IS_FULLY_COMPLETE(aStatus) || aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE, "Column set should be complete if the available height is unconstrained"); return NS_OK; }
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; }
void nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext, nsHTMLReflowMetrics& aButtonDesiredSize, const nsHTMLReflowState& aButtonReflowState, nsIFrame* aFirstKid) { // Buttons have some bonus renderer-determined border/padding, // which occupies part of the button's content-box area: const nsMargin focusPadding = mRenderer.GetAddedButtonBorderAndPadding(); nsSize availSize(aButtonReflowState.ComputedWidth(), NS_INTRINSICSIZE); // Indent the child inside us by the focus border. We must do this separate // from the regular border. availSize.width -= focusPadding.LeftRight(); // See whether out availSize's width is big enough. If it's smaller than our // intrinsic min width, that means that the kid wouldn't really fit; for a // better look in such cases we adjust the available width and our left // offset to allow the kid to spill left into our padding. nscoord xoffset = focusPadding.left + aButtonReflowState.ComputedPhysicalBorderPadding().left; nscoord extrawidth = GetMinWidth(aButtonReflowState.rendContext) - aButtonReflowState.ComputedWidth(); if (extrawidth > 0) { nscoord extraleft = extrawidth / 2; nscoord extraright = extrawidth - extraleft; NS_ASSERTION(extraright >=0, "How'd that happen?"); // Do not allow the extras to be bigger than the relevant padding extraleft = std::min(extraleft, aButtonReflowState.ComputedPhysicalPadding().left); extraright = std::min(extraright, aButtonReflowState.ComputedPhysicalPadding().right); xoffset -= extraleft; availSize.width += extraleft + extraright; } availSize.width = std::max(availSize.width,0); // Give child a clone of the button's reflow state, with height/width reduced // by focusPadding, so that descendants with height:100% don't protrude. nsHTMLReflowState adjustedButtonReflowState = CloneReflowStateWithReducedContentBox(aButtonReflowState, focusPadding); nsHTMLReflowState contentsReflowState(aPresContext, adjustedButtonReflowState, aFirstKid, availSize); nsReflowStatus contentsReflowStatus; nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState); ReflowChild(aFirstKid, aPresContext, contentsDesiredSize, contentsReflowState, xoffset, focusPadding.top + aButtonReflowState.ComputedPhysicalBorderPadding().top, 0, contentsReflowStatus); MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus), "We gave button-contents frame unconstrained available height, " "so it should be complete"); // Compute the button's content-box height: nscoord buttonContentBoxHeight = 0; if (aButtonReflowState.ComputedHeight() != NS_INTRINSICSIZE) { // Button has a fixed height -- that's its content-box height. buttonContentBoxHeight = aButtonReflowState.ComputedHeight(); } else { // Button is intrinsically sized -- it should shrinkwrap the // button-contents' height, plus any focus-padding space: buttonContentBoxHeight = contentsDesiredSize.Height() + focusPadding.TopBottom(); // Make sure we obey min/max-height in the case when we're doing intrinsic // sizing (we get it for free when we have a non-intrinsic // aButtonReflowState.ComputedHeight()). Note that we do this before // adjusting for borderpadding, since mComputedMaxHeight and // mComputedMinHeight are content heights. buttonContentBoxHeight = NS_CSS_MINMAX(buttonContentBoxHeight, aButtonReflowState.ComputedMinHeight(), aButtonReflowState.ComputedMaxHeight()); } // Center child vertically in the button // (technically, inside of the button's focus-padding area) nscoord extraSpace = buttonContentBoxHeight - focusPadding.TopBottom() - contentsDesiredSize.Height(); nscoord yoffset = std::max(0, extraSpace / 2); // Adjust yoffset to be in terms of the button's frame-rect, instead of // its focus-padding rect: yoffset += focusPadding.top + aButtonReflowState.ComputedPhysicalBorderPadding().top; // Place the child FinishReflowChild(aFirstKid, aPresContext, contentsDesiredSize, &contentsReflowState, xoffset, yoffset, 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.Width() = aButtonReflowState.ComputedWidth() + aButtonReflowState.ComputedPhysicalBorderPadding().LeftRight(); aButtonDesiredSize.Height() = buttonContentBoxHeight + aButtonReflowState.ComputedPhysicalBorderPadding().TopBottom(); // * Button's ascent is its child's ascent, plus the child's y-offset // within our frame: aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.BlockStartAscent() + yoffset); aButtonDesiredSize.SetOverflowAreasToDesiredBounds(); }
bool nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus, const ReflowConfig& aConfig, bool aUnboundedLastColumn, nsCollapsingMargin* aCarriedOutBEndMargin, ColumnBalanceData& aColData) { aColData.Reset(); bool allFit = true; WritingMode wm = GetWritingMode(); bool isVertical = wm.IsVertical(); bool isRTL = !wm.IsBidiLTR(); bool shrinkingBSizeOnly = !NS_SUBTREE_DIRTY(this) && mLastBalanceBSize > aConfig.mColMaxBSize; #ifdef DEBUG_roc printf("*** Doing column reflow pass: mLastBalanceBSize=%d, mColMaxBSize=%d, RTL=%d\n" " mBalanceColCount=%d, mColISize=%d, mColGap=%d\n", mLastBalanceBSize, aConfig.mColMaxBSize, isRTL, aConfig.mBalanceColCount, aConfig.mColISize, aConfig.mColGap); #endif DrainOverflowColumns(); const bool colBSizeChanged = mLastBalanceBSize != aConfig.mColMaxBSize; if (colBSizeChanged) { mLastBalanceBSize = aConfig.mColMaxBSize; // 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 LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding(); borderPadding.ApplySkipSides(GetLogicalSkipSides(&aReflowState)); nsRect contentRect(0, 0, 0, 0); nsOverflowAreas overflowRects; nsIFrame* child = mFrames.FirstChild(); LogicalPoint childOrigin(wm, borderPadding.IStart(wm), borderPadding.BStart(wm)); // In vertical-rl mode, columns will not be correctly placed if the // reflowState's ComputedWidth() is UNCONSTRAINED (in which case we'll get // a containerSize.width of zero here). In that case, the column positions // will be adjusted later, after our correct contentSize is known. nsSize containerSize = aReflowState.ComputedSizeAsContainerIfConstrained(); // For RTL, 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 // XXX when all of layout is converted to logical coordinates, we // probably won't need to do this hack any more. For now, we // confine it to the legacy horizontal-rl case if (!isVertical && isRTL) { nscoord availISize = aReflowState.AvailableISize(); if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) { availISize = aReflowState.ComputedISize(); } if (availISize != NS_INTRINSICSIZE) { childOrigin.I(wm) = containerSize.width - borderPadding.Left(wm) - availISize; #ifdef DEBUG_roc printf("*** childOrigin.iCoord = %d\n", childOrigin.I(wm)); #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 skipResizeBSizeShrink = false; if (shrinkingBSizeOnly) { switch (wm.GetBlockDir()) { case WritingMode::eBlockTB: if (child->GetScrollableOverflowRect().YMost() <= aConfig.mColMaxBSize) { skipResizeBSizeShrink = true; } break; case WritingMode::eBlockLR: if (child->GetScrollableOverflowRect().XMost() <= aConfig.mColMaxBSize) { skipResizeBSizeShrink = true; } break; case WritingMode::eBlockRL: // XXX not sure how to handle this, so for now just don't attempt // the optimization break; default: NS_NOTREACHED("unknown block direction"); break; } } nscoord childContentBEnd = 0; if (!reflowNext && (skipIncremental || skipResizeBSizeShrink)) { // This child does not need to be reflowed, but we may need to move it MoveChildTo(child, childOrigin, wm, containerSize); // 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 block-size shrink %d): status = %d\n", columnCount, (void*)child, skipIncremental, skipResizeBSizeShrink, aStatus); #endif } else { LogicalSize availSize(wm, aConfig.mColISize, aConfig.mColMaxBSize); if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) { availSize.BSize(wm) = GetAvailableContentBSize(aReflowState); } LogicalSize computedSize = aReflowState.ComputedSize(wm); if (reflowNext) child->AddStateBits(NS_FRAME_IS_DIRTY); LogicalSize kidCBSize(wm, availSize.ISize(wm), computedSize.BSize(wm)); nsHTMLReflowState kidReflowState(PresContext(), aReflowState, child, availSize, &kidCBSize); 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 = !colBSizeChanged; #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 LogicalPoint origin(wm, childOrigin.I(wm) + kidReflowState.ComputedLogicalMargin().IStart(wm), childOrigin.B(wm) + kidReflowState.ComputedLogicalMargin().BStart(wm)); ReflowChild(child, PresContext(), kidDesiredSize, kidReflowState, wm, origin, containerSize, 0, aStatus); reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0; #ifdef DEBUG_roc printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d CarriedOutBEndMargin=%d\n", columnCount, (void*)child, aStatus, kidDesiredSize.Width(), kidDesiredSize.Height(), kidDesiredSize.mCarriedOutBEndMargin.get()); #endif NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus); *aCarriedOutBEndMargin = kidDesiredSize.mCarriedOutBEndMargin; FinishReflowChild(child, PresContext(), kidDesiredSize, &kidReflowState, wm, childOrigin, containerSize, 0); childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child); if (childContentBEnd > aConfig.mColMaxBSize) { allFit = false; } if (childContentBEnd > availSize.BSize(wm)) { aColData.mMaxOverflowingBSize = std::max(childContentBEnd, aColData.mMaxOverflowingBSize); } } contentRect.UnionRect(contentRect, child->GetRect()); ConsiderChildOverflow(overflowRects, child); contentBEnd = std::max(contentBEnd, childContentBEnd); aColData.mLastBSize = childContentBEnd; aColData.mSumBSize += 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 kidNextInFlow = CreateNextInFlow(child); } // 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.mHasExcessBSize = 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) { childOrigin.I(wm) += aConfig.mColISize + aConfig.mColGap; #ifdef DEBUG_roc printf("*** NEXT CHILD ORIGIN.icoord = %d\n", childOrigin.I(wm)); #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.mMaxBSize = contentBEnd; LogicalSize contentSize = LogicalSize(wm, contentRect.Size()); contentSize.BSize(wm) = std::max(contentSize.BSize(wm), contentBEnd); mLastFrameStatus = aStatus; // Apply computed and min/max values if (aConfig.mComputedBSize != NS_INTRINSICSIZE) { if (aReflowState.AvailableBSize() != NS_INTRINSICSIZE) { contentSize.BSize(wm) = std::min(contentSize.BSize(wm), aConfig.mComputedBSize); } else { contentSize.BSize(wm) = aConfig.mComputedBSize; } } else { // We add the "consumed" block-size back in so that we're applying // constraints to the correct bSize 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 bSize. but which // has prev-in-flows, trigger a larger bSize than actually required. contentSize.BSize(wm) = aReflowState.ApplyMinMaxBSize(contentSize.BSize(wm), aConfig.mConsumedBSize); } if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) { contentSize.ISize(wm) = aReflowState.ComputedISize(); } else { contentSize.ISize(wm) = aReflowState.ApplyMinMaxISize(contentSize.ISize(wm)); } contentSize.ISize(wm) += borderPadding.IStartEnd(wm); contentSize.BSize(wm) += borderPadding.BStartEnd(wm); aDesiredSize.SetSize(wm, contentSize); aDesiredSize.mOverflowAreas = overflowRects; aDesiredSize.UnionOverflowAreasWithDesiredBounds(); // In vertical-rl mode, make a second pass if necessary to reposition the // columns with the correct container width. (In other writing modes, // correct containerSize was not required for column positioning so we don't // need this fixup.) if (wm.IsVerticalRL() && containerSize.width != contentSize.Width(wm)) { const nsSize finalContainerSize = aDesiredSize.PhysicalSize(); for (nsIFrame* child : mFrames) { // Get the logical position as set previously using a provisional or // dummy containerSize, and reset with the correct container size. child->SetPosition(wm, child->GetLogicalPosition(wm, containerSize), finalContainerSize); } } #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); }
/** * Attempt to place the block frame within the available space. If * it fits, apply inline-dir ("horizontal") positioning (CSS 10.3.3), * collapse margins (CSS2 8.3.1). Also apply relative positioning. */ bool nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState, bool aForceFit, nsLineBox* aLine, nsCollapsingMargin& aBEndMarginResult, nsOverflowAreas& aOverflowAreas, nsReflowStatus aReflowStatus) { // Compute collapsed block-end margin value. WritingMode wm = aReflowState.GetWritingMode(); WritingMode parentWM = mMetrics.GetWritingMode(); if (NS_FRAME_IS_COMPLETE(aReflowStatus)) { aBEndMarginResult = mMetrics.mCarriedOutBEndMargin; aBEndMarginResult.Include(aReflowState.ComputedLogicalMargin(). ConvertTo(parentWM, wm).BEnd(parentWM)); } else { // The used bottom-margin is set to zero above a break. aBEndMarginResult.Zero(); } nscoord backupContainingBlockAdvance = 0; // Check whether the block's block-end margin collapses with its block-start // margin. See CSS 2.1 section 8.3.1; those rules seem to match // nsBlockFrame::IsEmpty(). Any such block must have zero block-size so // check that first. Note that a block can have clearance and still // have adjoining block-start/end margins, because the clearance goes // above the block-start margin. // Mark the frame as non-dirty; it has been reflowed (or we wouldn't // be here), and we don't want to assert in CachedIsEmpty() mFrame->RemoveStateBits(NS_FRAME_IS_DIRTY); bool empty = 0 == mMetrics.BSize(parentWM) && aLine->CachedIsEmpty(); if (empty) { // Collapse the block-end margin with the block-start margin that was // already applied. aBEndMarginResult.Include(mBStartMargin); #ifdef NOISY_BLOCKDIR_MARGINS printf(" "); nsFrame::ListTag(stdout, mOuterReflowState.frame); printf(": "); nsFrame::ListTag(stdout, mFrame); printf(" -- collapsing block start & end margin together; BStart=%d spaceBStart=%d\n", mBCoord, mSpace.BStart(mWritingMode)); #endif // Section 8.3.1 of CSS 2.1 says that blocks with adjoining // "top/bottom" (i.e. block-start/end) margins whose top margin collapses // with their parent's top margin should have their top border-edge at the // top border-edge of their parent. We actually don't have to do // anything special to make this happen. In that situation, // nsBlockFrame::ShouldApplyBStartMargin will have returned false, // and mBStartMargin and aClearance will have been zero in // ReflowBlock. // If we did apply our block-start margin, but now we're collapsing it // into the block-end margin, we need to back up the containing // block's bCoord-advance by our block-start margin so that it doesn't get // counted twice. Note that here we're allowing the line's bounds // to become different from the block's position; we do this // because the containing block will place the next line at the // line's BEnd, and it must place the next line at a different // point from where this empty block will be. backupContainingBlockAdvance = mBStartMargin.get(); } // See if the frame fit. If it's the first frame or empty then it // always fits. If the block-size is unconstrained then it always fits, // even if there's some sort of integer overflow that makes bCoord + // mMetrics.BSize() appear to go beyond the available height. if (!empty && !aForceFit && mSpace.BSize(mWritingMode) != NS_UNCONSTRAINEDSIZE) { nscoord bEnd = mBCoord - backupContainingBlockAdvance + mMetrics.BSize(mWritingMode); if (bEnd > mSpace.BEnd(mWritingMode)) { // didn't fit, we must acquit. mFrame->DidReflow(mPresContext, &aReflowState, nsDidReflowStatus::FINISHED); return false; } } aLine->SetBounds(mWritingMode, mICoord, mBCoord - backupContainingBlockAdvance, mMetrics.ISize(mWritingMode), mMetrics.BSize(mWritingMode), mContainerWidth); WritingMode frameWM = mFrame->GetWritingMode(); LogicalPoint logPos = LogicalPoint(mWritingMode, mICoord, mBCoord). ConvertTo(frameWM, mWritingMode, mContainerWidth - mMetrics.Width()); aReflowState.ApplyRelativePositioning(&logPos, mContainerWidth); // Now place the frame and complete the reflow process nsContainerFrame::FinishReflowChild(mFrame, mPresContext, mMetrics, &aReflowState, frameWM, logPos, mContainerWidth, 0); aOverflowAreas = mMetrics.mOverflowAreas + mFrame->GetPosition(); return true; }
void nsColumnSetFrame::FindBestBalanceBSize(const nsHTMLReflowState& aReflowState, nsPresContext* aPresContext, ReflowConfig& aConfig, ColumnBalanceData& aColData, nsHTMLReflowMetrics& aDesiredSize, nsCollapsingMargin& aOutMargin, bool& aUnboundedLastColumn, bool& aRunWasFeasible, nsReflowStatus& aStatus) { bool feasible = aRunWasFeasible; nsMargin bp = aReflowState.ComputedPhysicalBorderPadding(); bp.ApplySkipSides(GetSkipSides()); bp.bottom = aReflowState.ComputedPhysicalBorderPadding().bottom; nscoord availableContentBSize = GetAvailableContentBSize(aReflowState); // Termination of the algorithm below is guaranteed because // aConfig.knownFeasibleBSize - aConfig.knownInfeasibleBSize decreases in every // iteration. // We set this flag when we detect that we may contain a frame // that can break anywhere (thus foiling the linear decrease-by-one // search) bool maybeContinuousBreakingDetected = false; while (!aPresContext->HasPendingInterrupt()) { nscoord lastKnownFeasibleBSize = aConfig.mKnownFeasibleBSize; // Record what we learned from the last reflow if (feasible) { // maxBSize is feasible. Also, mLastBalanceBSize is feasible. aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize, aColData.mMaxBSize); aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize, mLastBalanceBSize); // Furthermore, no height less than the height of the last // column can ever be feasible. (We might be able to reduce the // height of a non-last column by moving content to a later column, // but we can't do that with the last column.) if (mFrames.GetLength() == aConfig.mBalanceColCount) { aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize, aColData.mLastBSize - 1); } } else { aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize, mLastBalanceBSize); // If a column didn't fit in its available height, then its current // height must be the minimum height for unbreakable content in // the column, and therefore no smaller height can be feasible. aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize, aColData.mMaxOverflowingBSize - 1); if (aUnboundedLastColumn) { // The last column is unbounded, so all content got reflowed, so the // mColMaxBSize is feasible. aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize, aColData.mMaxBSize); } } #ifdef DEBUG_roc printf("*** nsColumnSetFrame::Reflow balancing knownInfeasible=%d knownFeasible=%d\n", aConfig.mKnownInfeasibleBSize, aConfig.mKnownFeasibleBSize); #endif if (aConfig.mKnownInfeasibleBSize >= aConfig.mKnownFeasibleBSize - 1) { // aConfig.mKnownFeasibleBSize is where we want to be break; } if (aConfig.mKnownInfeasibleBSize >= availableContentBSize) { break; } if (lastKnownFeasibleBSize - aConfig.mKnownFeasibleBSize == 1) { // We decreased the feasible height by one twip only. This could // indicate that there is a continuously breakable child frame // that we are crawling through. maybeContinuousBreakingDetected = true; } nscoord nextGuess = (aConfig.mKnownFeasibleBSize + aConfig.mKnownInfeasibleBSize)/2; // The constant of 600 twips is arbitrary. It's about two line-heights. if (aConfig.mKnownFeasibleBSize - nextGuess < 600 && !maybeContinuousBreakingDetected) { // We're close to our target, so just try shrinking just the // minimum amount that will cause one of our columns to break // differently. nextGuess = aConfig.mKnownFeasibleBSize - 1; } else if (aUnboundedLastColumn) { // Make a guess by dividing that into N columns. Add some slop // to try to make it on the feasible side. The constant of // 600 twips is arbitrary. It's about two line-heights. nextGuess = aColData.mSumBSize/aConfig.mBalanceColCount + 600; // Sanitize it nextGuess = clamped(nextGuess, aConfig.mKnownInfeasibleBSize + 1, aConfig.mKnownFeasibleBSize - 1); } else if (aConfig.mKnownFeasibleBSize == NS_INTRINSICSIZE) { // This can happen when we had a next-in-flow so we didn't // want to do an unbounded height measuring step. Let's just increase // from the infeasible height by some reasonable amount. nextGuess = aConfig.mKnownInfeasibleBSize*2 + 600; } // Don't bother guessing more than our height constraint. nextGuess = std::min(availableContentBSize, nextGuess); #ifdef DEBUG_roc printf("*** nsColumnSetFrame::Reflow balancing choosing next guess=%d\n", nextGuess); #endif aConfig.mColMaxBSize = nextGuess; aUnboundedLastColumn = false; AddStateBits(NS_FRAME_IS_DIRTY); feasible = ReflowColumns(aDesiredSize, aReflowState, aStatus, aConfig, false, &aOutMargin, aColData); if (!aConfig.mIsBalancing) { // Looks like we had excess height when balancing, so we gave up on // trying to balance. break; } } if (aConfig.mIsBalancing && !feasible && !aPresContext->HasPendingInterrupt()) { // We may need to reflow one more time at the feasible height to // get a valid layout. bool skip = false; if (aConfig.mKnownInfeasibleBSize >= availableContentBSize) { aConfig.mColMaxBSize = availableContentBSize; if (mLastBalanceBSize == availableContentBSize) { skip = true; } } else { aConfig.mColMaxBSize = aConfig.mKnownFeasibleBSize; } if (!skip) { // If our height is unconstrained, make sure that the last column is // allowed to have arbitrary height here, even though we were balancing. // Otherwise we'd have to split, and it's not clear what we'd do with // that. AddStateBits(NS_FRAME_IS_DIRTY); feasible = ReflowColumns(aDesiredSize, aReflowState, aStatus, aConfig, availableContentBSize == NS_UNCONSTRAINEDSIZE, &aOutMargin, aColData); } } aRunWasFeasible = feasible; }
NS_IMETHODIMP nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { nsresult rv = NS_OK; nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE); nsReflowStatus childStatus; aDesiredSize.width = aDesiredSize.height = 0; aDesiredSize.ascent = 0; nsBoundingMetrics bmSqr, bmBase, bmIndex; nsRenderingContext& renderingContext = *aReflowState.rendContext; ////////////////// // Reflow Children int32_t count = 0; nsIFrame* baseFrame = nullptr; nsIFrame* indexFrame = nullptr; nsHTMLReflowMetrics baseSize; nsHTMLReflowMetrics indexSize; nsIFrame* childFrame = mFrames.FirstChild(); while (childFrame) { // ask our children to compute their bounding metrics nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS); nsHTMLReflowState childReflowState(aPresContext, aReflowState, childFrame, availSize); rv = ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowState, childStatus); //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status"); if (NS_FAILED(rv)) { // Call DidReflow() for the child frames we successfully did reflow. DidReflowChildren(mFrames.FirstChild(), childFrame); return rv; } 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 rv = ReflowError(renderingContext, aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); // Call DidReflow() for the child frames we successfully did reflow. DidReflowChildren(mFrames.FirstChild(), childFrame); return rv; } //////////// // Prepare the radical symbol and the overline bar nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); renderingContext.SetFont(fm); // For radical glyphs from TeX fonts and some of the radical glyphs from // Mathematica fonts, the thickness of the overline can be obtained from the // ascent of the glyph. Most fonts however have radical glyphs above the // baseline so no assumption can be made about the meaning of the ascent. nscoord ruleThickness, leading, em; GetRuleThickness(renderingContext, fm, ruleThickness); PRUnichar one = '1'; nsBoundingMetrics bmOne = renderingContext.GetBoundingMetrics(&one, 1); // get the leading to be left at the top of the resulting frame // this seems more reliable than using fm->GetLeading() on suspicious fonts GetEmHeight(fm, em); leading = nscoord(0.2f * em); // Rule 11, App. G, TeXbook // psi = clearance between rule and content nscoord phi = 0, psi = 0; if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) phi = fm->XHeight(); else phi = ruleThickness; psi = ruleThickness + phi/4; // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131) 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, renderingContext, NS_STRETCH_DIRECTION_VERTICAL, contSize, radicalSize, NS_STRETCH_LARGER, NS_MATHML_IS_RTL(mPresentationData.flags)); // 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 = NS_MAX(bmBase.descent, (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent)); mBoundingMetrics.width = bmSqr.width + bmBase.width; mBoundingMetrics.leftBearing = bmSqr.leftBearing; mBoundingMetrics.rightBearing = bmSqr.width + NS_MAX(bmBase.width, bmBase.rightBearing); // take also care of the rule aDesiredSize.ascent = mBoundingMetrics.ascent + leading; aDesiredSize.height = aDesiredSize.ascent + NS_MAX(baseSize.height - baseSize.ascent, 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 nscoord raiseIndexDelta = NSToCoordRound(0.6f * (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.ascent; aDesiredSize.ascent = mBoundingMetrics.ascent + leading; aDesiredSize.height = aDesiredSize.ascent + descent; } nscoord dxIndex, dxSqr; GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr); mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width; mBoundingMetrics.leftBearing = NS_MIN(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing); mBoundingMetrics.rightBearing = dxSqr + bmSqr.width + NS_MAX(bmBase.width, bmBase.rightBearing); aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; GatherAndStoreOverflow(&aDesiredSize); // place the index nscoord dx = dxIndex; nscoord dy = aDesiredSize.ascent - (indexRaisedAscent + indexSize.ascent - bmIndex.ascent); FinishReflowChild(indexFrame, aPresContext, nullptr, indexSize, 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.ascent - baseSize.ascent; FinishReflowChild(baseFrame, aPresContext, nullptr, baseSize, MirrorIfRTL(aDesiredSize.width, baseSize.width, dx), dy, 0); mReference.x = 0; mReference.y = aDesiredSize.ascent; aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return NS_OK; }
void nsVideoFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsVideoFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsVideoFrame::Reflow: availSize=%d,%d", aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; aMetrics.Width() = aReflowState.ComputedWidth(); aMetrics.Height() = aReflowState.ComputedHeight(); // stash this away so we can compute our inner area later mBorderPadding = aReflowState.ComputedPhysicalBorderPadding(); aMetrics.Width() += mBorderPadding.left + mBorderPadding.right; aMetrics.Height() += mBorderPadding.top + mBorderPadding.bottom; // Reflow the child frames. We may have up to two, an image frame // which is the poster, and a box frame, which is the video controls. for (nsIFrame *child = mFrames.FirstChild(); child; child = child->GetNextSibling()) { if (child->GetContent() == mPosterImage) { // Reflow the poster frame. nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child); nsHTMLReflowMetrics kidDesiredSize(aReflowState); WritingMode wm = imageFrame->GetWritingMode(); LogicalSize availableSize = aReflowState.AvailableSize(wm); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, imageFrame, availableSize, aMetrics.Width(), aMetrics.Height()); nsRect posterRenderRect; if (ShouldDisplayPoster()) { posterRenderRect = nsRect(nsPoint(mBorderPadding.left, mBorderPadding.top), nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); } kidReflowState.SetComputedWidth(posterRenderRect.width); kidReflowState.SetComputedHeight(posterRenderRect.height); ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowState, posterRenderRect.x, posterRenderRect.y, 0, aStatus); FinishReflowChild(imageFrame, aPresContext, kidDesiredSize, &kidReflowState, posterRenderRect.x, posterRenderRect.y, 0); } else if (child->GetContent() == mVideoControls) { // Reflow the video controls frame. nsBoxLayoutState boxState(PresContext(), aReflowState.rendContext); nsSize size = child->GetSize(); nsBoxFrame::LayoutChildAt(boxState, child, nsRect(mBorderPadding.left, mBorderPadding.top, aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); if (child->GetSize() != size) { nsRefPtr<nsRunnable> event = new DispatchResizeToControls(child->GetContent()); nsContentUtils::AddScriptRunner(event); } } else if (child->GetContent() == mCaptionDiv) { // Reflow to caption div nsHTMLReflowMetrics kidDesiredSize(aReflowState); WritingMode wm = child->GetWritingMode(); LogicalSize availableSize = aReflowState.AvailableSize(wm); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, child, availableSize, aMetrics.Width(), aMetrics.Height()); nsSize size(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); size.width -= kidReflowState.ComputedPhysicalBorderPadding().LeftRight(); size.height -= kidReflowState.ComputedPhysicalBorderPadding().TopBottom(); kidReflowState.SetComputedWidth(std::max(size.width, 0)); kidReflowState.SetComputedHeight(std::max(size.height, 0)); ReflowChild(child, aPresContext, kidDesiredSize, kidReflowState, mBorderPadding.left, mBorderPadding.top, 0, aStatus); FinishReflowChild(child, aPresContext, kidDesiredSize, &kidReflowState, mBorderPadding.left, mBorderPadding.top, 0); } } aMetrics.SetOverflowAreasToDesiredBounds(); FinishAndStoreOverflow(&aMetrics); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsVideoFrame::Reflow: size=%d,%d", aMetrics.Width(), aMetrics.Height())); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); }