Bug 375304. Fix absolute positioning for scrolled abs-pos containers with weird height constraints. patch by Eli Friedman, r+sr=roc

This commit is contained in:
roc+@cs.cmu.edu 2007-11-11 18:19:35 -08:00
parent 27f5bebbe9
commit 4da3232491
30 changed files with 250 additions and 95 deletions

View File

@ -782,12 +782,8 @@ CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState& aReflowState,
// The issue here is that for a 'height' of 'auto' the reflow state
// code won't know how to calculate the containing block height
// because it's calculated bottom up. So we use our own computed
// size as the dimensions. We don't really want to do this for the
// initial containing block
// size as the dimensions.
nsIFrame* frame = aReflowState.frame;
if (nsLayoutUtils::IsInitialContainingBlock(frame)) {
return nsSize(-1, -1);
}
nsSize cbSize(aFrameSize);
// Containing block is relative to the padding edge
@ -823,13 +819,9 @@ CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState& aReflowState,
nsBoxLayoutState dummyState(aLastRS->frame->PresContext(),
aLastRS->rendContext);
scrollbars = scrollFrame->GetDesiredScrollbarSizes(&dummyState);
// XXX We should account for the horizontal scrollbar too --- but currently
// nsGfxScrollFrame assumes nothing depends on the presence (or absence) of
// a horizontal scrollbar, so accounting for it would create incremental
// reflow bugs.
//if (!lastButOneRS->mFlags.mAssumingHScrollbar) {
if (!lastButOneRS->mFlags.mAssumingHScrollbar) {
scrollbars.top = scrollbars.bottom = 0;
//}
}
if (!lastButOneRS->mFlags.mAssumingVScrollbar) {
scrollbars.left = scrollbars.right = 0;
}

View File

@ -420,6 +420,19 @@ nsHTMLScrollFrame::ReflowScrolledFrame(const ScrollReflowState& aState,
nscoord availWidth = aState.mReflowState.ComputedWidth() + paddingLR;
nscoord computedHeight = aState.mReflowState.ComputedHeight();
nscoord computedMinHeight = aState.mReflowState.mComputedMinHeight;
nscoord computedMaxHeight = aState.mReflowState.mComputedMaxHeight;
if (aAssumeHScroll) {
nsSize hScrollbarPrefSize =
mInner.mHScrollbarBox->GetPrefSize(const_cast<nsBoxLayoutState&>(aState.mBoxState));
if (computedHeight != NS_UNCONSTRAINEDSIZE)
computedHeight = PR_MAX(0, computedHeight - hScrollbarPrefSize.height);
computedMinHeight = PR_MAX(0, computedMinHeight - hScrollbarPrefSize.height);
if (computedMaxHeight != NS_UNCONSTRAINEDSIZE)
computedMaxHeight = PR_MAX(0, computedMaxHeight - hScrollbarPrefSize.height);
}
if (aAssumeVScroll) {
nsSize vScrollbarPrefSize =
mInner.mVScrollbarBox->GetPrefSize(const_cast<nsBoxLayoutState&>(aState.mBoxState));
@ -443,6 +456,9 @@ nsHTMLScrollFrame::ReflowScrolledFrame(const ScrollReflowState& aState,
&aState.mReflowState.mComputedPadding);
kidReflowState.mFlags.mAssumingHScrollbar = aAssumeHScroll;
kidReflowState.mFlags.mAssumingVScrollbar = aAssumeVScroll;
kidReflowState.SetComputedHeight(computedHeight);
kidReflowState.mComputedMinHeight = computedMinHeight;
kidReflowState.mComputedMaxHeight = computedMaxHeight;
nsReflowStatus status;
nsresult rv = ReflowChild(mInner.mScrolledFrame, presContext, *aMetrics,
@ -526,13 +542,14 @@ nsresult
nsHTMLScrollFrame::ReflowContents(ScrollReflowState* aState,
const nsHTMLReflowMetrics& aDesiredSize)
{
PRBool currentlyUsingVScrollbar = GuessVScrollbarNeeded(*aState);
PRBool lastUsedVScrollbar = GuessVScrollbarNeeded(*aState);
PRBool lastUsedHScrollbar = mInner.mHasHorizontalScrollbar; // XXX is this good enough?
nsHTMLReflowMetrics kidDesiredSize(aDesiredSize.mFlags);
nsresult rv = ReflowScrolledFrame(*aState, PR_FALSE, currentlyUsingVScrollbar,
nsresult rv = ReflowScrolledFrame(*aState, lastUsedHScrollbar, lastUsedVScrollbar,
&kidDesiredSize, PR_TRUE);
if (NS_FAILED(rv))
return rv;
PRBool didUseScrollbar = currentlyUsingVScrollbar;
NS_ENSURE_SUCCESS(rv, rv);
PRBool relativeHeight = (mInner.mScrolledFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) != 0;
// There's an important special case ... if the child appears to fit
// in the inside-border rect (but overflows the scrollport), we
@ -550,7 +567,10 @@ nsHTMLScrollFrame::ReflowContents(ScrollReflowState* aState,
// Detecting when we enter this special case is important for when
// people design layouts that exactly fit the container "most of the
// time".
if (currentlyUsingVScrollbar &&
// XXX Is this check really sufficient to catch all the incremental cases
// where the ideal case doesn't have a scrollbar?
if ((lastUsedVScrollbar || (lastUsedHScrollbar && relativeHeight)) &&
aState->mStyles.mVertical != NS_STYLE_OVERFLOW_SCROLL &&
aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_SCROLL) {
nsSize insideBorderSize =
@ -559,56 +579,66 @@ nsHTMLScrollFrame::ReflowContents(ScrollReflowState* aState,
nsRect scrolledRect = mInner.GetScrolledRect(insideBorderSize);
if (nsRect(nsPoint(0, 0), insideBorderSize).Contains(scrolledRect)) {
// Let's pretend we had no vertical scrollbar coming in here
currentlyUsingVScrollbar = PR_FALSE;
rv = ReflowScrolledFrame(*aState, PR_FALSE, currentlyUsingVScrollbar,
rv = ReflowScrolledFrame(*aState, PR_FALSE, PR_FALSE,
&kidDesiredSize, PR_FALSE);
if (NS_FAILED(rv))
return rv;
didUseScrollbar = PR_FALSE;
NS_ENSURE_SUCCESS(rv, rv);
lastUsedVScrollbar = PR_FALSE;
lastUsedHScrollbar = PR_FALSE;
}
}
// First try a layout without a horizontal scrollbar, then with.
if (TryLayout(aState, kidDesiredSize, didUseScrollbar, PR_FALSE, PR_FALSE))
return NS_OK;
// XXX Adding a horizontal scrollbar could cause absolute children positioned
// relative to the bottom padding-edge to need to be reflowed. But we don't,
// because that would be slow.
if (TryLayout(aState, kidDesiredSize, didUseScrollbar, PR_TRUE, PR_FALSE))
return NS_OK;
if (!relativeHeight) {
if (TryLayout(aState, kidDesiredSize, lastUsedVScrollbar, PR_FALSE, PR_FALSE))
return NS_OK;
if (TryLayout(aState, kidDesiredSize, lastUsedVScrollbar, PR_TRUE, PR_FALSE))
return NS_OK;
} else {
if (TryLayout(aState, kidDesiredSize, lastUsedVScrollbar, lastUsedHScrollbar, PR_FALSE))
return NS_OK;
}
PRBool canHaveVerticalScrollbar =
aState->mStyles.mVertical != NS_STYLE_OVERFLOW_HIDDEN;
PRBool canHaveHorizontalScrollbar =
aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN;
// That didn't work. Try the other setting for the vertical scrollbar.
// But don't try to show a scrollbar if we know there can't be one.
if (currentlyUsingVScrollbar || canHaveVerticalScrollbar) {
nsHTMLReflowMetrics kidRetrySize(aDesiredSize.mFlags);
rv = ReflowScrolledFrame(*aState, PR_FALSE, !currentlyUsingVScrollbar,
&kidRetrySize, PR_FALSE);
if (NS_FAILED(rv))
return rv;
didUseScrollbar = !currentlyUsingVScrollbar;
// XXX Adding a horizontal scrollbar could cause absolute children positioned
// relative to the bottom padding-edge to need to be reflowed. But we don't,
// because that would be slow.
if (TryLayout(aState, kidRetrySize, didUseScrollbar, PR_FALSE, PR_FALSE))
return NS_OK;
if (TryLayout(aState, kidRetrySize, didUseScrollbar, PR_TRUE, PR_FALSE))
return NS_OK;
NS_WARNING("Strange content ... we can't find logically consistent scrollbar settings");
} else {
NS_WARNING("Strange content ... we can't find logically consistent scrollbar settings");
if (lastUsedVScrollbar || canHaveVerticalScrollbar) {
rv = ReflowScrolledFrame(*aState, PR_FALSE, !lastUsedVScrollbar,
&kidDesiredSize, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
lastUsedHScrollbar = PR_FALSE;
lastUsedVScrollbar = !lastUsedVScrollbar;
if (!relativeHeight) {
if (TryLayout(aState, kidDesiredSize, lastUsedVScrollbar, PR_FALSE, PR_FALSE))
return NS_OK;
if (TryLayout(aState, kidDesiredSize, lastUsedVScrollbar, PR_TRUE, PR_FALSE))
return NS_OK;
} else {
if (TryLayout(aState, kidDesiredSize, lastUsedVScrollbar, lastUsedHScrollbar, PR_FALSE))
return NS_OK;
}
}
// Fall back to no scrollbars --- even if NS_STYLE_OVERFLOW_SCROLL is
// in effect. They might not fit anyway.
if (didUseScrollbar) {
rv = ReflowScrolledFrame(*aState, PR_FALSE, PR_FALSE, &kidDesiredSize, PR_FALSE);
if (NS_FAILED(rv))
return rv;
if ((lastUsedHScrollbar || canHaveHorizontalScrollbar) && relativeHeight) {
rv = ReflowScrolledFrame(*aState, PR_TRUE, PR_FALSE,
&kidDesiredSize, PR_FALSE);
lastUsedHScrollbar = PR_TRUE;
lastUsedVScrollbar = PR_FALSE;
NS_ENSURE_SUCCESS(rv, rv);
if (TryLayout(aState, kidDesiredSize, lastUsedVScrollbar, lastUsedHScrollbar, PR_FALSE))
return NS_OK;
}
TryLayout(aState, kidDesiredSize, PR_FALSE, PR_FALSE, PR_TRUE);
rv = ReflowScrolledFrame(*aState, PR_TRUE, PR_TRUE,
&kidDesiredSize, PR_FALSE);
lastUsedHScrollbar = PR_TRUE;
lastUsedVScrollbar = PR_TRUE;
NS_ENSURE_SUCCESS(rv, rv);
TryLayout(aState, kidDesiredSize, lastUsedVScrollbar, lastUsedHScrollbar, PR_TRUE);
return NS_OK;
}

View File

@ -362,16 +362,7 @@ IsQuirkContainingBlockHeight(const nsHTMLReflowState* rs)
nsIAtom* frameType = rs->frame->GetType();
if (nsGkAtoms::blockFrame == frameType ||
nsGkAtoms::areaFrame == frameType ||
nsGkAtoms::scrollFrame == frameType) {
if (nsGkAtoms::areaFrame == frameType) {
// Skip over scrolled-content area frames
if (rs->frame->GetStyleContext()->GetPseudoType() ==
nsCSSAnonBoxes::scrolledContent) {
return PR_FALSE;
}
}
nsGkAtoms::scrollFrame == frameType) {
// Note: This next condition could change due to a style change,
// but that would cause a style reflow anyway, which means we're ok.
if (NS_AUTOHEIGHT == rs->ComputedHeight()) {
@ -424,7 +415,7 @@ nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext)
mStylePosition->mMinHeight.GetUnit() == eStyleUnit_Percent ||
mStylePosition->mMaxHeight.GetUnit() == eStyleUnit_Percent ||
mStylePosition->mOffset.GetTopUnit() == eStyleUnit_Percent ||
mStylePosition->mOffset.GetBottomUnit() == eStyleUnit_Percent ||
mStylePosition->mOffset.GetBottomUnit() != eStyleUnit_Auto ||
frame->IsBoxFrame();
// If we're the child of a table cell that performs special height
@ -1416,14 +1407,6 @@ CalcQuirkContainingBlockHeight(const nsHTMLReflowState* aCBReflowState)
if (nsGkAtoms::blockFrame == frameType ||
nsGkAtoms::areaFrame == frameType ||
nsGkAtoms::scrollFrame == frameType) {
if (nsGkAtoms::areaFrame == frameType) {
// Skip over scrolled-content area frames
if (rs->frame->GetStyleContext()->GetPseudoType() ==
nsCSSAnonBoxes::scrolledContent) {
continue;
}
}
secondAncestorRS = firstAncestorRS;
firstAncestorRS = (nsHTMLReflowState*)rs;
@ -1441,12 +1424,7 @@ CalcQuirkContainingBlockHeight(const nsHTMLReflowState* aCBReflowState)
}
}
else if (nsGkAtoms::canvasFrame == frameType) {
// Use scroll frames' computed height if we have one, this will
// allow us to get viewport height for native scrollbars.
nsHTMLReflowState* scrollState = (nsHTMLReflowState *)rs->parentReflowState;
if (nsGkAtoms::scrollFrame == scrollState->frame->GetType()) {
rs = scrollState;
}
// Always continue on to the height calculation
}
else if (nsGkAtoms::pageContentFrame == frameType) {
nsIFrame* prevInFlow = rs->frame->GetPrevInFlow();
@ -1667,24 +1645,14 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext,
// content
nsIAtom* fType;
if (NS_AUTOHEIGHT == aContainingBlockHeight) {
// See if the containing block is (1) a scrolled frame, i.e. its
// parent is a scroll frame. The presence of the intervening
// frame (that the scroll frame scrolls) needs to be hidden from
// the containingBlockHeight calcuation, or (2) a cell frame which needs
// See if the containing block is a cell frame which needs
// to use the mComputedHeight of the cell instead of what the cell block passed in.
// XXX It seems like this could lead to bugs with min-height and friends
if (cbrs->parentReflowState) {
nsIFrame* f = cbrs->parentReflowState->frame;
fType = f->GetType();
if (nsGkAtoms::scrollFrame == fType) {
// Use the scroll frame's computed height instead
aContainingBlockHeight = cbrs->parentReflowState->mComputedHeight;
}
else {
fType = cbrs->frame->GetType();
if (IS_TABLE_CELL(fType)) {
// use the cell's computed height
aContainingBlockHeight = cbrs->mComputedHeight;
}
fType = cbrs->frame->GetType();
if (IS_TABLE_CELL(fType)) {
// use the cell's computed height
aContainingBlockHeight = cbrs->mComputedHeight;
}
}
}

View File

@ -0,0 +1 @@
<div style="height:200px;width:200px; background:green">

View File

@ -0,0 +1,2 @@
<div style="overflow:auto; background:green; height:200px; width:200px">
<div style="width:300px; height:1px">

View File

@ -0,0 +1,5 @@
<!DOCTYPE html>
<div style="overflow:auto; width:200px; position:absolute; height:200px;">
<div style="position:absolute; width:300px; height:100%; background:green">
</div>
</div>

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<body onload="document.getElementById('a').style.height='100%';">
<div style="overflow:auto; width:200px; position:absolute; height:200px;">
<div id=a style="position:absolute; width:300px; background:green">
</div>
</div>

View File

@ -0,0 +1,5 @@
<!DOCTYPE html>
<div style="overflow:auto; width:200px; position:absolute; height:200px;">
<div style="position:absolute; width:300px; height:100%; background:green">
</div>
</div>

View File

@ -0,0 +1,5 @@
<body onload="document.getElementById('a').style.height='100%';">
<div style="overflow:auto; width:200px; position:absolute; height:200px;">
<div id=a style="position:absolute; width:300px; background:green">
</div>
</div>

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<body onload="document.getElementById('a').style.minHeight='100%';">
<div style="overflow:auto; width:200px; position:absolute; height:200px;">
<div id=a style="position:absolute; width:300px; background:green">
</div>
</div>

View File

@ -0,0 +1,5 @@
<body onload="document.getElementById('a').style.minHeight='100%';">
<div style="overflow:auto; width:200px; position:absolute; height:200px;">
<div id=a style="position:absolute; width:300px; background:green">
</div>
</div>

View File

@ -0,0 +1,5 @@
<!DOCTYPE html>
<div style="overflow:auto; width:200px; height:200px;">
<div style="width:300px; height:100%; background:green">
</div>
</div>

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<body onload="document.getElementById('a').style.height='100%';">
<div style="overflow:auto; width:200px; height:200px;">
<div id=a style="width:300px; background:green">
</div>
</div>

View File

@ -0,0 +1,4 @@
<div style="overflow:auto; width:200px; height:200px;">
<div style="width:300px; height:100%; background:green">
</div>
</div>

View File

@ -0,0 +1,5 @@
<body onload="document.getElementById('a').style.height='100%';">
<div style="overflow:auto; width:200px; height:200px;">
<div id=a style="width:300px; background:green">
</div>
</div>

View File

@ -0,0 +1,6 @@
<div style="overflow:auto; width:200px; height:200px;">
<div>
<div style="width:300px; height:100%; background:green">
</div>
</div>
</div>

View File

@ -0,0 +1,7 @@
<body onload="document.getElementById('a').style.height='100%';">
<div style="overflow:auto; width:200px; height:200px;">
<div>
<div id=a style="width:300px; background:green">
</div>
</div>
</div>

View File

@ -0,0 +1,8 @@
<div style="width:200px; height:200px; overflow:auto">
<div>
<table cellspacing=0 cellpadding=0 border=0 height="100%"><tr><td>
<div style="width:300px; height:100%; background:green"><div style="height:1px"><!--Workaround for unrelated table bug--></div>
</div>
</table>
</div>
</div>

View File

@ -0,0 +1,9 @@
<body onload="document.getElementById('a').style.height='100%';">
<div style="width:200px; height:200px; overflow:auto">
<div>
<table id=a cellspacing=0 cellpadding=0 border=0 height="100%"><tr><td>
<div style="width:300px; background:green; height:100%"><div style="height:1px"><!--Workaround for unrelated table bug--></div>
</div>
</table>
</div>
</div>

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<body onload="document.getElementById('a').style.minHeight='100%';">
<div style="overflow:auto; width:200px; height:200px;">
<div id=a style="width:300px; background:green">
</div>
</div>

View File

@ -0,0 +1,5 @@
<body onload="document.getElementById('a').style.minHeight='100%';">
<div style="overflow:auto; width:200px; height:200px;">
<div id=a style="width:300px; background:green">
</div>
</div>

View File

@ -0,0 +1,9 @@
<body onload="document.getElementById('a').style.minHeight='100%';">
<div style="width:200px; height:200px; overflow:auto">
<div>
<table id=a cellspacing=0 cellpadding=0 border=0 height="100%"><tr><td>
<div style="width:300px; background:green; height:100%"><div style="height:1px"><!--Workaround for unrelated table bug--></div>
</div>
</table>
</div>
</div>

View File

@ -0,0 +1,23 @@
== simpleHeight100.html greenbox.html
== simpleAbsHeight.html greenbox.html
== hScrollSimpleHeight.html greenboxhbar.html
== hScrollSimpleHeightQuirks-1.html greenboxhbar.html
== hScrollSimpleHeightQuirks-2.html greenboxhbar.html
== hScrollSimpleHeightQuirks-3.html greenboxhbar.html
== hScrollAbsHeight.html greenboxhbar.html
== hScrollAbsHeightQuirks.html greenboxhbar.html
== simpleHeight100D.html greenbox.html
== simpleAbsHeightD.html greenbox.html
== hScrollSimpleHeightD.html greenboxhbar.html
== hScrollSimpleHeightQuirks-1D.html greenboxhbar.html
== hScrollSimpleHeightQuirks-2D.html greenboxhbar.html
== hScrollSimpleHeightQuirks-3D.html greenboxhbar.html
== hScrollAbsHeightD.html greenboxhbar.html
== hScrollAbsHeightQuirksD.html greenboxhbar.html
== simpleMinHeight100D.html greenbox.html
== simpleAbsMinHeightD.html greenbox.html
== hScrollSimpleMinHeightD.html greenboxhbar.html
== hScrollSimpleMinHeightQuirks-1D.html greenboxhbar.html
== hScrollSimpleMinHeightQuirks-3D.html greenboxhbar.html
== hScrollAbsMinHeightD.html greenboxhbar.html
== hScrollAbsMinHeightQuirksD.html greenboxhbar.html

View File

@ -0,0 +1,5 @@
<!DOCTYPE html>
<div style="overflow:auto; height:200px; width:200px; position:relative">
<div style="background: green; height:100%; width:100%; position:absolute">
</div>
</div>

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<body onload="document.getElementById('a').style.height='100%';">
<div style="overflow:auto; height:200px; width:200px; position:relative">
<div id=a style="background: green; width:100%; position:absolute">
</div>
</div>

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<body onload="document.getElementById('a').style.minHeight='100%'">
<div style="overflow:auto; height:200px; width:200px; position:relative">
<div id=a style="background: green; min-width:100%; position:absolute">
</div>
</div>

View File

@ -0,0 +1,5 @@
<!DOCTYPE html>
<div style="overflow:auto; height:200px; width:200px">
<div style="background: green; height:100%">
</div>
</div>

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<body onload="document.getElementById('a').style.height='100%';">
<div style="overflow:auto; height:200px; width:200px">
<div id=a style="background: green;">
</div>
</div>

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<body onload="document.getElementById('a').style.minHeight='100%';">
<div style="overflow:auto; height:200px; width:200px">
<div id=a style="background: green;">
</div>
</div>

View File

@ -71,3 +71,6 @@ include ib-split/reftest.list
# line-breaking
include line-breaking/reftest.list
include percent-overflow-sizing/reftest.list