NS_IMETHODIMP nsCanvasFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow"); // Initialize OUT parameter aStatus = NS_FRAME_COMPLETE; nsCanvasFrame* prevCanvasFrame = static_cast<nsCanvasFrame*> (GetPrevInFlow()); if (prevCanvasFrame) { nsAutoPtr<nsFrameList> overflow(prevCanvasFrame->StealOverflowFrames()); if (overflow) { NS_ASSERTION(overflow->OnlyChild(), "must have doc root as canvas frame's only child"); nsContainerFrame::ReparentFrameViewList(aPresContext, *overflow, prevCanvasFrame, this); // Prepend overflow to the our child list. There may already be // children placeholders for fixed-pos elements, which don't get // reflowed but must not be lost until the canvas frame is destroyed. mFrames.InsertFrames(this, nullptr, *overflow); } } // Set our size up front, since some parts of reflow depend on it // being already set. Note that the computed height may be // unconstrained; that's ok. Consumers should watch out for that. SetSize(nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); // Reflow our one and only normal child frame. It's either the root // element's frame or a placeholder for that frame, if the root element // is abs-pos or fixed-pos. We may have additional children which // are placeholders for continuations of fixed-pos content, but those // don't need to be reflowed. The normal child is always comes before // the fixed-pos placeholders, because we insert it at the start // of the child list, above. nsHTMLReflowMetrics kidDesiredSize; if (mFrames.IsEmpty()) { // We have no child frame, so return an empty size aDesiredSize.width = aDesiredSize.height = 0; } else { nsIFrame* kidFrame = mFrames.FirstChild(); bool kidDirty = (kidFrame->GetStateBits() & NS_FRAME_IS_DIRTY) != 0; nsHTMLReflowState kidReflowState(aPresContext, aReflowState, kidFrame, nsSize(aReflowState.availableWidth, aReflowState.availableHeight)); if (aReflowState.mFlags.mVResize && (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { // Tell our kid it's being vertically resized too. Bit of a // hack for framesets. kidReflowState.mFlags.mVResize = true; } nsPoint kidPt(kidReflowState.mComputedMargin.left, kidReflowState.mComputedMargin.top); // Apply CSS relative positioning const nsStyleDisplay* styleDisp = kidFrame->GetStyleDisplay(); if (NS_STYLE_POSITION_RELATIVE == styleDisp->mPosition) { kidPt += nsPoint(kidReflowState.mComputedOffsets.left, kidReflowState.mComputedOffsets.top); } // Reflow the frame ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState, kidPt.x, kidPt.y, 0, aStatus); // Complete the reflow and position and size the child frame FinishReflowChild(kidFrame, aPresContext, &kidReflowState, kidDesiredSize, kidPt.x, kidPt.y, 0); if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { nsIFrame* nextFrame = kidFrame->GetNextInFlow(); NS_ASSERTION(nextFrame || aStatus & NS_FRAME_REFLOW_NEXTINFLOW, "If it's incomplete and has no nif yet, it must flag a nif reflow."); if (!nextFrame) { nsresult rv = aPresContext->PresShell()->FrameConstructor()-> CreateContinuingFrame(aPresContext, kidFrame, this, &nextFrame); NS_ENSURE_SUCCESS(rv, rv); SetOverflowFrames(aPresContext, nsFrameList(nextFrame, nextFrame)); // Root overflow containers will be normal children of // the canvas frame, but that's ok because there // aren't any other frames we need to isolate them from // during reflow. } if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { nextFrame->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } } // If the child frame was just inserted, then we're responsible for making sure // it repaints if (kidDirty) { // But we have a new child, which will affect our background, so // invalidate our whole rect. // Note: Even though we request to be sized to our child's size, our // scroll frame ensures that we are always the size of the viewport. // Also note: GetPosition() on a CanvasFrame is always going to return // (0, 0). We only want to invalidate GetRect() since Get*OverflowRect() // could also include overflow to our top and left (out of the viewport) // which doesn't need to be painted. nsIFrame* viewport = PresContext()->GetPresShell()->GetRootFrame(); viewport->InvalidateFrame(); } // Return our desired size. Normally it's what we're told, but // sometimes we can be given an unconstrained height (when a window // is sizing-to-content), and we should compute our desired height. aDesiredSize.width = aReflowState.ComputedWidth(); if (aReflowState.ComputedHeight() == NS_UNCONSTRAINEDSIZE) { aDesiredSize.height = kidFrame->GetRect().height + kidReflowState.mComputedMargin.TopBottom(); } else { aDesiredSize.height = aReflowState.ComputedHeight(); } aDesiredSize.SetOverflowAreasToDesiredBounds(); aDesiredSize.mOverflowAreas.UnionWith( kidDesiredSize.mOverflowAreas + kidPt); } if (prevCanvasFrame) { ReflowOverflowContainerChildren(aPresContext, aReflowState, aDesiredSize.mOverflowAreas, 0, aStatus); } FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); NS_FRAME_TRACE_REFLOW_OUT("nsCanvasFrame::Reflow", aStatus); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return NS_OK; }
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 = PR_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 = NS_MIN(NS_MAX(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 = PR_FALSE; AddStateBits(NS_FRAME_IS_DIRTY); feasible = ReflowChildren(aDesiredSize, aReflowState, aStatus, config, PR_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 = PR_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); FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); 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; }