Bug 298893. Collapse the leading and trailing margins of a column set. Makes columns degrade more gracefully.

This commit is contained in:
roc+%cs.cmu.edu 2005-08-02 20:05:52 +00:00
parent 2b4becb59d
commit 0984b1ecc0
2 changed files with 72 additions and 32 deletions

View File

@ -80,6 +80,14 @@ nsBlockReflowContext::nsBlockReflowContext(nsPresContext* aPresContext,
mMetrics.mFlags |= NS_REFLOW_CALC_MAX_WIDTH;
}
static nsIFrame* DescendIntoBlockLevelFrame(nsIFrame* aFrame)
{
nsIAtom* type = aFrame->GetType();
if (type == nsLayoutAtoms::columnSetFrame)
return DescendIntoBlockLevelFrame(aFrame->GetFirstChild(nsnull));
return aFrame;
}
PRBool
nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame, PRBool* aMayNeedRetry)
@ -104,16 +112,18 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
// 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->GetPresContext();
if (0 == aRS.mComputedBorderPadding.top &&
!(aRS.frame->GetStateBits() & NS_BLOCK_MARGIN_ROOT) &&
NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, &bf))) {
!(frame->GetStateBits() & NS_BLOCK_MARGIN_ROOT) &&
NS_SUCCEEDED(frame->QueryInterface(kBlockFrameCID, &bf))) {
// 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 = NS_STATIC_CAST(nsBlockFrame*, aRS.frame);
for (nsBlockFrame* block = NS_STATIC_CAST(nsBlockFrame*, frame);
block; block = NS_STATIC_CAST(nsBlockFrame*, block->GetNextInFlow())) {
for (PRBool overflowLines = PR_FALSE; overflowLines <= PR_TRUE; ++overflowLines) {
nsBlockFrame::line_iterator line;
@ -143,8 +153,8 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
PRBool isEmpty = line->IsEmpty();
if (line->IsBlock()) {
nsBlockFrame* kidBlock = NS_STATIC_CAST(nsBlockFrame*, line->mFirstChild);
if (kidBlock == aClearanceFrame) {
nsIFrame* kid = line->mFirstChild;
if (kid == aClearanceFrame) {
line->SetHasClearance();
line->MarkDirty();
dirtiedLine = PR_TRUE;
@ -157,21 +167,43 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
// state for it. Since the reflow reason is irrelevant, we'll
// arbitrarily make it a `resize' to avoid the path-plucking
// behavior if we're in an incremental reflow.
nsSize availSpace(aRS.mComputedWidth, aRS.mComputedHeight);
nsHTMLReflowState reflowState(kidBlock->GetPresContext(),
aRS, kidBlock,
availSpace, eReflowReason_Resize);
// Record that we're being optimistic by assuming the kid
// has no clearance
if (kidBlock->GetStyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) {
*aMayNeedRetry = PR_TRUE;
// 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.mComputedWidth, aRS.mComputedHeight);
outerReflowState = new nsHTMLReflowState(prescontext,
aRS, frame,
availSpace, eReflowReason_Resize);
if (!outerReflowState)
goto done;
}
if (ComputeCollapsedTopMargin(reflowState, aMargin, aClearanceFrame, aMayNeedRetry)) {
line->MarkDirty();
dirtiedLine = PR_TRUE;
{
nsSize availSpace(outerReflowState->mComputedWidth,
outerReflowState->mComputedHeight);
nsHTMLReflowState innerReflowState(prescontext,
*outerReflowState, kid,
availSpace, eReflowReason_Resize);
// 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)) {
line->MarkDirty();
dirtiedLine = PR_TRUE;
}
if (isEmpty)
aMargin->Include(innerReflowState.mComputedMargin.bottom);
}
if (outerReflowState != &aRS) {
delete NS_CONST_CAST(nsHTMLReflowState*, outerReflowState);
}
if (isEmpty)
aMargin->Include(reflowState.mComputedMargin.bottom);
}
if (!isEmpty)
goto done;

View File

@ -127,7 +127,8 @@ protected:
nsReflowReason aReason,
nsReflowStatus& aStatus,
const ReflowConfig& aConfig,
PRBool aLastColumnUnbounded);
PRBool aLastColumnUnbounded,
nsCollapsingMargin* aCarriedOutBottomMargin);
};
/**
@ -337,11 +338,12 @@ static void MoveChildTo(nsIFrame* aParent, nsIFrame* aChild, nsPoint aOrigin) {
PRBool
nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowReason aKidReason,
nsReflowStatus& aStatus,
const ReflowConfig& aConfig,
PRBool aUnboundedLastColumn) {
const nsHTMLReflowState& aReflowState,
nsReflowReason aKidReason,
nsReflowStatus& aStatus,
const ReflowConfig& aConfig,
PRBool aUnboundedLastColumn,
nsCollapsingMargin* aBottomMarginCarriedOut) {
PRBool allFit = PR_TRUE;
PRBool RTL = GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
PRBool shrinkingHeightOnly = aKidReason == eReflowReason_Resize &&
@ -399,11 +401,12 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
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.
// 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.
PRBool skipIncremental = aKidReason == eReflowReason_Incremental
&& !(child->GetStateBits() & NS_FRAME_IS_DIRTY)
&& (!child->GetNextSibling()
|| !(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY))
&& child->GetNextSibling()
&& !(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY)
&& !aDesiredSize.mComputeMEW;
// 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.
@ -490,6 +493,8 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus);
*aBottomMarginCarriedOut = kidDesiredSize.mCarriedOutBottomMargin;
FinishReflowChild(child, GetPresContext(), &kidReflowState,
kidDesiredSize, childOrigin.x, childOrigin.y, 0);
@ -733,8 +738,9 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
// content back here and then have to push it out again!
nsIFrame* nextInFlow = GetNextInFlow();
PRBool unboundedLastColumn = isBalancing && nextInFlow;
nsCollapsingMargin carriedOutBottomMargin;
PRBool feasible = ReflowChildren(aDesiredSize, aReflowState, kidReason,
aStatus, config, unboundedLastColumn);
aStatus, config, unboundedLastColumn, &carriedOutBottomMargin);
if (isBalancing) {
if (feasible ||
@ -845,7 +851,8 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
unboundedLastColumn = PR_FALSE;
feasible = ReflowChildren(aDesiredSize, aReflowState,
kidReason, aStatus, config, PR_FALSE);
kidReason, aStatus, config, PR_FALSE,
&carriedOutBottomMargin);
}
if (!feasible) {
@ -861,15 +868,16 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
config.mColMaxHeight = knownFeasibleHeight;
}
if (!skip) {
ReflowChildren(aDesiredSize, aReflowState,
eReflowReason_Resize, aStatus, config, PR_FALSE);
ReflowChildren(aDesiredSize, aReflowState, eReflowReason_Resize,
aStatus, config, PR_FALSE, &carriedOutBottomMargin);
}
}
}
CheckInvalidateSizeChange(GetPresContext(), aDesiredSize, aReflowState);
FinishAndStoreOverflow(&aDesiredSize);
aDesiredSize.mCarriedOutBottomMargin = carriedOutBottomMargin;
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);