From e8833a517e213a86026f7d79df9c017c691aab74 Mon Sep 17 00:00:00 2001 From: "buster%netscape.com" Date: Wed, 14 Jun 2000 23:15:59 +0000 Subject: [PATCH] fixed nsbeta2+ bugs 42138 35772 37657 38157 40283 35964 r=waterson, rods git-svn-id: svn://10.0.0.236/trunk@72241 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/layout/generic/nsBlockFrame.cpp | 35 +++-- .../layout/generic/nsBlockReflowContext.cpp | 131 ++++++++++++------ mozilla/layout/generic/nsBlockReflowContext.h | 6 + mozilla/layout/generic/nsBlockReflowState.cpp | 35 +++-- mozilla/layout/generic/nsBlockReflowState.h | 35 +++-- mozilla/layout/generic/nsLineLayout.cpp | 44 ++++-- mozilla/layout/html/base/src/nsBlockFrame.cpp | 35 +++-- .../html/base/src/nsBlockReflowContext.cpp | 131 ++++++++++++------ .../html/base/src/nsBlockReflowContext.h | 6 + .../html/base/src/nsBlockReflowState.cpp | 35 +++-- .../layout/html/base/src/nsBlockReflowState.h | 35 +++-- mozilla/layout/html/base/src/nsLineLayout.cpp | 44 ++++-- 12 files changed, 362 insertions(+), 210 deletions(-) diff --git a/mozilla/layout/generic/nsBlockFrame.cpp b/mozilla/layout/generic/nsBlockFrame.cpp index 6e6d5ad0e2b..5d777fc69d9 100644 --- a/mozilla/layout/generic/nsBlockFrame.cpp +++ b/mozilla/layout/generic/nsBlockFrame.cpp @@ -165,7 +165,13 @@ InitDebugFlags() #endif -//#define FIX_BUG_38157 needs review before it can be enabled +#define FIX_BUG_38157 +#define FIX_BUG_37657 + +// add in a sanity check for absurdly deep frame trees. See bug 42138 +// can't just use IsFrameTreeTooDeep() because that method has side effects we don't want +static PRInt32 gRenumberListDepthCounter; +#define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic //---------------------------------------------------------------------- @@ -4263,21 +4269,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, const nsMargin& borderPadding = aState.BorderPadding(); nscoord x = aState.mAvailSpaceRect.x + borderPadding.left; - // the available width is the smaller of the widths given by: - // a) the space manager, accounting for floaters impacting this line - // b) the parent frame's computed width - // part 1 of a possible fix for 38157 -#ifdef FIX_BUG_38157 - const nsMargin& margin = aState.Margin(); - nscoord availContentWidth = aState.mReflowState.availableWidth; - if (NS_UNCONSTRAINEDSIZE != availContentWidth) { - availContentWidth -= (borderPadding.left + borderPadding.right) + - (margin.left + margin.right); - } - nscoord availWidth = PR_MIN(aState.mAvailSpaceRect.width, availContentWidth); -#else nscoord availWidth = aState.mAvailSpaceRect.width; -#endif nscoord availHeight; if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { availHeight = NS_UNCONSTRAINEDSIZE; @@ -4446,6 +4438,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Send post-reflow notification aState.mPrevChild = aFrame; + /* XXX + This is where we need to add logic to handle some odd behavior. + For one thing, we should usually place at least one thing next + to a left floater, even when that floater takes up all the width on a line. + see bug 22496 + */ + // Process the child frames reflow status. There are 5 cases: // complete, not-complete, break-before, break-after-complete, // break-after-not-complete. There are two situations: we are a @@ -6999,6 +6998,7 @@ nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame) void nsBlockFrame::RenumberLists(nsIPresContext* aPresContext) { + gRenumberListDepthCounter = 0; // reset the sanity check counter if (!FrameStartsCounterScope(this)) { // If this frame doesn't start a counter scope then we don't need // to renumber child list items. @@ -7090,6 +7090,11 @@ nsBlockFrame::RenumberListsFor(nsIPresContext* aPresContext, nsIFrame* aKid, PRInt32* aOrdinal) { + // add in a sanity check for absurdly deep frame trees. See bug 42138 + gRenumberListDepthCounter++; + if (MAX_DEPTH_FOR_LIST_RENUMBERING < gRenumberListDepthCounter) + return PR_FALSE; + PRBool kidRenumberedABullet = PR_FALSE; // If the frame is a list-item and the frame implements our diff --git a/mozilla/layout/generic/nsBlockReflowContext.cpp b/mozilla/layout/generic/nsBlockReflowContext.cpp index 82c4d9ed8ba..bfcb5193b96 100644 --- a/mozilla/layout/generic/nsBlockReflowContext.cpp +++ b/mozilla/layout/generic/nsBlockReflowContext.cpp @@ -31,6 +31,9 @@ #include "nsHTMLContainerFrame.h" #include "nsBlockFrame.h" #include "nsIDOMHTMLParagraphElement.h" +#include "nsIDOMHTMLTableCellElement.h" +#include "nsIDOMHTMLBodyElement.h" +#include "nsLayoutAtoms.h" #include "nsCOMPtr.h" #ifdef NS_DEBUG @@ -75,6 +78,43 @@ nsBlockReflowContext::IsHTMLParagraph(nsIFrame* aFrame) return result; } + +PRBool +nsBlockReflowContext::IsFirstSignificantChild(const nsIFrame* aParentFrame, const nsIFrame* aChildFrame) const +{ + NS_ASSERTION(aParentFrame && aChildFrame, "bad args"); + if (!aParentFrame || !aChildFrame) return PR_FALSE; + nsIFrame *child; + aParentFrame->FirstChild((nsIPresContext *)(&mPresContext), nsnull, &child); + while (child) + { + if (aChildFrame == child) { + return PR_TRUE; // we found aChildFrame, and we haven't yet encountered a geometrically significant frame + } + nsSize size; + child->GetSize(size); + if (size.width || size.height) { + return PR_FALSE; // we found a geometrically significant frame and it wasn't aChildFrame + } + child->GetNextSibling(&child); + } + return PR_FALSE; //aChildFrame is not in the default child list of aParentFrame +} + +PRBool IsCollapsingBlockParentFrame(const nsIFrame* aFrame) +{ + if (!aFrame) return PR_FALSE; + nsCOMPtr frameType; + aFrame->GetFrameType(getter_AddRefs(frameType)); + if (frameType==nsLayoutAtoms::blockFrame || + frameType==nsLayoutAtoms::areaFrame || + frameType==nsLayoutAtoms::tableCellFrame || + frameType==nsLayoutAtoms::tableCaptionFrame) { + return PR_TRUE; + } + return PR_FALSE; +} + nscoord nsBlockReflowContext::ComputeCollapsedTopMargin(nsIPresContext* aPresContext, nsHTMLReflowState& aRS) @@ -310,7 +350,8 @@ nsBlockReflowContext::ReflowBlock(nsIFrame* aFrame, // XXX subtract out vertical margin? nsSize availSpace(aSpace.width, aSpace.height); nsHTMLReflowState reflowState(mPresContext, mOuterReflowState, aFrame, - availSpace, reason); + availSpace, aSpace.width, aSpace.height); + reflowState.reason = reason; aComputedOffsets = reflowState.mComputedOffsets; reflowState.mLineLayout = nsnull; if (!aIsAdjacentWithTop) { @@ -645,22 +686,38 @@ nsBlockReflowContext::PlaceBlock(PRBool aForceFit, nscoord y = mY; // When deciding whether it's an empty paragraph we also need to take into // account the overflow area - if ((0 == mMetrics.height) && (0 == mMetrics.mOverflowArea.height)) { + if ((0 == mMetrics.height) && (0 == mMetrics.mOverflowArea.height)) + { + PRBool handled = PR_FALSE; if (IsHTMLParagraph(mFrame)) { // Special "feature" for HTML compatability - empty paragraphs // collapse into nothingness, including their margins. Signal // the special nature here by returning -1. - *aBottomMarginResult = -1; + // In general, we turn off this behavior due to re-interpretation of the vague HTML 4 spec. + // See bug 35772. However, we do need this behavior for

inside of table cells, + // floaters, and positioned elements + nsIFrame *parent; + mFrame->GetParent(&parent); + if (parent) + { + if (IsCollapsingBlockParentFrame(mFrame) && + IsFirstSignificantChild(parent, mFrame)) + { + handled = PR_TRUE; + *aBottomMarginResult = -1; #ifdef NOISY_VERTICAL_MARGINS - printf(" "); - nsFrame::ListTag(stdout, mOuterReflowState.frame); - printf(": "); - nsFrame::ListTag(stdout, mFrame); - printf(" -- zapping top & bottom margin; y=%d spaceY=%d\n", - y, mSpace.y); + printf(" "); + nsFrame::ListTag(stdout, mOuterReflowState.frame); + printf(": "); + nsFrame::ListTag(stdout, mFrame); + printf(" -- zapping top & bottom margin; y=%d spaceY=%d\n", + y, mSpace.y); #endif + } + } } - else { + if (!handled) + { // Collapse the bottom margin with the top margin that was already // applied. nscoord newBottomMargin = MaxMargin(collapsedBottomMargin, mTopMargin); @@ -675,19 +732,6 @@ nsBlockReflowContext::PlaceBlock(PRBool aForceFit, #endif } -#if XXX - // For empty blocks we revert the y coordinate back so that the - // top margin is no longer applied. - nsBlockFrame* bf; - nsresult rv = mFrame->QueryInterface(kBlockFrameCID, (void**)&bf); - if (NS_SUCCEEDED(rv)) { - // XXX This isn't good enough. What if the floater was placed - // downward, just below another floater? - nscoord dy = mSpace.y - mY; - bf->MoveInSpaceManager(mPresContext, mOuterReflowState.mSpaceManager, - dy); - } -#endif y = mSpace.y; // Empty blocks do not have anything special done to them and they @@ -707,7 +751,24 @@ nsBlockReflowContext::PlaceBlock(PRBool aForceFit, else { // See if the frame fit. If its the first frame then it always // fits. - if (aForceFit || (y + mMetrics.height <= mSpace.YMost())) { + if (aForceFit || (y + mMetrics.height <= mSpace.YMost())) + { + // if it's a

and it's parent is special, then collapse the

's top margin + if (IsHTMLParagraph(mFrame)) { + // Special "feature" for HTML compatability - a paragraph's + // top margin collapses into nothingness, inside of certain containers. + nsIFrame *parent; + mFrame->GetParent(&parent); + if (parent) + { + if (IsCollapsingBlockParentFrame(mFrame) && + IsFirstSignificantChild(parent, mFrame)) + { + y=0; + } + } + } + // Calculate the actual x-offset and left and right margin nsBlockHorizontalAlign align; @@ -743,28 +804,6 @@ nsBlockReflowContext::PlaceBlock(PRBool aForceFit, // Now place the frame and complete the reflow process nsContainerFrame::FinishReflowChild(mFrame, mPresContext, mMetrics, x, y, 0); -// XXX obsolete, i believe... -#if 0 - // If the block frame ended up moving then we need to slide - // anything inside of it that impacts the space manager - // (otherwise the impacted space in the space manager will be - // out of sync with where the frames really are). - nscoord dx = x - mX; - nscoord dy = y - mY; - if ((0 != dx) || (0 != dy)) { - nsIHTMLReflow* htmlReflow; - nsresult rv; - rv = mFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); - if (NS_SUCCEEDED(rv)) { - // If the child has any floaters that impact the space manager, - // slide them now - htmlReflow->MoveInSpaceManager(mPresContext, - mOuterReflowState.mSpaceManager, - dx, dy); - } - } -#endif - // Adjust the max-element-size in the metrics to take into // account the margins around the block element. Note that we // use the collapsed top and bottom margin values. diff --git a/mozilla/layout/generic/nsBlockReflowContext.h b/mozilla/layout/generic/nsBlockReflowContext.h index 78c5aba1fdf..bfa50ef6458 100644 --- a/mozilla/layout/generic/nsBlockReflowContext.h +++ b/mozilla/layout/generic/nsBlockReflowContext.h @@ -114,6 +114,12 @@ public: static PRBool IsHTMLParagraph(nsIFrame* aFrame); + /** return PR_TRUE if aChildFrame is the first geometrically significant child of aParentFrame + * to be considered significant, a frame must have both width and height != 0 + * if aChildFrame is not in the default child list of aParentFrame, we return PR_FALSE + */ + PRBool nsBlockReflowContext::IsFirstSignificantChild(const nsIFrame* aParentFrame, const nsIFrame* aChildFrame) const; + static nscoord ComputeCollapsedTopMargin(nsIPresContext* aPresContext, nsHTMLReflowState& aRS); diff --git a/mozilla/layout/generic/nsBlockReflowState.cpp b/mozilla/layout/generic/nsBlockReflowState.cpp index 6e6d5ad0e2b..5d777fc69d9 100644 --- a/mozilla/layout/generic/nsBlockReflowState.cpp +++ b/mozilla/layout/generic/nsBlockReflowState.cpp @@ -165,7 +165,13 @@ InitDebugFlags() #endif -//#define FIX_BUG_38157 needs review before it can be enabled +#define FIX_BUG_38157 +#define FIX_BUG_37657 + +// add in a sanity check for absurdly deep frame trees. See bug 42138 +// can't just use IsFrameTreeTooDeep() because that method has side effects we don't want +static PRInt32 gRenumberListDepthCounter; +#define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic //---------------------------------------------------------------------- @@ -4263,21 +4269,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, const nsMargin& borderPadding = aState.BorderPadding(); nscoord x = aState.mAvailSpaceRect.x + borderPadding.left; - // the available width is the smaller of the widths given by: - // a) the space manager, accounting for floaters impacting this line - // b) the parent frame's computed width - // part 1 of a possible fix for 38157 -#ifdef FIX_BUG_38157 - const nsMargin& margin = aState.Margin(); - nscoord availContentWidth = aState.mReflowState.availableWidth; - if (NS_UNCONSTRAINEDSIZE != availContentWidth) { - availContentWidth -= (borderPadding.left + borderPadding.right) + - (margin.left + margin.right); - } - nscoord availWidth = PR_MIN(aState.mAvailSpaceRect.width, availContentWidth); -#else nscoord availWidth = aState.mAvailSpaceRect.width; -#endif nscoord availHeight; if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { availHeight = NS_UNCONSTRAINEDSIZE; @@ -4446,6 +4438,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Send post-reflow notification aState.mPrevChild = aFrame; + /* XXX + This is where we need to add logic to handle some odd behavior. + For one thing, we should usually place at least one thing next + to a left floater, even when that floater takes up all the width on a line. + see bug 22496 + */ + // Process the child frames reflow status. There are 5 cases: // complete, not-complete, break-before, break-after-complete, // break-after-not-complete. There are two situations: we are a @@ -6999,6 +6998,7 @@ nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame) void nsBlockFrame::RenumberLists(nsIPresContext* aPresContext) { + gRenumberListDepthCounter = 0; // reset the sanity check counter if (!FrameStartsCounterScope(this)) { // If this frame doesn't start a counter scope then we don't need // to renumber child list items. @@ -7090,6 +7090,11 @@ nsBlockFrame::RenumberListsFor(nsIPresContext* aPresContext, nsIFrame* aKid, PRInt32* aOrdinal) { + // add in a sanity check for absurdly deep frame trees. See bug 42138 + gRenumberListDepthCounter++; + if (MAX_DEPTH_FOR_LIST_RENUMBERING < gRenumberListDepthCounter) + return PR_FALSE; + PRBool kidRenumberedABullet = PR_FALSE; // If the frame is a list-item and the frame implements our diff --git a/mozilla/layout/generic/nsBlockReflowState.h b/mozilla/layout/generic/nsBlockReflowState.h index 6e6d5ad0e2b..5d777fc69d9 100644 --- a/mozilla/layout/generic/nsBlockReflowState.h +++ b/mozilla/layout/generic/nsBlockReflowState.h @@ -165,7 +165,13 @@ InitDebugFlags() #endif -//#define FIX_BUG_38157 needs review before it can be enabled +#define FIX_BUG_38157 +#define FIX_BUG_37657 + +// add in a sanity check for absurdly deep frame trees. See bug 42138 +// can't just use IsFrameTreeTooDeep() because that method has side effects we don't want +static PRInt32 gRenumberListDepthCounter; +#define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic //---------------------------------------------------------------------- @@ -4263,21 +4269,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, const nsMargin& borderPadding = aState.BorderPadding(); nscoord x = aState.mAvailSpaceRect.x + borderPadding.left; - // the available width is the smaller of the widths given by: - // a) the space manager, accounting for floaters impacting this line - // b) the parent frame's computed width - // part 1 of a possible fix for 38157 -#ifdef FIX_BUG_38157 - const nsMargin& margin = aState.Margin(); - nscoord availContentWidth = aState.mReflowState.availableWidth; - if (NS_UNCONSTRAINEDSIZE != availContentWidth) { - availContentWidth -= (borderPadding.left + borderPadding.right) + - (margin.left + margin.right); - } - nscoord availWidth = PR_MIN(aState.mAvailSpaceRect.width, availContentWidth); -#else nscoord availWidth = aState.mAvailSpaceRect.width; -#endif nscoord availHeight; if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { availHeight = NS_UNCONSTRAINEDSIZE; @@ -4446,6 +4438,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Send post-reflow notification aState.mPrevChild = aFrame; + /* XXX + This is where we need to add logic to handle some odd behavior. + For one thing, we should usually place at least one thing next + to a left floater, even when that floater takes up all the width on a line. + see bug 22496 + */ + // Process the child frames reflow status. There are 5 cases: // complete, not-complete, break-before, break-after-complete, // break-after-not-complete. There are two situations: we are a @@ -6999,6 +6998,7 @@ nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame) void nsBlockFrame::RenumberLists(nsIPresContext* aPresContext) { + gRenumberListDepthCounter = 0; // reset the sanity check counter if (!FrameStartsCounterScope(this)) { // If this frame doesn't start a counter scope then we don't need // to renumber child list items. @@ -7090,6 +7090,11 @@ nsBlockFrame::RenumberListsFor(nsIPresContext* aPresContext, nsIFrame* aKid, PRInt32* aOrdinal) { + // add in a sanity check for absurdly deep frame trees. See bug 42138 + gRenumberListDepthCounter++; + if (MAX_DEPTH_FOR_LIST_RENUMBERING < gRenumberListDepthCounter) + return PR_FALSE; + PRBool kidRenumberedABullet = PR_FALSE; // If the frame is a list-item and the frame implements our diff --git a/mozilla/layout/generic/nsLineLayout.cpp b/mozilla/layout/generic/nsLineLayout.cpp index a4986c04c7b..1568e9ea1a7 100644 --- a/mozilla/layout/generic/nsLineLayout.cpp +++ b/mozilla/layout/generic/nsLineLayout.cpp @@ -558,19 +558,30 @@ nsLineLayout::EndSpan(nsIFrame* aFrame, width = psd->mX - psd->mLeftEdge; PerFrameData* pfd = psd->mFirstFrame; while (nsnull != pfd) { - if (pfd->mBounds.height > maxHeight) maxHeight = pfd->mBounds.height; + /* there's one oddball case we need to guard against + * if we're reflowed with NS_UNCONSTRAINEDSIZE + * then the last frame will not contribute to the max element size height + * if it is a text frame that only contains whitespace + */ + if (NS_UNCONSTRAINEDSIZE != psd->mRightEdge || // it's not an unconstrained reflow + pfd->mNext || // or it's not the last frame in the span + !pfd->GetFlag(PFD_ISTEXTFRAME) || // or it's not a text frame + pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME) // or it contains something other than whitespace + ) { + if (pfd->mBounds.height > maxHeight) maxHeight = pfd->mBounds.height; - // Compute max-element-size if necessary - if (aMaxElementSize) { - nscoord mw = pfd->mMaxElementSize.width + - pfd->mMargin.left + pfd->mMargin.right; - if (maxElementWidth < mw) { - maxElementWidth = mw; - } - nscoord mh = pfd->mMaxElementSize.height + - pfd->mMargin.top + pfd->mMargin.bottom; - if (maxElementHeight < mh) { - maxElementHeight = mh; + // Compute max-element-size if necessary + if (aMaxElementSize) { + nscoord mw = pfd->mMaxElementSize.width + + pfd->mMargin.left + pfd->mMargin.right; + if (maxElementWidth < mw) { + maxElementWidth = mw; + } + nscoord mh = pfd->mMaxElementSize.height + + pfd->mMargin.top + pfd->mMargin.bottom; + if (maxElementHeight < mh) { + maxElementHeight = mh; + } } } pfd = pfd->mNext; @@ -1627,8 +1638,13 @@ nsLineLayout::VerticalAlignFrames(nsLineBox* aLineBox, if (mComputeMaxElementSize) { nscoord mw = pfd->mMaxElementSize.width + pfd->mMargin.left + pfd->mMargin.right; - if (maxElementWidth < mw) { - maxElementWidth = mw; + if (psd->mNoWrap) { + maxElementWidth += mw; + } + else { + if (maxElementWidth < mw) { + maxElementWidth = mw; + } } nscoord mh = pfd->mMaxElementSize.height + pfd->mMargin.top + pfd->mMargin.bottom; diff --git a/mozilla/layout/html/base/src/nsBlockFrame.cpp b/mozilla/layout/html/base/src/nsBlockFrame.cpp index 6e6d5ad0e2b..5d777fc69d9 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.cpp +++ b/mozilla/layout/html/base/src/nsBlockFrame.cpp @@ -165,7 +165,13 @@ InitDebugFlags() #endif -//#define FIX_BUG_38157 needs review before it can be enabled +#define FIX_BUG_38157 +#define FIX_BUG_37657 + +// add in a sanity check for absurdly deep frame trees. See bug 42138 +// can't just use IsFrameTreeTooDeep() because that method has side effects we don't want +static PRInt32 gRenumberListDepthCounter; +#define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic //---------------------------------------------------------------------- @@ -4263,21 +4269,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, const nsMargin& borderPadding = aState.BorderPadding(); nscoord x = aState.mAvailSpaceRect.x + borderPadding.left; - // the available width is the smaller of the widths given by: - // a) the space manager, accounting for floaters impacting this line - // b) the parent frame's computed width - // part 1 of a possible fix for 38157 -#ifdef FIX_BUG_38157 - const nsMargin& margin = aState.Margin(); - nscoord availContentWidth = aState.mReflowState.availableWidth; - if (NS_UNCONSTRAINEDSIZE != availContentWidth) { - availContentWidth -= (borderPadding.left + borderPadding.right) + - (margin.left + margin.right); - } - nscoord availWidth = PR_MIN(aState.mAvailSpaceRect.width, availContentWidth); -#else nscoord availWidth = aState.mAvailSpaceRect.width; -#endif nscoord availHeight; if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { availHeight = NS_UNCONSTRAINEDSIZE; @@ -4446,6 +4438,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Send post-reflow notification aState.mPrevChild = aFrame; + /* XXX + This is where we need to add logic to handle some odd behavior. + For one thing, we should usually place at least one thing next + to a left floater, even when that floater takes up all the width on a line. + see bug 22496 + */ + // Process the child frames reflow status. There are 5 cases: // complete, not-complete, break-before, break-after-complete, // break-after-not-complete. There are two situations: we are a @@ -6999,6 +6998,7 @@ nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame) void nsBlockFrame::RenumberLists(nsIPresContext* aPresContext) { + gRenumberListDepthCounter = 0; // reset the sanity check counter if (!FrameStartsCounterScope(this)) { // If this frame doesn't start a counter scope then we don't need // to renumber child list items. @@ -7090,6 +7090,11 @@ nsBlockFrame::RenumberListsFor(nsIPresContext* aPresContext, nsIFrame* aKid, PRInt32* aOrdinal) { + // add in a sanity check for absurdly deep frame trees. See bug 42138 + gRenumberListDepthCounter++; + if (MAX_DEPTH_FOR_LIST_RENUMBERING < gRenumberListDepthCounter) + return PR_FALSE; + PRBool kidRenumberedABullet = PR_FALSE; // If the frame is a list-item and the frame implements our diff --git a/mozilla/layout/html/base/src/nsBlockReflowContext.cpp b/mozilla/layout/html/base/src/nsBlockReflowContext.cpp index 82c4d9ed8ba..bfcb5193b96 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowContext.cpp +++ b/mozilla/layout/html/base/src/nsBlockReflowContext.cpp @@ -31,6 +31,9 @@ #include "nsHTMLContainerFrame.h" #include "nsBlockFrame.h" #include "nsIDOMHTMLParagraphElement.h" +#include "nsIDOMHTMLTableCellElement.h" +#include "nsIDOMHTMLBodyElement.h" +#include "nsLayoutAtoms.h" #include "nsCOMPtr.h" #ifdef NS_DEBUG @@ -75,6 +78,43 @@ nsBlockReflowContext::IsHTMLParagraph(nsIFrame* aFrame) return result; } + +PRBool +nsBlockReflowContext::IsFirstSignificantChild(const nsIFrame* aParentFrame, const nsIFrame* aChildFrame) const +{ + NS_ASSERTION(aParentFrame && aChildFrame, "bad args"); + if (!aParentFrame || !aChildFrame) return PR_FALSE; + nsIFrame *child; + aParentFrame->FirstChild((nsIPresContext *)(&mPresContext), nsnull, &child); + while (child) + { + if (aChildFrame == child) { + return PR_TRUE; // we found aChildFrame, and we haven't yet encountered a geometrically significant frame + } + nsSize size; + child->GetSize(size); + if (size.width || size.height) { + return PR_FALSE; // we found a geometrically significant frame and it wasn't aChildFrame + } + child->GetNextSibling(&child); + } + return PR_FALSE; //aChildFrame is not in the default child list of aParentFrame +} + +PRBool IsCollapsingBlockParentFrame(const nsIFrame* aFrame) +{ + if (!aFrame) return PR_FALSE; + nsCOMPtr frameType; + aFrame->GetFrameType(getter_AddRefs(frameType)); + if (frameType==nsLayoutAtoms::blockFrame || + frameType==nsLayoutAtoms::areaFrame || + frameType==nsLayoutAtoms::tableCellFrame || + frameType==nsLayoutAtoms::tableCaptionFrame) { + return PR_TRUE; + } + return PR_FALSE; +} + nscoord nsBlockReflowContext::ComputeCollapsedTopMargin(nsIPresContext* aPresContext, nsHTMLReflowState& aRS) @@ -310,7 +350,8 @@ nsBlockReflowContext::ReflowBlock(nsIFrame* aFrame, // XXX subtract out vertical margin? nsSize availSpace(aSpace.width, aSpace.height); nsHTMLReflowState reflowState(mPresContext, mOuterReflowState, aFrame, - availSpace, reason); + availSpace, aSpace.width, aSpace.height); + reflowState.reason = reason; aComputedOffsets = reflowState.mComputedOffsets; reflowState.mLineLayout = nsnull; if (!aIsAdjacentWithTop) { @@ -645,22 +686,38 @@ nsBlockReflowContext::PlaceBlock(PRBool aForceFit, nscoord y = mY; // When deciding whether it's an empty paragraph we also need to take into // account the overflow area - if ((0 == mMetrics.height) && (0 == mMetrics.mOverflowArea.height)) { + if ((0 == mMetrics.height) && (0 == mMetrics.mOverflowArea.height)) + { + PRBool handled = PR_FALSE; if (IsHTMLParagraph(mFrame)) { // Special "feature" for HTML compatability - empty paragraphs // collapse into nothingness, including their margins. Signal // the special nature here by returning -1. - *aBottomMarginResult = -1; + // In general, we turn off this behavior due to re-interpretation of the vague HTML 4 spec. + // See bug 35772. However, we do need this behavior for

inside of table cells, + // floaters, and positioned elements + nsIFrame *parent; + mFrame->GetParent(&parent); + if (parent) + { + if (IsCollapsingBlockParentFrame(mFrame) && + IsFirstSignificantChild(parent, mFrame)) + { + handled = PR_TRUE; + *aBottomMarginResult = -1; #ifdef NOISY_VERTICAL_MARGINS - printf(" "); - nsFrame::ListTag(stdout, mOuterReflowState.frame); - printf(": "); - nsFrame::ListTag(stdout, mFrame); - printf(" -- zapping top & bottom margin; y=%d spaceY=%d\n", - y, mSpace.y); + printf(" "); + nsFrame::ListTag(stdout, mOuterReflowState.frame); + printf(": "); + nsFrame::ListTag(stdout, mFrame); + printf(" -- zapping top & bottom margin; y=%d spaceY=%d\n", + y, mSpace.y); #endif + } + } } - else { + if (!handled) + { // Collapse the bottom margin with the top margin that was already // applied. nscoord newBottomMargin = MaxMargin(collapsedBottomMargin, mTopMargin); @@ -675,19 +732,6 @@ nsBlockReflowContext::PlaceBlock(PRBool aForceFit, #endif } -#if XXX - // For empty blocks we revert the y coordinate back so that the - // top margin is no longer applied. - nsBlockFrame* bf; - nsresult rv = mFrame->QueryInterface(kBlockFrameCID, (void**)&bf); - if (NS_SUCCEEDED(rv)) { - // XXX This isn't good enough. What if the floater was placed - // downward, just below another floater? - nscoord dy = mSpace.y - mY; - bf->MoveInSpaceManager(mPresContext, mOuterReflowState.mSpaceManager, - dy); - } -#endif y = mSpace.y; // Empty blocks do not have anything special done to them and they @@ -707,7 +751,24 @@ nsBlockReflowContext::PlaceBlock(PRBool aForceFit, else { // See if the frame fit. If its the first frame then it always // fits. - if (aForceFit || (y + mMetrics.height <= mSpace.YMost())) { + if (aForceFit || (y + mMetrics.height <= mSpace.YMost())) + { + // if it's a

and it's parent is special, then collapse the

's top margin + if (IsHTMLParagraph(mFrame)) { + // Special "feature" for HTML compatability - a paragraph's + // top margin collapses into nothingness, inside of certain containers. + nsIFrame *parent; + mFrame->GetParent(&parent); + if (parent) + { + if (IsCollapsingBlockParentFrame(mFrame) && + IsFirstSignificantChild(parent, mFrame)) + { + y=0; + } + } + } + // Calculate the actual x-offset and left and right margin nsBlockHorizontalAlign align; @@ -743,28 +804,6 @@ nsBlockReflowContext::PlaceBlock(PRBool aForceFit, // Now place the frame and complete the reflow process nsContainerFrame::FinishReflowChild(mFrame, mPresContext, mMetrics, x, y, 0); -// XXX obsolete, i believe... -#if 0 - // If the block frame ended up moving then we need to slide - // anything inside of it that impacts the space manager - // (otherwise the impacted space in the space manager will be - // out of sync with where the frames really are). - nscoord dx = x - mX; - nscoord dy = y - mY; - if ((0 != dx) || (0 != dy)) { - nsIHTMLReflow* htmlReflow; - nsresult rv; - rv = mFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); - if (NS_SUCCEEDED(rv)) { - // If the child has any floaters that impact the space manager, - // slide them now - htmlReflow->MoveInSpaceManager(mPresContext, - mOuterReflowState.mSpaceManager, - dx, dy); - } - } -#endif - // Adjust the max-element-size in the metrics to take into // account the margins around the block element. Note that we // use the collapsed top and bottom margin values. diff --git a/mozilla/layout/html/base/src/nsBlockReflowContext.h b/mozilla/layout/html/base/src/nsBlockReflowContext.h index 78c5aba1fdf..bfa50ef6458 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowContext.h +++ b/mozilla/layout/html/base/src/nsBlockReflowContext.h @@ -114,6 +114,12 @@ public: static PRBool IsHTMLParagraph(nsIFrame* aFrame); + /** return PR_TRUE if aChildFrame is the first geometrically significant child of aParentFrame + * to be considered significant, a frame must have both width and height != 0 + * if aChildFrame is not in the default child list of aParentFrame, we return PR_FALSE + */ + PRBool nsBlockReflowContext::IsFirstSignificantChild(const nsIFrame* aParentFrame, const nsIFrame* aChildFrame) const; + static nscoord ComputeCollapsedTopMargin(nsIPresContext* aPresContext, nsHTMLReflowState& aRS); diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.cpp b/mozilla/layout/html/base/src/nsBlockReflowState.cpp index 6e6d5ad0e2b..5d777fc69d9 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.cpp +++ b/mozilla/layout/html/base/src/nsBlockReflowState.cpp @@ -165,7 +165,13 @@ InitDebugFlags() #endif -//#define FIX_BUG_38157 needs review before it can be enabled +#define FIX_BUG_38157 +#define FIX_BUG_37657 + +// add in a sanity check for absurdly deep frame trees. See bug 42138 +// can't just use IsFrameTreeTooDeep() because that method has side effects we don't want +static PRInt32 gRenumberListDepthCounter; +#define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic //---------------------------------------------------------------------- @@ -4263,21 +4269,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, const nsMargin& borderPadding = aState.BorderPadding(); nscoord x = aState.mAvailSpaceRect.x + borderPadding.left; - // the available width is the smaller of the widths given by: - // a) the space manager, accounting for floaters impacting this line - // b) the parent frame's computed width - // part 1 of a possible fix for 38157 -#ifdef FIX_BUG_38157 - const nsMargin& margin = aState.Margin(); - nscoord availContentWidth = aState.mReflowState.availableWidth; - if (NS_UNCONSTRAINEDSIZE != availContentWidth) { - availContentWidth -= (borderPadding.left + borderPadding.right) + - (margin.left + margin.right); - } - nscoord availWidth = PR_MIN(aState.mAvailSpaceRect.width, availContentWidth); -#else nscoord availWidth = aState.mAvailSpaceRect.width; -#endif nscoord availHeight; if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { availHeight = NS_UNCONSTRAINEDSIZE; @@ -4446,6 +4438,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Send post-reflow notification aState.mPrevChild = aFrame; + /* XXX + This is where we need to add logic to handle some odd behavior. + For one thing, we should usually place at least one thing next + to a left floater, even when that floater takes up all the width on a line. + see bug 22496 + */ + // Process the child frames reflow status. There are 5 cases: // complete, not-complete, break-before, break-after-complete, // break-after-not-complete. There are two situations: we are a @@ -6999,6 +6998,7 @@ nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame) void nsBlockFrame::RenumberLists(nsIPresContext* aPresContext) { + gRenumberListDepthCounter = 0; // reset the sanity check counter if (!FrameStartsCounterScope(this)) { // If this frame doesn't start a counter scope then we don't need // to renumber child list items. @@ -7090,6 +7090,11 @@ nsBlockFrame::RenumberListsFor(nsIPresContext* aPresContext, nsIFrame* aKid, PRInt32* aOrdinal) { + // add in a sanity check for absurdly deep frame trees. See bug 42138 + gRenumberListDepthCounter++; + if (MAX_DEPTH_FOR_LIST_RENUMBERING < gRenumberListDepthCounter) + return PR_FALSE; + PRBool kidRenumberedABullet = PR_FALSE; // If the frame is a list-item and the frame implements our diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.h b/mozilla/layout/html/base/src/nsBlockReflowState.h index 6e6d5ad0e2b..5d777fc69d9 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.h +++ b/mozilla/layout/html/base/src/nsBlockReflowState.h @@ -165,7 +165,13 @@ InitDebugFlags() #endif -//#define FIX_BUG_38157 needs review before it can be enabled +#define FIX_BUG_38157 +#define FIX_BUG_37657 + +// add in a sanity check for absurdly deep frame trees. See bug 42138 +// can't just use IsFrameTreeTooDeep() because that method has side effects we don't want +static PRInt32 gRenumberListDepthCounter; +#define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic //---------------------------------------------------------------------- @@ -4263,21 +4269,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, const nsMargin& borderPadding = aState.BorderPadding(); nscoord x = aState.mAvailSpaceRect.x + borderPadding.left; - // the available width is the smaller of the widths given by: - // a) the space manager, accounting for floaters impacting this line - // b) the parent frame's computed width - // part 1 of a possible fix for 38157 -#ifdef FIX_BUG_38157 - const nsMargin& margin = aState.Margin(); - nscoord availContentWidth = aState.mReflowState.availableWidth; - if (NS_UNCONSTRAINEDSIZE != availContentWidth) { - availContentWidth -= (borderPadding.left + borderPadding.right) + - (margin.left + margin.right); - } - nscoord availWidth = PR_MIN(aState.mAvailSpaceRect.width, availContentWidth); -#else nscoord availWidth = aState.mAvailSpaceRect.width; -#endif nscoord availHeight; if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { availHeight = NS_UNCONSTRAINEDSIZE; @@ -4446,6 +4438,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Send post-reflow notification aState.mPrevChild = aFrame; + /* XXX + This is where we need to add logic to handle some odd behavior. + For one thing, we should usually place at least one thing next + to a left floater, even when that floater takes up all the width on a line. + see bug 22496 + */ + // Process the child frames reflow status. There are 5 cases: // complete, not-complete, break-before, break-after-complete, // break-after-not-complete. There are two situations: we are a @@ -6999,6 +6998,7 @@ nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame) void nsBlockFrame::RenumberLists(nsIPresContext* aPresContext) { + gRenumberListDepthCounter = 0; // reset the sanity check counter if (!FrameStartsCounterScope(this)) { // If this frame doesn't start a counter scope then we don't need // to renumber child list items. @@ -7090,6 +7090,11 @@ nsBlockFrame::RenumberListsFor(nsIPresContext* aPresContext, nsIFrame* aKid, PRInt32* aOrdinal) { + // add in a sanity check for absurdly deep frame trees. See bug 42138 + gRenumberListDepthCounter++; + if (MAX_DEPTH_FOR_LIST_RENUMBERING < gRenumberListDepthCounter) + return PR_FALSE; + PRBool kidRenumberedABullet = PR_FALSE; // If the frame is a list-item and the frame implements our diff --git a/mozilla/layout/html/base/src/nsLineLayout.cpp b/mozilla/layout/html/base/src/nsLineLayout.cpp index a4986c04c7b..1568e9ea1a7 100644 --- a/mozilla/layout/html/base/src/nsLineLayout.cpp +++ b/mozilla/layout/html/base/src/nsLineLayout.cpp @@ -558,19 +558,30 @@ nsLineLayout::EndSpan(nsIFrame* aFrame, width = psd->mX - psd->mLeftEdge; PerFrameData* pfd = psd->mFirstFrame; while (nsnull != pfd) { - if (pfd->mBounds.height > maxHeight) maxHeight = pfd->mBounds.height; + /* there's one oddball case we need to guard against + * if we're reflowed with NS_UNCONSTRAINEDSIZE + * then the last frame will not contribute to the max element size height + * if it is a text frame that only contains whitespace + */ + if (NS_UNCONSTRAINEDSIZE != psd->mRightEdge || // it's not an unconstrained reflow + pfd->mNext || // or it's not the last frame in the span + !pfd->GetFlag(PFD_ISTEXTFRAME) || // or it's not a text frame + pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME) // or it contains something other than whitespace + ) { + if (pfd->mBounds.height > maxHeight) maxHeight = pfd->mBounds.height; - // Compute max-element-size if necessary - if (aMaxElementSize) { - nscoord mw = pfd->mMaxElementSize.width + - pfd->mMargin.left + pfd->mMargin.right; - if (maxElementWidth < mw) { - maxElementWidth = mw; - } - nscoord mh = pfd->mMaxElementSize.height + - pfd->mMargin.top + pfd->mMargin.bottom; - if (maxElementHeight < mh) { - maxElementHeight = mh; + // Compute max-element-size if necessary + if (aMaxElementSize) { + nscoord mw = pfd->mMaxElementSize.width + + pfd->mMargin.left + pfd->mMargin.right; + if (maxElementWidth < mw) { + maxElementWidth = mw; + } + nscoord mh = pfd->mMaxElementSize.height + + pfd->mMargin.top + pfd->mMargin.bottom; + if (maxElementHeight < mh) { + maxElementHeight = mh; + } } } pfd = pfd->mNext; @@ -1627,8 +1638,13 @@ nsLineLayout::VerticalAlignFrames(nsLineBox* aLineBox, if (mComputeMaxElementSize) { nscoord mw = pfd->mMaxElementSize.width + pfd->mMargin.left + pfd->mMargin.right; - if (maxElementWidth < mw) { - maxElementWidth = mw; + if (psd->mNoWrap) { + maxElementWidth += mw; + } + else { + if (maxElementWidth < mw) { + maxElementWidth = mw; + } } nscoord mh = pfd->mMaxElementSize.height + pfd->mMargin.top + pfd->mMargin.bottom;