Bug 626395. Make sure that when we're clipping our kids we don't create overflow containers for their overflow. Force the kids to lay out within our size instead. Also don't include overflowing kids in our overflow area if we're clipping them. r=roc, a=blocker

This commit is contained in:
Boris Zbarsky 2011-01-19 17:39:31 -05:00
parent df7aab2ff1
commit 2b3d8a6402
13 changed files with 164 additions and 41 deletions

View File

@ -947,6 +947,13 @@ CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState& aReflowState,
return cbSize;
}
static inline PRBool IsClippingChildren(nsIFrame* aFrame,
const nsHTMLReflowState& aReflowState)
{
return aReflowState.mStyleDisplay->mOverflowX == NS_STYLE_OVERFLOW_CLIP ||
nsFrame::ApplyPaginatedOverflowClipping(aFrame);
}
NS_IMETHODIMP
nsBlockFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aMetrics,
@ -972,12 +979,36 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
}
#endif
const nsHTMLReflowState *reflowState = &aReflowState;
nsAutoPtr<nsHTMLReflowState> mutableReflowState;
// If we have non-auto height, we're clipping our kids and we fit,
// make sure our kids fit too.
if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE &&
aReflowState.ComputedHeight() != NS_AUTOHEIGHT &&
IsClippingChildren(this, aReflowState)) {
nsMargin heightExtras = aReflowState.mComputedBorderPadding;
if (GetSkipSides() & NS_SIDE_TOP) {
heightExtras.top = 0;
} else {
// Bottom margin never causes us to create continuations, so we
// don't need to worry about whether it fits in its entirety.
heightExtras.top += aReflowState.mComputedMargin.top;
}
if (GetEffectiveComputedHeight(aReflowState) + heightExtras.TopBottom() <=
aReflowState.availableHeight) {
mutableReflowState = new nsHTMLReflowState(aReflowState);
mutableReflowState->availableHeight = NS_UNCONSTRAINEDSIZE;
reflowState = mutableReflowState;
}
}
// See comment below about oldSize. Use *only* for the
// abs-pos-containing-block-size-change optimization!
nsSize oldSize = GetSize();
// Should we create a float manager?
nsAutoFloatManager autoFloatManager(const_cast<nsHTMLReflowState &>(aReflowState));
nsAutoFloatManager autoFloatManager(const_cast<nsHTMLReflowState&>(*reflowState));
// XXXldb If we start storing the float manager in the frame rather
// than keeping it around only during reflow then we should create it
@ -993,12 +1024,12 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
// delete the line with the line cursor.
ClearLineCursor();
if (IsFrameTreeTooDeep(aReflowState, aMetrics, aStatus)) {
if (IsFrameTreeTooDeep(*reflowState, aMetrics, aStatus)) {
return NS_OK;
}
PRBool marginRoot = BlockIsMarginRoot(this);
nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics,
nsBlockReflowState state(*reflowState, aPresContext, this, aMetrics,
marginRoot, marginRoot, needFloatManager);
#ifdef IBMBIDI
@ -1021,7 +1052,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
nsOverflowAreas ocBounds;
nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
if (GetPrevInFlow()) {
ReflowOverflowContainerChildren(aPresContext, aReflowState, ocBounds, 0,
ReflowOverflowContainerChildren(aPresContext, *reflowState, ocBounds, 0,
ocStatus);
}
@ -1040,7 +1071,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
// If we're not dirty (which means we'll mark everything dirty later)
// and our width has changed, mark the lines dirty that we need to
// mark dirty for a resize reflow.
if (aReflowState.mFlags.mHResize)
if (reflowState->mFlags.mHResize)
PrepareResizeReflow(state);
mState &= ~NS_FRAME_FIRST_REFLOW;
@ -1055,7 +1086,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
// If we end in a BR with clear and affected floats continue,
// we need to continue, too.
if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight &&
if (NS_UNCONSTRAINEDSIZE != reflowState->availableHeight &&
NS_FRAME_IS_COMPLETE(state.mReflowStatus) &&
state.mFloatManager->ClearContinues(FindTrailingClear())) {
NS_FRAME_SET_INCOMPLETE(state.mReflowStatus);
@ -1096,7 +1127,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
nsLayoutUtils::LinePosition position;
PRBool havePosition = nsLayoutUtils::GetFirstLinePosition(this, &position);
nscoord lineTop = havePosition ? position.mTop
: aReflowState.mComputedBorderPadding.top;
: reflowState->mComputedBorderPadding.top;
ReflowBullet(state, metrics, lineTop);
NS_ASSERTION(!BulletIsEmpty() || metrics.height == 0,
"empty bullet took up space");
@ -1117,8 +1148,8 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
// Compute our final size
nscoord bottomEdgeOfChildren;
ComputeFinalSize(aReflowState, state, aMetrics, &bottomEdgeOfChildren);
ComputeOverflowAreas(aReflowState, aMetrics, bottomEdgeOfChildren);
ComputeFinalSize(*reflowState, state, aMetrics, &bottomEdgeOfChildren);
ComputeOverflowAreas(*reflowState, aMetrics, bottomEdgeOfChildren);
// Factor overflow container child bounds into the overflow area
aMetrics.mOverflowAreas.UnionWith(ocBounds);
// Factor pushed float child bounds into the overflow area
@ -1143,7 +1174,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
// will not be our real new size. This also happens to be more efficient.
if (mAbsoluteContainer.HasAbsoluteFrames()) {
PRBool haveInterrupt = aPresContext->HasPendingInterrupt();
if (aReflowState.WillReflowAgainForClearance() ||
if (reflowState->WillReflowAgainForClearance() ||
haveInterrupt) {
// Make sure that when we reflow again we'll actually reflow all the abs
// pos frames that might conceivably depend on our size (or all of them,
@ -1158,7 +1189,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
}
} else {
nsSize containingBlockSize =
CalculateContainingBlockSizeForAbsolutes(aReflowState,
CalculateContainingBlockSizeForAbsolutes(*reflowState,
nsSize(aMetrics.width,
aMetrics.height));
@ -1175,10 +1206,10 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
// viewport height, which can't change during incremental
// reflow.
PRBool cbHeightChanged =
!(isRoot && NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight()) &&
!(isRoot && NS_UNCONSTRAINEDSIZE == reflowState->ComputedHeight()) &&
aMetrics.height != oldSize.height;
rv = mAbsoluteContainer.Reflow(this, aPresContext, aReflowState,
rv = mAbsoluteContainer.Reflow(this, aPresContext, *reflowState,
state.mReflowStatus,
containingBlockSize.width,
containingBlockSize.height, PR_TRUE,
@ -1252,7 +1283,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
}
#endif
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
NS_FRAME_SET_TRUNCATION(aStatus, (*reflowState), aMetrics);
return rv;
}
@ -1352,18 +1383,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
if (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()) {
// Figure out how much of the computed height should be
// applied to this frame.
nscoord computedHeightLeftOver = aReflowState.ComputedHeight();
if (GetPrevInFlow()) {
// Reduce the height by the computed height of prev-in-flows.
for (nsIFrame* prev = GetPrevInFlow(); prev; prev = prev->GetPrevInFlow()) {
computedHeightLeftOver -= prev->GetRect().height;
}
// We just subtracted our top-border padding, since it was included in the
// first frame's height. Add it back to get the content height.
computedHeightLeftOver += aReflowState.mComputedBorderPadding.top;
// We may have stretched the frame beyond its computed height. Oh well.
computedHeightLeftOver = NS_MAX(0, computedHeightLeftOver);
}
nscoord computedHeightLeftOver = GetEffectiveComputedHeight(aReflowState);
NS_ASSERTION(!( IS_TRUE_OVERFLOW_CONTAINER(this)
&& computedHeightLeftOver ),
"overflow container must not have computedHeightLeftOver");
@ -1468,7 +1488,7 @@ nsBlockFrame::ComputeOverflowAreas(const nsHTMLReflowState& aReflowState,
nsRect bounds(0, 0, aMetrics.width, aMetrics.height);
nsOverflowAreas areas(bounds, bounds);
if (NS_STYLE_OVERFLOW_CLIP != aReflowState.mStyleDisplay->mOverflowX) {
if (!IsClippingChildren(this, aReflowState)) {
PRBool inQuirks = (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks);
for (line_iterator line = begin_lines(), line_end = end_lines();
line != line_end;
@ -7115,6 +7135,26 @@ nsBlockFrame::GetNearestAncestorBlock(nsIFrame* aCandidate)
return nsnull;
}
nscoord
nsBlockFrame::GetEffectiveComputedHeight(const nsHTMLReflowState& aReflowState) const
{
nscoord height = aReflowState.ComputedHeight();
NS_ABORT_IF_FALSE(height != NS_UNCONSTRAINEDSIZE, "Don't call me!");
if (GetPrevInFlow()) {
// Reduce the height by the computed height of prev-in-flows.
for (nsIFrame* prev = GetPrevInFlow(); prev; prev = prev->GetPrevInFlow()) {
height -= prev->GetRect().height;
}
// We just subtracted our top-border padding, since it was included in the
// first frame's height. Add it back to get the content height.
height += aReflowState.mComputedBorderPadding.top;
// We may have stretched the frame beyond its computed height. Oh well.
height = NS_MAX(0, height);
}
return height;
}
#ifdef IBMBIDI
nsresult
nsBlockFrame::ResolveBidi()

View File

@ -707,6 +707,13 @@ protected:
nsLineList* RemoveOverflowLines();
nsresult SetOverflowLines(nsLineList* aOverflowLines);
// Determine the computed height that's in effect for this block
// frame (that is, our computed height minus the heights of our
// previous in-flows).
// XXXbz this clearly makes laying out a block with N in-flows
// O(N^2)! Good thing the constant is tiny.
nscoord GetEffectiveComputedHeight(const nsHTMLReflowState& aReflowState) const;
/**
* This class is useful for efficiently modifying the out of flow
* overflow list. It gives the client direct writable access to

View File

@ -1228,18 +1228,6 @@ static inline PRBool ApplyOverflowHiddenClipping(nsIFrame* aFrame,
type == nsGkAtoms::bcTableCellFrame;
}
static inline PRBool ApplyPaginatedOverflowClipping(nsIFrame* aFrame,
const nsStyleDisplay* aDisp)
{
// If we're paginated and aFrame is a block, and it has
// NS_BLOCK_CLIP_PAGINATED_OVERFLOW set, then we want to clip our
// overflow.
return
aFrame->PresContext()->IsPaginated() &&
aFrame->GetType() == nsGkAtoms::blockFrame &&
(aFrame->GetStateBits() & NS_BLOCK_CLIP_PAGINATED_OVERFLOW) != 0;
}
static PRBool ApplyOverflowClipping(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame,
const nsStyleDisplay* aDisp, nsRect* aRect) {
@ -1252,7 +1240,7 @@ static PRBool ApplyOverflowClipping(nsDisplayListBuilder* aBuilder,
// frames, and any non-visible value for blocks in a paginated context).
// Other overflow clipping is applied by nsHTML/XULScrollFrame.
if (!ApplyOverflowHiddenClipping(aFrame, aDisp) &&
!ApplyPaginatedOverflowClipping(aFrame, aDisp)) {
!nsFrame::ApplyPaginatedOverflowClipping(aFrame)) {
PRBool clip = aDisp->mOverflowX == NS_STYLE_OVERFLOW_CLIP;
if (!clip)
return PR_FALSE;
@ -6129,7 +6117,8 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
NS_ASSERTION((disp->mOverflowY == NS_STYLE_OVERFLOW_CLIP) ==
(disp->mOverflowX == NS_STYLE_OVERFLOW_CLIP),
"If one overflow is clip, the other should be too");
if (disp->mOverflowX == NS_STYLE_OVERFLOW_CLIP) {
if (disp->mOverflowX == NS_STYLE_OVERFLOW_CLIP ||
nsFrame::ApplyPaginatedOverflowClipping(this)) {
// The contents are actually clipped to the padding area
aOverflowAreas.SetAllTo(bounds);
}

View File

@ -50,6 +50,7 @@
#include "nsFrameSelection.h"
#include "nsHTMLReflowState.h"
#include "nsHTMLReflowMetrics.h"
#include "nsHTMLParts.h"
/**
* nsFrame logging constants. We redefine the nspr
@ -572,6 +573,17 @@ public:
PRBool aLockScroll,
nsIFrame** aContainingBlock = nsnull);
// test whether aFrame should apply paginated overflow clipping.
static PRBool ApplyPaginatedOverflowClipping(nsIFrame* aFrame)
{
// If we're paginated and a block, and have NS_BLOCK_CLIP_PAGINATED_OVERFLOW
// set, then we want to clip our overflow.
return
aFrame->PresContext()->IsPaginated() &&
aFrame->GetType() == nsGkAtoms::blockFrame &&
(aFrame->GetStateBits() & NS_BLOCK_CLIP_PAGINATED_OVERFLOW) != 0;
}
protected:
// Test if we are selecting a table object:

View File

@ -0,0 +1,8 @@
<!DOCTYPE html>
<html class="reftest-print">
<body>
<div style="overflow: hidden; height: 3in">
</div>
Some text
</body>
</html>

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html class="reftest-print">
<body>
<div style="overflow: hidden; height: 3in;">
<div style="height: 10in;"></div>
</div>
Some text
</body>
</html>

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html class="reftest-print">
<body>
<div style="overflow: -moz-hidden-unscrollable; height: 3in;">
<div style="height: 10in;"></div>
</div>
Some text
</body>
</html>

View File

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html class="reftest-print">
<body>
<div style="overflow: hidden; height: 3in">
</div>
</body>
</html>

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html class="reftest-print">
<body>
<div style="overflow: hidden; height: 3in;
padding-bottom: 0.5in; margin-bottom: 5in">
<div style="height: 10in;"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html class="reftest-print">
<body>
<div style="overflow: -moz-hidden-unscrollable; height: 3in;
padding-bottom: 0.5in; margin-bottom: 5in">
<div style="height: 10in;"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html class="reftest-print">
<body>
<div style="overflow: hidden; height: 1in;
padding-bottom: 1in; border-bottom: 1in solid transparent">
<div style="height: 10in;"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html class="reftest-print">
<body>
<div style="overflow: -moz-hidden-unscrollable; height: 1in;
padding-bottom: 1in; border-bottom: 1in solid transparent">
<div style="height: 10in;"></div>
</div>
</body>
</html>

View File

@ -10,3 +10,9 @@
== 129941-1a.html 129941-1-ref.html
== 129941-1b.html 129941-1-ref.html
== 577450-1.html 577450-1-ref.html
== 626395-1a.html 626395-1-ref.html
== 626395-1b.html 626395-1-ref.html
== 626395-2a.html 626395-2-ref.html
== 626395-2b.html 626395-2-ref.html
== 626395-2c.html 626395-2-ref.html
== 626395-2d.html 626395-2-ref.html