From 38970ee28a246a6ab6003fdefccc9e2a09e3d056 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 19 Jan 2011 17:39:31 -0500 Subject: [PATCH] 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 --- layout/generic/nsBlockFrame.cpp | 94 +++++++++++++++------- layout/generic/nsBlockFrame.h | 7 ++ layout/generic/nsFrame.cpp | 17 +--- layout/generic/nsFrame.h | 12 +++ layout/reftests/printing/626395-1-ref.html | 8 ++ layout/reftests/printing/626395-1a.html | 9 +++ layout/reftests/printing/626395-1b.html | 9 +++ layout/reftests/printing/626395-2-ref.html | 7 ++ layout/reftests/printing/626395-2a.html | 9 +++ layout/reftests/printing/626395-2b.html | 9 +++ layout/reftests/printing/626395-2c.html | 9 +++ layout/reftests/printing/626395-2d.html | 9 +++ layout/reftests/printing/reftest.list | 6 ++ 13 files changed, 164 insertions(+), 41 deletions(-) create mode 100644 layout/reftests/printing/626395-1-ref.html create mode 100644 layout/reftests/printing/626395-1a.html create mode 100644 layout/reftests/printing/626395-1b.html create mode 100644 layout/reftests/printing/626395-2-ref.html create mode 100644 layout/reftests/printing/626395-2a.html create mode 100644 layout/reftests/printing/626395-2b.html create mode 100644 layout/reftests/printing/626395-2c.html create mode 100644 layout/reftests/printing/626395-2d.html diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index ac36fc85c4a..3cbbd660317 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -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 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(aReflowState)); + nsAutoFloatManager autoFloatManager(const_cast(*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() diff --git a/layout/generic/nsBlockFrame.h b/layout/generic/nsBlockFrame.h index c4f588fc6fa..b5d3f170d09 100644 --- a/layout/generic/nsBlockFrame.h +++ b/layout/generic/nsBlockFrame.h @@ -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 diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 282a4ad95bf..4b4b46e7aee 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -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); } diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h index 13d87621738..f65e92fca09 100644 --- a/layout/generic/nsFrame.h +++ b/layout/generic/nsFrame.h @@ -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: diff --git a/layout/reftests/printing/626395-1-ref.html b/layout/reftests/printing/626395-1-ref.html new file mode 100644 index 00000000000..bb44e5ce07a --- /dev/null +++ b/layout/reftests/printing/626395-1-ref.html @@ -0,0 +1,8 @@ + + + +
+
+ Some text + + diff --git a/layout/reftests/printing/626395-1a.html b/layout/reftests/printing/626395-1a.html new file mode 100644 index 00000000000..5c3df2d33ff --- /dev/null +++ b/layout/reftests/printing/626395-1a.html @@ -0,0 +1,9 @@ + + + +
+
+
+ Some text + + diff --git a/layout/reftests/printing/626395-1b.html b/layout/reftests/printing/626395-1b.html new file mode 100644 index 00000000000..9276ff2ce9d --- /dev/null +++ b/layout/reftests/printing/626395-1b.html @@ -0,0 +1,9 @@ + + + +
+
+
+ Some text + + diff --git a/layout/reftests/printing/626395-2-ref.html b/layout/reftests/printing/626395-2-ref.html new file mode 100644 index 00000000000..710d612ff5b --- /dev/null +++ b/layout/reftests/printing/626395-2-ref.html @@ -0,0 +1,7 @@ + + + +
+
+ + diff --git a/layout/reftests/printing/626395-2a.html b/layout/reftests/printing/626395-2a.html new file mode 100644 index 00000000000..ec32da78f0e --- /dev/null +++ b/layout/reftests/printing/626395-2a.html @@ -0,0 +1,9 @@ + + + +
+
+
+ + diff --git a/layout/reftests/printing/626395-2b.html b/layout/reftests/printing/626395-2b.html new file mode 100644 index 00000000000..d1e38c9adc4 --- /dev/null +++ b/layout/reftests/printing/626395-2b.html @@ -0,0 +1,9 @@ + + + +
+
+
+ + diff --git a/layout/reftests/printing/626395-2c.html b/layout/reftests/printing/626395-2c.html new file mode 100644 index 00000000000..b8691b80688 --- /dev/null +++ b/layout/reftests/printing/626395-2c.html @@ -0,0 +1,9 @@ + + + +
+
+
+ + diff --git a/layout/reftests/printing/626395-2d.html b/layout/reftests/printing/626395-2d.html new file mode 100644 index 00000000000..d2082b2ab55 --- /dev/null +++ b/layout/reftests/printing/626395-2d.html @@ -0,0 +1,9 @@ + + + +
+
+
+ + diff --git a/layout/reftests/printing/reftest.list b/layout/reftests/printing/reftest.list index 234ad055db4..60a5d550f3e 100644 --- a/layout/reftests/printing/reftest.list +++ b/layout/reftests/printing/reftest.list @@ -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