mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 07:13:20 +00:00
Bug 298893. Collapse the leading and trailing margins of a column set. Makes columns degrade more gracefully.
This commit is contained in:
parent
2b4becb59d
commit
0984b1ecc0
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user