From 7f490d3aac983de60048fc2b5f1e088004cd68e7 Mon Sep 17 00:00:00 2001 From: "roc+%cs.cmu.edu" Date: Sun, 14 Nov 2004 20:22:58 +0000 Subject: [PATCH] Bug 261064. Compute static absolute positions more accurately. Also, ensure that static absolutely positioned frames are moved when their placeholders move. Also, ensure that absolutely positioned frames positioned relative to the left or bottom edge of their container move when the container size changes. r+sr=dbaron (rubberstamp) git-svn-id: svn://10.0.0.236/trunk@165349 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/layout/base/public/nsIFrame.h | 6 + .../generic/nsAbsoluteContainingBlock.cpp | 176 +++++++++++++++--- .../generic/nsAbsoluteContainingBlock.h | 31 +-- mozilla/layout/generic/nsBlockFrame.cpp | 124 +++++++----- mozilla/layout/generic/nsBlockFrame.h | 1 + mozilla/layout/generic/nsBlockReflowState.cpp | 2 +- mozilla/layout/generic/nsFrame.cpp | 6 + mozilla/layout/generic/nsFrame.h | 1 + mozilla/layout/generic/nsHTMLReflowState.cpp | 41 +++- mozilla/layout/generic/nsIFrame.h | 6 + mozilla/layout/generic/nsInlineFrame.cpp | 59 +++--- mozilla/layout/generic/nsInlineFrame.h | 1 + mozilla/layout/generic/nsPlaceholderFrame.cpp | 6 - mozilla/layout/generic/nsPlaceholderFrame.h | 3 +- mozilla/layout/generic/nsTextFrame.cpp | 1 + mozilla/layout/generic/nsViewportFrame.cpp | 20 +- .../base/src/nsAbsoluteContainingBlock.cpp | 176 +++++++++++++++--- .../html/base/src/nsAbsoluteContainingBlock.h | 31 +-- mozilla/layout/html/base/src/nsBlockFrame.cpp | 124 +++++++----- mozilla/layout/html/base/src/nsBlockFrame.h | 1 + .../html/base/src/nsBlockReflowState.cpp | 2 +- mozilla/layout/html/base/src/nsFrame.cpp | 6 + mozilla/layout/html/base/src/nsFrame.h | 1 + .../html/base/src/nsHTMLReflowState.cpp | 41 +++- .../layout/html/base/src/nsInlineFrame.cpp | 59 +++--- mozilla/layout/html/base/src/nsInlineFrame.h | 1 + .../html/base/src/nsPlaceholderFrame.cpp | 6 - .../layout/html/base/src/nsPlaceholderFrame.h | 3 +- mozilla/layout/html/base/src/nsTextFrame.cpp | 1 + .../layout/html/base/src/nsViewportFrame.cpp | 20 +- 30 files changed, 660 insertions(+), 296 deletions(-) diff --git a/mozilla/layout/base/public/nsIFrame.h b/mozilla/layout/base/public/nsIFrame.h index a353cc15728..426a2f650a1 100644 --- a/mozilla/layout/base/public/nsIFrame.h +++ b/mozilla/layout/base/public/nsIFrame.h @@ -1219,6 +1219,12 @@ public: */ virtual PRBool IsEmpty() = 0; + /** + * Determine whether the frame is logically empty, assuming that all + * its children are empty. + */ + virtual PRBool IsFrameEmpty() = 0; + /** * IsGeneratedContentFrame returns whether a frame corresponds to * generated content diff --git a/mozilla/layout/generic/nsAbsoluteContainingBlock.cpp b/mozilla/layout/generic/nsAbsoluteContainingBlock.cpp index 921f8b98375..440523bb901 100644 --- a/mozilla/layout/generic/nsAbsoluteContainingBlock.cpp +++ b/mozilla/layout/generic/nsAbsoluteContainingBlock.cpp @@ -251,19 +251,160 @@ nsAbsoluteContainingBlock::CalculateChildBounds(nsPresContext* aPresContext, } } -nsresult +PRBool +nsAbsoluteContainingBlock::ReflowingAbsolutesOnly(nsIFrame* aDelegatingFrame, + const nsHTMLReflowState& aReflowState) +{ + // See if the reflow command is targeted at us. + nsReflowPath *path = aReflowState.path; + nsHTMLReflowCommand *command = path->mReflowCommand; + + if (command) { + // It's targeted at us. See if it's for the positioned child frames + nsCOMPtr listName; + command->GetChildListName(*getter_AddRefs(listName)); + + if (GetChildListName() != listName) { + // A reflow command is targeted directly at this block. + // The block will have to do a proper reflow. + return PR_FALSE; + } + } + + nsReflowPath::iterator iter = path->FirstChild(); + nsReflowPath::iterator end = path->EndChildren(); + + if (iter != end && mAbsoluteFrames.NotEmpty()) { + for ( ; iter != end; ++iter) { + // See if it's one of our absolutely positioned child frames + if (!mAbsoluteFrames.ContainsFrame(*iter)) { + // At least one of the frames along the reflow path wasn't + // absolutely positioned, so we'll need to deal with it in + // normal block reflow. + return PR_FALSE; + } + } + } + + return PR_TRUE; +} + +static PRBool IsFixedBorderSize(nsStyleUnit aUnit) { + return aUnit == eStyleUnit_Coord || aUnit == eStyleUnit_Enumerated + || aUnit == eStyleUnit_Null; +} +static PRBool IsFixedPaddingSize(nsStyleUnit aUnit) { + return aUnit == eStyleUnit_Coord || aUnit == eStyleUnit_Null; +} +static PRBool IsFixedMarginSize(nsStyleUnit aUnit) { + return aUnit == eStyleUnit_Coord || aUnit == eStyleUnit_Null; +} +static PRBool IsFixedMaxSize(nsStyleUnit aUnit) { + return aUnit == eStyleUnit_Null || aUnit == eStyleUnit_Coord; +} + +// XXX this logic should eventually be combined into the Reflow methods +// so we reflow only those frames that need it +PRBool +nsAbsoluteContainingBlock::FramesDependOnContainer(PRBool aWidthChanged, + PRBool aHeightChanged) +{ + for (nsIFrame* f = mAbsoluteFrames.FirstChild(); f; f = f->GetNextSibling()) { + const nsStylePosition* pos = f->GetStylePosition(); + // See if f's position might have changed because it depends on a + // placeholder's position + if (pos->mOffset.GetTopUnit() == eStyleUnit_Auto || + (f->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL + ? pos->mOffset.GetRightUnit() : pos->mOffset.GetLeftUnit()) == eStyleUnit_Auto) { + // Note that in CSS2.1, 'bottom:auto' can never actually induce a dependence + // on the position of the placeholder + return PR_TRUE; + } + if (!aWidthChanged && !aHeightChanged) { + // skip getting style data + continue; + } + const nsStyleBorder* border = f->GetStyleBorder(); + const nsStylePadding* padding = f->GetStylePadding(); + const nsStyleMargin* margin = f->GetStyleMargin(); + if (aWidthChanged) { + // See if f's width might have changed. + // If border-left, border-right, padding-left, padding-right, + // width, min-width, and max-width are all lengths, 'none', or enumerated, + // then our frame width does not depend on the parent width. + if (pos->mWidth.GetUnit() != eStyleUnit_Coord || + pos->mMinWidth.GetUnit() != eStyleUnit_Coord || + !IsFixedMaxSize(pos->mMaxWidth.GetUnit()) || + !IsFixedBorderSize(border->mBorder.GetLeftUnit()) || + !IsFixedBorderSize(border->mBorder.GetRightUnit()) || + !IsFixedPaddingSize(padding->mPadding.GetLeftUnit()) || + !IsFixedPaddingSize(padding->mPadding.GetRightUnit())) { + return PR_TRUE; + } + + // See if f's position might have changed. If we're RTL then the + // rules are slightly different. We'll assume percentage or auto + // margins will always induce a dependency on the size + if (!IsFixedMarginSize(margin->mMargin.GetLeftUnit()) || + !IsFixedMarginSize(margin->mMargin.GetRightUnit())) { + return PR_TRUE; + } + if (f->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { + // Note that even if 'left' is a length, our position can + // still depend on the containing block width, because if + // 'right' is also a length we will discard 'left' and be + // positioned relative to the containing block right edge. + // 'left' length and 'right' auto is the only combination + // we can be sure of. + if (pos->mOffset.GetLeftUnit() != eStyleUnit_Coord || + pos->mOffset.GetRightUnit() != eStyleUnit_Auto) { + return PR_TRUE; + } + } else { + if (pos->mOffset.GetLeftUnit() != eStyleUnit_Coord) { + return PR_TRUE; + } + } + } + if (aHeightChanged) { + // See if f's height might have changed. + // If border-top, border-bottom, padding-top, padding-bottom, + // min-height, and max-height are all lengths or 'none', + // and height is a length or height and bottom are auto and top is not auto, + // then our frame height does not depend on the parent height. + if (!(pos->mHeight.GetUnit() == eStyleUnit_Coord || + (pos->mHeight.GetUnit() == eStyleUnit_Auto && + pos->mOffset.GetBottomUnit() == eStyleUnit_Auto && + pos->mOffset.GetTopUnit() != eStyleUnit_Auto)) || + pos->mMinHeight.GetUnit() != eStyleUnit_Coord || + !IsFixedMaxSize(pos->mMaxHeight.GetUnit()) || + !IsFixedBorderSize(border->mBorder.GetTopUnit()) || + !IsFixedBorderSize(border->mBorder.GetBottomUnit()) || + !IsFixedPaddingSize(padding->mPadding.GetTopUnit()) || + !IsFixedPaddingSize(padding->mPadding.GetBottomUnit())) { + return PR_TRUE; + } + + // See if f's position might have changed. + if (!IsFixedMarginSize(margin->mMargin.GetTopUnit()) || + !IsFixedMarginSize(margin->mMargin.GetBottomUnit())) { + return PR_TRUE; + } + if (pos->mOffset.GetTopUnit() != eStyleUnit_Coord) { + return PR_TRUE; + } + } + } + return PR_FALSE; +} + +void nsAbsoluteContainingBlock::IncrementalReflow(nsIFrame* aDelegatingFrame, - nsPresContext* aPresContext, + nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, - PRBool& aWasHandled) + nscoord aContainingBlockHeight) { - // Initialize the OUT parameters - // Assume we handled the incremental reflow - // until we find something that requires a full block reflow - aWasHandled = PR_TRUE; - // See if the reflow command is targeted at us. nsReflowPath *path = aReflowState.path; nsHTMLReflowCommand *command = path->mReflowCommand; @@ -300,11 +441,6 @@ nsAbsoluteContainingBlock::IncrementalReflow(nsIFrame* aDelegatin reason, status); } } - } else { - // A reflow command is targeted directly at this block. - // The block will have to do a proper reflow. - // But keep going to make sure that we process any dirty absolute frames, below. - aWasHandled = PR_FALSE; } } @@ -326,20 +462,10 @@ nsAbsoluteContainingBlock::IncrementalReflow(nsIFrame* aDelegatin // repainting, and because it has a view if it changes size // the view manager will damage the dirty area - // Prune the path so we don't flow the block frame _again_ - // when returning to the caller. aReflowState.path->Remove(iter); } - else { - // At least one of the frames along the reflow path wasn't - // absolutely positioned, so we'll need to deal with it in - // normal block reflow. - aWasHandled = PR_FALSE; - } } } - - return NS_OK; } void @@ -526,7 +652,7 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat strcpy(aBuf, "UC"); } else { - if(0xdeadbeef == aSize) + if((PRInt32)0xdeadbeef == aSize) { strcpy(aBuf, "deadbeef"); } diff --git a/mozilla/layout/generic/nsAbsoluteContainingBlock.h b/mozilla/layout/generic/nsAbsoluteContainingBlock.h index 6dd015f964d..68f18425b55 100644 --- a/mozilla/layout/generic/nsAbsoluteContainingBlock.h +++ b/mozilla/layout/generic/nsAbsoluteContainingBlock.h @@ -110,19 +110,24 @@ public: nscoord aContainingBlockHeight, nsRect* aChildBounds = nsnull); - // Called only for a reflow reason of eReflowReason_Incremental. The - // aWasHandled return value indicates whether the reflow command was - // handled (i.e., the reflow command involved an absolutely positioned - // child element), or whether the caller should handle it. - // Returns (in the local coordinate space) the bounding rect of the absolutely - // positioned child elements taking into account their overflow area (if it - // is visible). This is only set if the reflow command was handled - nsresult IncrementalReflow(nsIFrame* aDelegatingFrame, - nsPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, - PRBool& aWasHandled); + // Called by the delegating frame to determine whether the + // incremental reflow is entirely targeted at absolute children + PRBool ReflowingAbsolutesOnly(nsIFrame* aDelegatingFrame, + const nsHTMLReflowState& aReflowState); + + // Called only for a reflow reason of eReflowReason_Incremental. + // Returns (in the local coordinate space) the bounding rect of the + // absolutely positioned child elements taking into account their + // overflow area (if it is visible). + void IncrementalReflow(nsIFrame* aDelegatingFrame, + nsPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nscoord aContainingBlockWidth, + nscoord aContainingBlockHeight); + + // Returns PR_TRUE if any absolute frames depend on the position of their + // placeholders + PRBool FramesDependOnContainer(PRBool aWidthChanged, PRBool aHeightChanged); void DestroyFrames(nsIFrame* aDelegatingFrame, nsPresContext* aPresContext); diff --git a/mozilla/layout/generic/nsBlockFrame.cpp b/mozilla/layout/generic/nsBlockFrame.cpp index 3178ea5e9a6..93d3a5bb5dd 100644 --- a/mozilla/layout/generic/nsBlockFrame.cpp +++ b/mozilla/layout/generic/nsBlockFrame.cpp @@ -620,7 +620,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, } #endif - nsRect oldRect(mRect); + nsSize oldSize = GetSize(); // Should we create a space manager? nsAutoSpaceManager autoSpaceManager(NS_CONST_CAST(nsHTMLReflowState &, aReflowState)); @@ -632,50 +632,47 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, if (NS_BLOCK_SPACE_MGR & mState) autoSpaceManager.CreateSpaceManagerFor(aPresContext, this); - // See if it's an incremental reflow command + // See if it's an incremental reflow command targeted only at + // absolute frames. If so we can skip a whole lot of work via this + // fast path. if (mAbsoluteContainer.HasAbsoluteFrames() && - eReflowReason_Incremental == aReflowState.reason) { - // Give the absolute positioning code a chance to handle it + eReflowReason_Incremental == aReflowState.reason && + !aMetrics.mComputeMEW && + mAbsoluteContainer.ReflowingAbsolutesOnly(this, aReflowState)) { nscoord containingBlockWidth; nscoord containingBlockHeight; - PRBool handled; CalculateContainingBlock(aReflowState, mRect.width, mRect.height, containingBlockWidth, containingBlockHeight); mAbsoluteContainer.IncrementalReflow(this, aPresContext, aReflowState, containingBlockWidth, - containingBlockHeight, - handled); + containingBlockHeight); - // If the incremental reflow command was handled by the absolute - // positioning code, then we're all done. - if (handled && !aMetrics.mComputeMEW) { - // Just return our current size as our desired size. - aMetrics.width = mRect.width; - aMetrics.height = mRect.height; - aMetrics.ascent = mAscent; - aMetrics.descent = aMetrics.height - aMetrics.ascent; - - // Whether or not we're complete hasn't changed - aStatus = (nsnull != mNextInFlow) ? NS_FRAME_NOT_COMPLETE : NS_FRAME_COMPLETE; - - // Factor the absolutely positioned child bounds into the overflow area - ComputeCombinedArea(aReflowState, aMetrics); - nsRect childBounds; - mAbsoluteContainer.CalculateChildBounds(aPresContext, childBounds); - aMetrics.mOverflowArea.UnionRect(aMetrics.mOverflowArea, childBounds); - - FinishAndStoreOverflow(&aMetrics); + // Just return our current size as our desired size. + aMetrics.width = mRect.width; + aMetrics.height = mRect.height; + aMetrics.ascent = mAscent; + aMetrics.descent = aMetrics.height - aMetrics.ascent; + + // Whether or not we're complete hasn't changed + aStatus = (nsnull != mNextInFlow) ? NS_FRAME_NOT_COMPLETE : NS_FRAME_COMPLETE; + + // Factor the absolutely positioned child bounds into the overflow area + ComputeCombinedArea(aReflowState, aMetrics); + nsRect childBounds; + mAbsoluteContainer.CalculateChildBounds(aPresContext, childBounds); + aMetrics.mOverflowArea.UnionRect(aMetrics.mOverflowArea, childBounds); + + FinishAndStoreOverflow(&aMetrics); #ifdef DEBUG - if (gNoisy) { - gNoiseIndent--; - } -#endif - - return NS_OK; + if (gNoisy) { + gNoiseIndent--; } +#endif + + return NS_OK; } // OK, some lines may be reflowed. Blow away any saved line cursor because @@ -939,6 +936,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, // Compute our final size ComputeFinalSize(aReflowState, state, aMetrics); + nsRect currentOverflow = aMetrics.mOverflowArea; FinishAndStoreOverflow(&aMetrics); // see if verifyReflow is enabled, and if so store off the space manager pointer @@ -969,17 +967,47 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, // can use our rect (the border edge) since if the border style // changed, the reflow would have been targeted at us so we'd satisfy // condition 1. - if (NS_SUCCEEDED(rv) && mAbsoluteContainer.HasAbsoluteFrames()) { + if (mAbsoluteContainer.HasAbsoluteFrames()) { nsRect childBounds; - if (eReflowReason_Incremental != aReflowState.reason || - aReflowState.path->mReflowCommand || - mRect != oldRect) { - nscoord containingBlockWidth; - nscoord containingBlockHeight; + nscoord containingBlockWidth; + nscoord containingBlockHeight; - CalculateContainingBlock(aReflowState, aMetrics.width, aMetrics.height, - containingBlockWidth, containingBlockHeight); + CalculateContainingBlock(aReflowState, aMetrics.width, aMetrics.height, + containingBlockWidth, containingBlockHeight); + PRBool needAbsoluteReflow = PR_TRUE; + if (eReflowReason_Incremental == aReflowState.reason) { + // Do the incremental reflows ... would be nice to merge with + // the reflows below but that would be more work, and more risky + mAbsoluteContainer.IncrementalReflow(this, aPresContext, aReflowState, + containingBlockWidth, + containingBlockHeight); + + // If a reflow was targeted at this block then we'd better + // reflow the absolutes. For example the borders and padding + // might have changed in a way that leaves the frame size the + // same but the padding edge has moved. + if (!aReflowState.path->mReflowCommand) { + // Now we can assume that the padding edge hasn't moved. + // We need to reflow the absolutes if one of them depends on + // its placeholder position, or the containing block size in a + // direction in which the containing block size might have + // changed. + PRBool cbWidthChanged = aMetrics.width != oldSize.width; + PRBool isRoot = !GetContent()->GetParent(); + // If isRoot and we have auto height, then we are the initial + // containing block and the containing block height is the + // viewport height, which can't change during incremental + // reflow. + PRBool cbHeightChanged = isRoot && NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight + ? PR_FALSE : aMetrics.height != oldSize.height; + if (!mAbsoluteContainer.FramesDependOnContainer(cbWidthChanged, cbHeightChanged)) { + needAbsoluteReflow = PR_FALSE; + } + } + } + + if (needAbsoluteReflow) { rv = mAbsoluteContainer.Reflow(this, aPresContext, aReflowState, containingBlockWidth, containingBlockHeight, @@ -989,8 +1017,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, } // Factor the absolutely positioned child bounds into the overflow area - aMetrics.mOverflowArea.UnionRect(aMetrics.mOverflowArea, childBounds); - + aMetrics.mOverflowArea.UnionRect(currentOverflow, childBounds); FinishAndStoreOverflow(&aMetrics); } @@ -2912,7 +2939,7 @@ IsMarginZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) } /* virtual */ PRBool -nsBlockFrame::IsEmpty() +nsBlockFrame::IsFrameEmpty() { const nsStylePosition* position = GetStylePosition(); @@ -2960,6 +2987,16 @@ nsBlockFrame::IsEmpty() return PR_FALSE; } + return PR_TRUE; +} + +PRBool +nsBlockFrame::IsEmpty() +{ + if (!IsFrameEmpty()) { + return PR_FALSE; + } + for (line_iterator line = begin_lines(), line_end = end_lines(); line != line_end; ++line) @@ -2967,6 +3004,7 @@ nsBlockFrame::IsEmpty() if (!line->IsEmpty()) return PR_FALSE; } + return PR_TRUE; } diff --git a/mozilla/layout/generic/nsBlockFrame.h b/mozilla/layout/generic/nsBlockFrame.h index 487f71518b5..1e6e77fc9e7 100644 --- a/mozilla/layout/generic/nsBlockFrame.h +++ b/mozilla/layout/generic/nsBlockFrame.h @@ -192,6 +192,7 @@ public: PRBool* aIsVisible); virtual PRBool IsEmpty(); + virtual PRBool IsFrameEmpty(); // nsIHTMLReflow NS_IMETHOD Reflow(nsPresContext* aPresContext, diff --git a/mozilla/layout/generic/nsBlockReflowState.cpp b/mozilla/layout/generic/nsBlockReflowState.cpp index 0a3accdb217..9554d37d99e 100644 --- a/mozilla/layout/generic/nsBlockReflowState.cpp +++ b/mozilla/layout/generic/nsBlockReflowState.cpp @@ -354,7 +354,7 @@ nsBlockReflowState::ClearPastFloats(PRUint8 aBreakType) #ifdef NOISY_FLOAT_CLEARING nsFrame::ListTag(stdout, mBlock); printf(": ClearPastFloats: mPrevBottomMargin=%d saveY=%d oldY=%d newY=%d deltaY=%d\n", - mPrevBottomMargin, saveY, saveY - mPrevBottomMargin, mY, + mPrevBottomMargin.get(), saveY, saveY - mPrevBottomMargin.get(), mY, mY - saveY); #endif diff --git a/mozilla/layout/generic/nsFrame.cpp b/mozilla/layout/generic/nsFrame.cpp index 8f52648d6ea..d281400ee43 100644 --- a/mozilla/layout/generic/nsFrame.cpp +++ b/mozilla/layout/generic/nsFrame.cpp @@ -2851,6 +2851,12 @@ nsFrame::IsEmpty() return PR_FALSE; } +/* virtual */ PRBool +nsFrame::IsFrameEmpty() +{ + return PR_FALSE; +} + NS_IMETHODIMP nsFrame::GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon) { diff --git a/mozilla/layout/generic/nsFrame.h b/mozilla/layout/generic/nsFrame.h index bee93a871e9..6c166abcd66 100644 --- a/mozilla/layout/generic/nsFrame.h +++ b/mozilla/layout/generic/nsFrame.h @@ -283,6 +283,7 @@ public: PRBool* aIsVisible); virtual PRBool IsEmpty(); + virtual PRBool IsFrameEmpty(); // nsIHTMLReflow NS_IMETHOD WillReflow(nsPresContext* aPresContext); diff --git a/mozilla/layout/generic/nsHTMLReflowState.cpp b/mozilla/layout/generic/nsHTMLReflowState.cpp index eb6aaf1662b..f0a90f5630f 100644 --- a/mozilla/layout/generic/nsHTMLReflowState.cpp +++ b/mozilla/layout/generic/nsHTMLReflowState.cpp @@ -703,6 +703,30 @@ FindImmediateChildOf(nsIFrame* aParent, nsIFrame* aDescendantFrame) return result; } +/** + * Returns PR_TRUE iff a pre-order traversal of the normal child + * frames rooted at aFrame finds no non-empty frame before aDescendant. + */ +static PRBool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame, + nsIFrame* aDescendant, PRBool* aFound) { + if (aFrame == aDescendant) { + *aFound = PR_TRUE; + return PR_TRUE; + } + if (!aFrame->IsFrameEmpty()) { + *aFound = PR_FALSE; + return PR_FALSE; + } + for (nsIFrame* f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling()) { + PRBool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound); + if (*aFound || !allEmpty) { + return allEmpty; + } + } + *aFound = PR_FALSE; + return PR_TRUE; +} + // Calculate the hypothetical box that the element would have if it were in // the flow. The values returned are relative to the padding edge of the // absolute containing block @@ -803,17 +827,18 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, // relatively positioned... if (lineBox != blockFrame->end_lines()) { nsIFrame * firstFrame = lineBox->mFirstChild; - while (firstFrame != aPlaceholderFrame) { - NS_ASSERTION(firstFrame, "Must reach our placeholder before end of list!"); - if (!firstFrame) // This can be removed when we split out-of-flow - break; // frames correctly, see bug 223064 - if (!firstFrame->IsEmpty()) { + PRBool found = PR_FALSE; + PRBool allEmpty = PR_TRUE; + while (firstFrame) { // See bug 223064 + allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame, + aPlaceholderFrame, &found); + if (found || !allEmpty) break; - } - firstFrame = firstFrame->GetNextSibling(); } - if (firstFrame == aPlaceholderFrame) { + NS_ASSERTION(firstFrame, "Couldn't find placeholder!"); + + if (allEmpty) { // The top of the hypothetical box is the top of the line containing // the placeholder, since there is nothing in the line before our // placeholder except empty frames. diff --git a/mozilla/layout/generic/nsIFrame.h b/mozilla/layout/generic/nsIFrame.h index a353cc15728..426a2f650a1 100644 --- a/mozilla/layout/generic/nsIFrame.h +++ b/mozilla/layout/generic/nsIFrame.h @@ -1219,6 +1219,12 @@ public: */ virtual PRBool IsEmpty() = 0; + /** + * Determine whether the frame is logically empty, assuming that all + * its children are empty. + */ + virtual PRBool IsFrameEmpty() = 0; + /** * IsGeneratedContentFrame returns whether a frame corresponds to * generated content diff --git a/mozilla/layout/generic/nsInlineFrame.cpp b/mozilla/layout/generic/nsInlineFrame.cpp index 230abed746e..b37b8dac403 100644 --- a/mozilla/layout/generic/nsInlineFrame.cpp +++ b/mozilla/layout/generic/nsInlineFrame.cpp @@ -138,7 +138,7 @@ IsMarginZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) } /* virtual */ PRBool -nsInlineFrame::IsEmpty() +nsInlineFrame::IsFrameEmpty() { #if 0 // I used to think inline frames worked this way, but it seems they @@ -170,11 +170,20 @@ nsInlineFrame::IsEmpty() margin->mMargin.GetLeft(coord))) { return PR_FALSE; } +} + +PRBool +nsInlineFrame::IsEmpty() +{ + if (!IsFrameEmpty()) { + return PR_FALSE; + } for (nsIFrame *kid = mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) { if (!kid->IsEmpty()) return PR_FALSE; } + return PR_TRUE; } @@ -1220,39 +1229,8 @@ nsPositionedInlineFrame::Reflow(nsPresContext* aPresContext, { nsresult rv = NS_OK; - nsRect oldRect(mRect); - - // See if it's an incremental reflow command - if (mAbsoluteContainer.HasAbsoluteFrames() && - eReflowReason_Incremental == aReflowState.reason) { - // Give the absolute positioning code a chance to handle it - PRBool handled; - nscoord containingBlockWidth = -1; - nscoord containingBlockHeight = -1; - - mAbsoluteContainer.IncrementalReflow(this, aPresContext, aReflowState, - containingBlockWidth, containingBlockHeight, - handled); - - // If the incremental reflow command was handled by the absolute positioning - // code, then we're all done - if (handled) { - // Just return our current size as our desired size - // XXX I don't know how to compute that without a reflow, so for the - // time being pretend a resize reflow occured - nsHTMLReflowState reflowState(aReflowState); - reflowState.reason = eReflowReason_Resize; - reflowState.path = nsnull; - rv = nsInlineFrame::Reflow(aPresContext, aDesiredSize, reflowState, aStatus); - - // Factor the absolutely positioned child bounds into the overflow area - // Don't include this frame's bounds, nor its inline descendants' bounds, - // and don't store the overflow property. - // That will all be done by nsLineLayout::RelativePositionFrames. - mAbsoluteContainer.CalculateChildBounds(aPresContext, aDesiredSize.mOverflowArea); - return rv; - } - } + // Don't bother optimizing for fast incremental reflow of absolute + // children of an inline // Let the inline frame do its reflow first rv = nsInlineFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); @@ -1267,10 +1245,7 @@ nsPositionedInlineFrame::Reflow(nsPresContext* aPresContext, // changed, the reflow would have been targeted at us so we'd satisfy // condition 1. if (NS_SUCCEEDED(rv) && - mAbsoluteContainer.HasAbsoluteFrames() && - (eReflowReason_Incremental != aReflowState.reason || - aReflowState.path->mReflowCommand || - mRect != oldRect)) { + mAbsoluteContainer.HasAbsoluteFrames()) { // The containing block for the abs pos kids is formed by our content edge. nscoord containingBlockWidth = aDesiredSize.width - (aReflowState.mComputedBorderPadding.left + @@ -1279,6 +1254,14 @@ nsPositionedInlineFrame::Reflow(nsPresContext* aPresContext, (aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom); + // Do any incremental reflows ... would be nice to merge with + // the reflows below but that would be more work, and more risky + if (eReflowReason_Incremental == aReflowState.reason) { + mAbsoluteContainer.IncrementalReflow(this, aPresContext, aReflowState, + containingBlockWidth, + containingBlockHeight); + } + // Factor the absolutely positioned child bounds into the overflow area // Don't include this frame's bounds, nor its inline descendants' bounds, // and don't store the overflow property. diff --git a/mozilla/layout/generic/nsInlineFrame.h b/mozilla/layout/generic/nsInlineFrame.h index 40c3698e9f9..f83c5155237 100644 --- a/mozilla/layout/generic/nsInlineFrame.h +++ b/mozilla/layout/generic/nsInlineFrame.h @@ -109,6 +109,7 @@ public: virtual nsIAtom* GetType() const; virtual PRBool IsEmpty(); + virtual PRBool IsFrameEmpty(); // nsIHTMLReflow overrides NS_IMETHOD Reflow(nsPresContext* aPresContext, diff --git a/mozilla/layout/generic/nsPlaceholderFrame.cpp b/mozilla/layout/generic/nsPlaceholderFrame.cpp index d81cb8df082..e81a660e001 100644 --- a/mozilla/layout/generic/nsPlaceholderFrame.cpp +++ b/mozilla/layout/generic/nsPlaceholderFrame.cpp @@ -92,12 +92,6 @@ nsPlaceholderFrame::GetType() const return nsLayoutAtoms::placeholderFrame; } -/* virtual */ PRBool -nsPlaceholderFrame::IsEmpty() -{ - return PR_TRUE; -} - #ifdef DEBUG NS_IMETHODIMP nsPlaceholderFrame::Paint(nsPresContext* aPresContext, diff --git a/mozilla/layout/generic/nsPlaceholderFrame.h b/mozilla/layout/generic/nsPlaceholderFrame.h index d48784a4aae..c23de746f0d 100644 --- a/mozilla/layout/generic/nsPlaceholderFrame.h +++ b/mozilla/layout/generic/nsPlaceholderFrame.h @@ -85,7 +85,8 @@ public: NS_IMETHOD GetFrameName(nsAString& aResult) const; #endif - virtual PRBool IsEmpty(); + virtual PRBool IsEmpty() { return PR_TRUE; } + virtual PRBool IsFrameEmpty() { return PR_TRUE; } protected: nsIFrame* mOutOfFlowFrame; diff --git a/mozilla/layout/generic/nsTextFrame.cpp b/mozilla/layout/generic/nsTextFrame.cpp index d5f266dd2a9..8c70b11a4b7 100644 --- a/mozilla/layout/generic/nsTextFrame.cpp +++ b/mozilla/layout/generic/nsTextFrame.cpp @@ -483,6 +483,7 @@ public: PRBool* aIsVisible); virtual PRBool IsEmpty(); + virtual PRBool IsFrameEmpty() { return IsEmpty(); } #ifdef ACCESSIBILITY NS_IMETHOD GetAccessible(nsIAccessible** aAccessible); diff --git a/mozilla/layout/generic/nsViewportFrame.cpp b/mozilla/layout/generic/nsViewportFrame.cpp index dcddc6712f9..c2538916a2f 100644 --- a/mozilla/layout/generic/nsViewportFrame.cpp +++ b/mozilla/layout/generic/nsViewportFrame.cpp @@ -295,24 +295,18 @@ ViewportFrame::Reflow(nsPresContext* aPresContext, command->GetType(reflowType); } - PRBool wasHandled = PR_FALSE; if (reflowType != eReflowType_UserDefined && aReflowState.reason == eReflowReason_Incremental) { // Incremental reflow - rv = mFixedContainer.IncrementalReflow(this, aPresContext, reflowState, - reflowState.mComputedWidth, - reflowState.mComputedHeight, - wasHandled); + mFixedContainer.IncrementalReflow(this, aPresContext, reflowState, + reflowState.mComputedWidth, + reflowState.mComputedHeight); } - if (!wasHandled) { - // It's the initial reflow or some other non-incremental reflow or - // IncrementalReflow() didn't handle it. Just reflow all the - // fixed-pos frames. - rv = mFixedContainer.Reflow(this, aPresContext, reflowState, - reflowState.mComputedWidth, - reflowState.mComputedHeight); - } + // Just reflow all the fixed-pos frames. + rv = mFixedContainer.Reflow(this, aPresContext, reflowState, + reflowState.mComputedWidth, + reflowState.mComputedHeight); // If this is an initial reflow, resize reflow, or style change reflow // then do a repaint diff --git a/mozilla/layout/html/base/src/nsAbsoluteContainingBlock.cpp b/mozilla/layout/html/base/src/nsAbsoluteContainingBlock.cpp index 921f8b98375..440523bb901 100644 --- a/mozilla/layout/html/base/src/nsAbsoluteContainingBlock.cpp +++ b/mozilla/layout/html/base/src/nsAbsoluteContainingBlock.cpp @@ -251,19 +251,160 @@ nsAbsoluteContainingBlock::CalculateChildBounds(nsPresContext* aPresContext, } } -nsresult +PRBool +nsAbsoluteContainingBlock::ReflowingAbsolutesOnly(nsIFrame* aDelegatingFrame, + const nsHTMLReflowState& aReflowState) +{ + // See if the reflow command is targeted at us. + nsReflowPath *path = aReflowState.path; + nsHTMLReflowCommand *command = path->mReflowCommand; + + if (command) { + // It's targeted at us. See if it's for the positioned child frames + nsCOMPtr listName; + command->GetChildListName(*getter_AddRefs(listName)); + + if (GetChildListName() != listName) { + // A reflow command is targeted directly at this block. + // The block will have to do a proper reflow. + return PR_FALSE; + } + } + + nsReflowPath::iterator iter = path->FirstChild(); + nsReflowPath::iterator end = path->EndChildren(); + + if (iter != end && mAbsoluteFrames.NotEmpty()) { + for ( ; iter != end; ++iter) { + // See if it's one of our absolutely positioned child frames + if (!mAbsoluteFrames.ContainsFrame(*iter)) { + // At least one of the frames along the reflow path wasn't + // absolutely positioned, so we'll need to deal with it in + // normal block reflow. + return PR_FALSE; + } + } + } + + return PR_TRUE; +} + +static PRBool IsFixedBorderSize(nsStyleUnit aUnit) { + return aUnit == eStyleUnit_Coord || aUnit == eStyleUnit_Enumerated + || aUnit == eStyleUnit_Null; +} +static PRBool IsFixedPaddingSize(nsStyleUnit aUnit) { + return aUnit == eStyleUnit_Coord || aUnit == eStyleUnit_Null; +} +static PRBool IsFixedMarginSize(nsStyleUnit aUnit) { + return aUnit == eStyleUnit_Coord || aUnit == eStyleUnit_Null; +} +static PRBool IsFixedMaxSize(nsStyleUnit aUnit) { + return aUnit == eStyleUnit_Null || aUnit == eStyleUnit_Coord; +} + +// XXX this logic should eventually be combined into the Reflow methods +// so we reflow only those frames that need it +PRBool +nsAbsoluteContainingBlock::FramesDependOnContainer(PRBool aWidthChanged, + PRBool aHeightChanged) +{ + for (nsIFrame* f = mAbsoluteFrames.FirstChild(); f; f = f->GetNextSibling()) { + const nsStylePosition* pos = f->GetStylePosition(); + // See if f's position might have changed because it depends on a + // placeholder's position + if (pos->mOffset.GetTopUnit() == eStyleUnit_Auto || + (f->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL + ? pos->mOffset.GetRightUnit() : pos->mOffset.GetLeftUnit()) == eStyleUnit_Auto) { + // Note that in CSS2.1, 'bottom:auto' can never actually induce a dependence + // on the position of the placeholder + return PR_TRUE; + } + if (!aWidthChanged && !aHeightChanged) { + // skip getting style data + continue; + } + const nsStyleBorder* border = f->GetStyleBorder(); + const nsStylePadding* padding = f->GetStylePadding(); + const nsStyleMargin* margin = f->GetStyleMargin(); + if (aWidthChanged) { + // See if f's width might have changed. + // If border-left, border-right, padding-left, padding-right, + // width, min-width, and max-width are all lengths, 'none', or enumerated, + // then our frame width does not depend on the parent width. + if (pos->mWidth.GetUnit() != eStyleUnit_Coord || + pos->mMinWidth.GetUnit() != eStyleUnit_Coord || + !IsFixedMaxSize(pos->mMaxWidth.GetUnit()) || + !IsFixedBorderSize(border->mBorder.GetLeftUnit()) || + !IsFixedBorderSize(border->mBorder.GetRightUnit()) || + !IsFixedPaddingSize(padding->mPadding.GetLeftUnit()) || + !IsFixedPaddingSize(padding->mPadding.GetRightUnit())) { + return PR_TRUE; + } + + // See if f's position might have changed. If we're RTL then the + // rules are slightly different. We'll assume percentage or auto + // margins will always induce a dependency on the size + if (!IsFixedMarginSize(margin->mMargin.GetLeftUnit()) || + !IsFixedMarginSize(margin->mMargin.GetRightUnit())) { + return PR_TRUE; + } + if (f->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { + // Note that even if 'left' is a length, our position can + // still depend on the containing block width, because if + // 'right' is also a length we will discard 'left' and be + // positioned relative to the containing block right edge. + // 'left' length and 'right' auto is the only combination + // we can be sure of. + if (pos->mOffset.GetLeftUnit() != eStyleUnit_Coord || + pos->mOffset.GetRightUnit() != eStyleUnit_Auto) { + return PR_TRUE; + } + } else { + if (pos->mOffset.GetLeftUnit() != eStyleUnit_Coord) { + return PR_TRUE; + } + } + } + if (aHeightChanged) { + // See if f's height might have changed. + // If border-top, border-bottom, padding-top, padding-bottom, + // min-height, and max-height are all lengths or 'none', + // and height is a length or height and bottom are auto and top is not auto, + // then our frame height does not depend on the parent height. + if (!(pos->mHeight.GetUnit() == eStyleUnit_Coord || + (pos->mHeight.GetUnit() == eStyleUnit_Auto && + pos->mOffset.GetBottomUnit() == eStyleUnit_Auto && + pos->mOffset.GetTopUnit() != eStyleUnit_Auto)) || + pos->mMinHeight.GetUnit() != eStyleUnit_Coord || + !IsFixedMaxSize(pos->mMaxHeight.GetUnit()) || + !IsFixedBorderSize(border->mBorder.GetTopUnit()) || + !IsFixedBorderSize(border->mBorder.GetBottomUnit()) || + !IsFixedPaddingSize(padding->mPadding.GetTopUnit()) || + !IsFixedPaddingSize(padding->mPadding.GetBottomUnit())) { + return PR_TRUE; + } + + // See if f's position might have changed. + if (!IsFixedMarginSize(margin->mMargin.GetTopUnit()) || + !IsFixedMarginSize(margin->mMargin.GetBottomUnit())) { + return PR_TRUE; + } + if (pos->mOffset.GetTopUnit() != eStyleUnit_Coord) { + return PR_TRUE; + } + } + } + return PR_FALSE; +} + +void nsAbsoluteContainingBlock::IncrementalReflow(nsIFrame* aDelegatingFrame, - nsPresContext* aPresContext, + nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, - PRBool& aWasHandled) + nscoord aContainingBlockHeight) { - // Initialize the OUT parameters - // Assume we handled the incremental reflow - // until we find something that requires a full block reflow - aWasHandled = PR_TRUE; - // See if the reflow command is targeted at us. nsReflowPath *path = aReflowState.path; nsHTMLReflowCommand *command = path->mReflowCommand; @@ -300,11 +441,6 @@ nsAbsoluteContainingBlock::IncrementalReflow(nsIFrame* aDelegatin reason, status); } } - } else { - // A reflow command is targeted directly at this block. - // The block will have to do a proper reflow. - // But keep going to make sure that we process any dirty absolute frames, below. - aWasHandled = PR_FALSE; } } @@ -326,20 +462,10 @@ nsAbsoluteContainingBlock::IncrementalReflow(nsIFrame* aDelegatin // repainting, and because it has a view if it changes size // the view manager will damage the dirty area - // Prune the path so we don't flow the block frame _again_ - // when returning to the caller. aReflowState.path->Remove(iter); } - else { - // At least one of the frames along the reflow path wasn't - // absolutely positioned, so we'll need to deal with it in - // normal block reflow. - aWasHandled = PR_FALSE; - } } } - - return NS_OK; } void @@ -526,7 +652,7 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat strcpy(aBuf, "UC"); } else { - if(0xdeadbeef == aSize) + if((PRInt32)0xdeadbeef == aSize) { strcpy(aBuf, "deadbeef"); } diff --git a/mozilla/layout/html/base/src/nsAbsoluteContainingBlock.h b/mozilla/layout/html/base/src/nsAbsoluteContainingBlock.h index 6dd015f964d..68f18425b55 100644 --- a/mozilla/layout/html/base/src/nsAbsoluteContainingBlock.h +++ b/mozilla/layout/html/base/src/nsAbsoluteContainingBlock.h @@ -110,19 +110,24 @@ public: nscoord aContainingBlockHeight, nsRect* aChildBounds = nsnull); - // Called only for a reflow reason of eReflowReason_Incremental. The - // aWasHandled return value indicates whether the reflow command was - // handled (i.e., the reflow command involved an absolutely positioned - // child element), or whether the caller should handle it. - // Returns (in the local coordinate space) the bounding rect of the absolutely - // positioned child elements taking into account their overflow area (if it - // is visible). This is only set if the reflow command was handled - nsresult IncrementalReflow(nsIFrame* aDelegatingFrame, - nsPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, - PRBool& aWasHandled); + // Called by the delegating frame to determine whether the + // incremental reflow is entirely targeted at absolute children + PRBool ReflowingAbsolutesOnly(nsIFrame* aDelegatingFrame, + const nsHTMLReflowState& aReflowState); + + // Called only for a reflow reason of eReflowReason_Incremental. + // Returns (in the local coordinate space) the bounding rect of the + // absolutely positioned child elements taking into account their + // overflow area (if it is visible). + void IncrementalReflow(nsIFrame* aDelegatingFrame, + nsPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nscoord aContainingBlockWidth, + nscoord aContainingBlockHeight); + + // Returns PR_TRUE if any absolute frames depend on the position of their + // placeholders + PRBool FramesDependOnContainer(PRBool aWidthChanged, PRBool aHeightChanged); void DestroyFrames(nsIFrame* aDelegatingFrame, nsPresContext* aPresContext); diff --git a/mozilla/layout/html/base/src/nsBlockFrame.cpp b/mozilla/layout/html/base/src/nsBlockFrame.cpp index 3178ea5e9a6..93d3a5bb5dd 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.cpp +++ b/mozilla/layout/html/base/src/nsBlockFrame.cpp @@ -620,7 +620,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, } #endif - nsRect oldRect(mRect); + nsSize oldSize = GetSize(); // Should we create a space manager? nsAutoSpaceManager autoSpaceManager(NS_CONST_CAST(nsHTMLReflowState &, aReflowState)); @@ -632,50 +632,47 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, if (NS_BLOCK_SPACE_MGR & mState) autoSpaceManager.CreateSpaceManagerFor(aPresContext, this); - // See if it's an incremental reflow command + // See if it's an incremental reflow command targeted only at + // absolute frames. If so we can skip a whole lot of work via this + // fast path. if (mAbsoluteContainer.HasAbsoluteFrames() && - eReflowReason_Incremental == aReflowState.reason) { - // Give the absolute positioning code a chance to handle it + eReflowReason_Incremental == aReflowState.reason && + !aMetrics.mComputeMEW && + mAbsoluteContainer.ReflowingAbsolutesOnly(this, aReflowState)) { nscoord containingBlockWidth; nscoord containingBlockHeight; - PRBool handled; CalculateContainingBlock(aReflowState, mRect.width, mRect.height, containingBlockWidth, containingBlockHeight); mAbsoluteContainer.IncrementalReflow(this, aPresContext, aReflowState, containingBlockWidth, - containingBlockHeight, - handled); + containingBlockHeight); - // If the incremental reflow command was handled by the absolute - // positioning code, then we're all done. - if (handled && !aMetrics.mComputeMEW) { - // Just return our current size as our desired size. - aMetrics.width = mRect.width; - aMetrics.height = mRect.height; - aMetrics.ascent = mAscent; - aMetrics.descent = aMetrics.height - aMetrics.ascent; - - // Whether or not we're complete hasn't changed - aStatus = (nsnull != mNextInFlow) ? NS_FRAME_NOT_COMPLETE : NS_FRAME_COMPLETE; - - // Factor the absolutely positioned child bounds into the overflow area - ComputeCombinedArea(aReflowState, aMetrics); - nsRect childBounds; - mAbsoluteContainer.CalculateChildBounds(aPresContext, childBounds); - aMetrics.mOverflowArea.UnionRect(aMetrics.mOverflowArea, childBounds); - - FinishAndStoreOverflow(&aMetrics); + // Just return our current size as our desired size. + aMetrics.width = mRect.width; + aMetrics.height = mRect.height; + aMetrics.ascent = mAscent; + aMetrics.descent = aMetrics.height - aMetrics.ascent; + + // Whether or not we're complete hasn't changed + aStatus = (nsnull != mNextInFlow) ? NS_FRAME_NOT_COMPLETE : NS_FRAME_COMPLETE; + + // Factor the absolutely positioned child bounds into the overflow area + ComputeCombinedArea(aReflowState, aMetrics); + nsRect childBounds; + mAbsoluteContainer.CalculateChildBounds(aPresContext, childBounds); + aMetrics.mOverflowArea.UnionRect(aMetrics.mOverflowArea, childBounds); + + FinishAndStoreOverflow(&aMetrics); #ifdef DEBUG - if (gNoisy) { - gNoiseIndent--; - } -#endif - - return NS_OK; + if (gNoisy) { + gNoiseIndent--; } +#endif + + return NS_OK; } // OK, some lines may be reflowed. Blow away any saved line cursor because @@ -939,6 +936,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, // Compute our final size ComputeFinalSize(aReflowState, state, aMetrics); + nsRect currentOverflow = aMetrics.mOverflowArea; FinishAndStoreOverflow(&aMetrics); // see if verifyReflow is enabled, and if so store off the space manager pointer @@ -969,17 +967,47 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, // can use our rect (the border edge) since if the border style // changed, the reflow would have been targeted at us so we'd satisfy // condition 1. - if (NS_SUCCEEDED(rv) && mAbsoluteContainer.HasAbsoluteFrames()) { + if (mAbsoluteContainer.HasAbsoluteFrames()) { nsRect childBounds; - if (eReflowReason_Incremental != aReflowState.reason || - aReflowState.path->mReflowCommand || - mRect != oldRect) { - nscoord containingBlockWidth; - nscoord containingBlockHeight; + nscoord containingBlockWidth; + nscoord containingBlockHeight; - CalculateContainingBlock(aReflowState, aMetrics.width, aMetrics.height, - containingBlockWidth, containingBlockHeight); + CalculateContainingBlock(aReflowState, aMetrics.width, aMetrics.height, + containingBlockWidth, containingBlockHeight); + PRBool needAbsoluteReflow = PR_TRUE; + if (eReflowReason_Incremental == aReflowState.reason) { + // Do the incremental reflows ... would be nice to merge with + // the reflows below but that would be more work, and more risky + mAbsoluteContainer.IncrementalReflow(this, aPresContext, aReflowState, + containingBlockWidth, + containingBlockHeight); + + // If a reflow was targeted at this block then we'd better + // reflow the absolutes. For example the borders and padding + // might have changed in a way that leaves the frame size the + // same but the padding edge has moved. + if (!aReflowState.path->mReflowCommand) { + // Now we can assume that the padding edge hasn't moved. + // We need to reflow the absolutes if one of them depends on + // its placeholder position, or the containing block size in a + // direction in which the containing block size might have + // changed. + PRBool cbWidthChanged = aMetrics.width != oldSize.width; + PRBool isRoot = !GetContent()->GetParent(); + // If isRoot and we have auto height, then we are the initial + // containing block and the containing block height is the + // viewport height, which can't change during incremental + // reflow. + PRBool cbHeightChanged = isRoot && NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight + ? PR_FALSE : aMetrics.height != oldSize.height; + if (!mAbsoluteContainer.FramesDependOnContainer(cbWidthChanged, cbHeightChanged)) { + needAbsoluteReflow = PR_FALSE; + } + } + } + + if (needAbsoluteReflow) { rv = mAbsoluteContainer.Reflow(this, aPresContext, aReflowState, containingBlockWidth, containingBlockHeight, @@ -989,8 +1017,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, } // Factor the absolutely positioned child bounds into the overflow area - aMetrics.mOverflowArea.UnionRect(aMetrics.mOverflowArea, childBounds); - + aMetrics.mOverflowArea.UnionRect(currentOverflow, childBounds); FinishAndStoreOverflow(&aMetrics); } @@ -2912,7 +2939,7 @@ IsMarginZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) } /* virtual */ PRBool -nsBlockFrame::IsEmpty() +nsBlockFrame::IsFrameEmpty() { const nsStylePosition* position = GetStylePosition(); @@ -2960,6 +2987,16 @@ nsBlockFrame::IsEmpty() return PR_FALSE; } + return PR_TRUE; +} + +PRBool +nsBlockFrame::IsEmpty() +{ + if (!IsFrameEmpty()) { + return PR_FALSE; + } + for (line_iterator line = begin_lines(), line_end = end_lines(); line != line_end; ++line) @@ -2967,6 +3004,7 @@ nsBlockFrame::IsEmpty() if (!line->IsEmpty()) return PR_FALSE; } + return PR_TRUE; } diff --git a/mozilla/layout/html/base/src/nsBlockFrame.h b/mozilla/layout/html/base/src/nsBlockFrame.h index 487f71518b5..1e6e77fc9e7 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.h +++ b/mozilla/layout/html/base/src/nsBlockFrame.h @@ -192,6 +192,7 @@ public: PRBool* aIsVisible); virtual PRBool IsEmpty(); + virtual PRBool IsFrameEmpty(); // nsIHTMLReflow NS_IMETHOD Reflow(nsPresContext* aPresContext, diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.cpp b/mozilla/layout/html/base/src/nsBlockReflowState.cpp index 0a3accdb217..9554d37d99e 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.cpp +++ b/mozilla/layout/html/base/src/nsBlockReflowState.cpp @@ -354,7 +354,7 @@ nsBlockReflowState::ClearPastFloats(PRUint8 aBreakType) #ifdef NOISY_FLOAT_CLEARING nsFrame::ListTag(stdout, mBlock); printf(": ClearPastFloats: mPrevBottomMargin=%d saveY=%d oldY=%d newY=%d deltaY=%d\n", - mPrevBottomMargin, saveY, saveY - mPrevBottomMargin, mY, + mPrevBottomMargin.get(), saveY, saveY - mPrevBottomMargin.get(), mY, mY - saveY); #endif diff --git a/mozilla/layout/html/base/src/nsFrame.cpp b/mozilla/layout/html/base/src/nsFrame.cpp index 8f52648d6ea..d281400ee43 100644 --- a/mozilla/layout/html/base/src/nsFrame.cpp +++ b/mozilla/layout/html/base/src/nsFrame.cpp @@ -2851,6 +2851,12 @@ nsFrame::IsEmpty() return PR_FALSE; } +/* virtual */ PRBool +nsFrame::IsFrameEmpty() +{ + return PR_FALSE; +} + NS_IMETHODIMP nsFrame::GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon) { diff --git a/mozilla/layout/html/base/src/nsFrame.h b/mozilla/layout/html/base/src/nsFrame.h index bee93a871e9..6c166abcd66 100644 --- a/mozilla/layout/html/base/src/nsFrame.h +++ b/mozilla/layout/html/base/src/nsFrame.h @@ -283,6 +283,7 @@ public: PRBool* aIsVisible); virtual PRBool IsEmpty(); + virtual PRBool IsFrameEmpty(); // nsIHTMLReflow NS_IMETHOD WillReflow(nsPresContext* aPresContext); diff --git a/mozilla/layout/html/base/src/nsHTMLReflowState.cpp b/mozilla/layout/html/base/src/nsHTMLReflowState.cpp index eb6aaf1662b..f0a90f5630f 100644 --- a/mozilla/layout/html/base/src/nsHTMLReflowState.cpp +++ b/mozilla/layout/html/base/src/nsHTMLReflowState.cpp @@ -703,6 +703,30 @@ FindImmediateChildOf(nsIFrame* aParent, nsIFrame* aDescendantFrame) return result; } +/** + * Returns PR_TRUE iff a pre-order traversal of the normal child + * frames rooted at aFrame finds no non-empty frame before aDescendant. + */ +static PRBool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame, + nsIFrame* aDescendant, PRBool* aFound) { + if (aFrame == aDescendant) { + *aFound = PR_TRUE; + return PR_TRUE; + } + if (!aFrame->IsFrameEmpty()) { + *aFound = PR_FALSE; + return PR_FALSE; + } + for (nsIFrame* f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling()) { + PRBool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound); + if (*aFound || !allEmpty) { + return allEmpty; + } + } + *aFound = PR_FALSE; + return PR_TRUE; +} + // Calculate the hypothetical box that the element would have if it were in // the flow. The values returned are relative to the padding edge of the // absolute containing block @@ -803,17 +827,18 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, // relatively positioned... if (lineBox != blockFrame->end_lines()) { nsIFrame * firstFrame = lineBox->mFirstChild; - while (firstFrame != aPlaceholderFrame) { - NS_ASSERTION(firstFrame, "Must reach our placeholder before end of list!"); - if (!firstFrame) // This can be removed when we split out-of-flow - break; // frames correctly, see bug 223064 - if (!firstFrame->IsEmpty()) { + PRBool found = PR_FALSE; + PRBool allEmpty = PR_TRUE; + while (firstFrame) { // See bug 223064 + allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame, + aPlaceholderFrame, &found); + if (found || !allEmpty) break; - } - firstFrame = firstFrame->GetNextSibling(); } - if (firstFrame == aPlaceholderFrame) { + NS_ASSERTION(firstFrame, "Couldn't find placeholder!"); + + if (allEmpty) { // The top of the hypothetical box is the top of the line containing // the placeholder, since there is nothing in the line before our // placeholder except empty frames. diff --git a/mozilla/layout/html/base/src/nsInlineFrame.cpp b/mozilla/layout/html/base/src/nsInlineFrame.cpp index 230abed746e..b37b8dac403 100644 --- a/mozilla/layout/html/base/src/nsInlineFrame.cpp +++ b/mozilla/layout/html/base/src/nsInlineFrame.cpp @@ -138,7 +138,7 @@ IsMarginZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) } /* virtual */ PRBool -nsInlineFrame::IsEmpty() +nsInlineFrame::IsFrameEmpty() { #if 0 // I used to think inline frames worked this way, but it seems they @@ -170,11 +170,20 @@ nsInlineFrame::IsEmpty() margin->mMargin.GetLeft(coord))) { return PR_FALSE; } +} + +PRBool +nsInlineFrame::IsEmpty() +{ + if (!IsFrameEmpty()) { + return PR_FALSE; + } for (nsIFrame *kid = mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) { if (!kid->IsEmpty()) return PR_FALSE; } + return PR_TRUE; } @@ -1220,39 +1229,8 @@ nsPositionedInlineFrame::Reflow(nsPresContext* aPresContext, { nsresult rv = NS_OK; - nsRect oldRect(mRect); - - // See if it's an incremental reflow command - if (mAbsoluteContainer.HasAbsoluteFrames() && - eReflowReason_Incremental == aReflowState.reason) { - // Give the absolute positioning code a chance to handle it - PRBool handled; - nscoord containingBlockWidth = -1; - nscoord containingBlockHeight = -1; - - mAbsoluteContainer.IncrementalReflow(this, aPresContext, aReflowState, - containingBlockWidth, containingBlockHeight, - handled); - - // If the incremental reflow command was handled by the absolute positioning - // code, then we're all done - if (handled) { - // Just return our current size as our desired size - // XXX I don't know how to compute that without a reflow, so for the - // time being pretend a resize reflow occured - nsHTMLReflowState reflowState(aReflowState); - reflowState.reason = eReflowReason_Resize; - reflowState.path = nsnull; - rv = nsInlineFrame::Reflow(aPresContext, aDesiredSize, reflowState, aStatus); - - // Factor the absolutely positioned child bounds into the overflow area - // Don't include this frame's bounds, nor its inline descendants' bounds, - // and don't store the overflow property. - // That will all be done by nsLineLayout::RelativePositionFrames. - mAbsoluteContainer.CalculateChildBounds(aPresContext, aDesiredSize.mOverflowArea); - return rv; - } - } + // Don't bother optimizing for fast incremental reflow of absolute + // children of an inline // Let the inline frame do its reflow first rv = nsInlineFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); @@ -1267,10 +1245,7 @@ nsPositionedInlineFrame::Reflow(nsPresContext* aPresContext, // changed, the reflow would have been targeted at us so we'd satisfy // condition 1. if (NS_SUCCEEDED(rv) && - mAbsoluteContainer.HasAbsoluteFrames() && - (eReflowReason_Incremental != aReflowState.reason || - aReflowState.path->mReflowCommand || - mRect != oldRect)) { + mAbsoluteContainer.HasAbsoluteFrames()) { // The containing block for the abs pos kids is formed by our content edge. nscoord containingBlockWidth = aDesiredSize.width - (aReflowState.mComputedBorderPadding.left + @@ -1279,6 +1254,14 @@ nsPositionedInlineFrame::Reflow(nsPresContext* aPresContext, (aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom); + // Do any incremental reflows ... would be nice to merge with + // the reflows below but that would be more work, and more risky + if (eReflowReason_Incremental == aReflowState.reason) { + mAbsoluteContainer.IncrementalReflow(this, aPresContext, aReflowState, + containingBlockWidth, + containingBlockHeight); + } + // Factor the absolutely positioned child bounds into the overflow area // Don't include this frame's bounds, nor its inline descendants' bounds, // and don't store the overflow property. diff --git a/mozilla/layout/html/base/src/nsInlineFrame.h b/mozilla/layout/html/base/src/nsInlineFrame.h index 40c3698e9f9..f83c5155237 100644 --- a/mozilla/layout/html/base/src/nsInlineFrame.h +++ b/mozilla/layout/html/base/src/nsInlineFrame.h @@ -109,6 +109,7 @@ public: virtual nsIAtom* GetType() const; virtual PRBool IsEmpty(); + virtual PRBool IsFrameEmpty(); // nsIHTMLReflow overrides NS_IMETHOD Reflow(nsPresContext* aPresContext, diff --git a/mozilla/layout/html/base/src/nsPlaceholderFrame.cpp b/mozilla/layout/html/base/src/nsPlaceholderFrame.cpp index d81cb8df082..e81a660e001 100644 --- a/mozilla/layout/html/base/src/nsPlaceholderFrame.cpp +++ b/mozilla/layout/html/base/src/nsPlaceholderFrame.cpp @@ -92,12 +92,6 @@ nsPlaceholderFrame::GetType() const return nsLayoutAtoms::placeholderFrame; } -/* virtual */ PRBool -nsPlaceholderFrame::IsEmpty() -{ - return PR_TRUE; -} - #ifdef DEBUG NS_IMETHODIMP nsPlaceholderFrame::Paint(nsPresContext* aPresContext, diff --git a/mozilla/layout/html/base/src/nsPlaceholderFrame.h b/mozilla/layout/html/base/src/nsPlaceholderFrame.h index d48784a4aae..c23de746f0d 100644 --- a/mozilla/layout/html/base/src/nsPlaceholderFrame.h +++ b/mozilla/layout/html/base/src/nsPlaceholderFrame.h @@ -85,7 +85,8 @@ public: NS_IMETHOD GetFrameName(nsAString& aResult) const; #endif - virtual PRBool IsEmpty(); + virtual PRBool IsEmpty() { return PR_TRUE; } + virtual PRBool IsFrameEmpty() { return PR_TRUE; } protected: nsIFrame* mOutOfFlowFrame; diff --git a/mozilla/layout/html/base/src/nsTextFrame.cpp b/mozilla/layout/html/base/src/nsTextFrame.cpp index d5f266dd2a9..8c70b11a4b7 100644 --- a/mozilla/layout/html/base/src/nsTextFrame.cpp +++ b/mozilla/layout/html/base/src/nsTextFrame.cpp @@ -483,6 +483,7 @@ public: PRBool* aIsVisible); virtual PRBool IsEmpty(); + virtual PRBool IsFrameEmpty() { return IsEmpty(); } #ifdef ACCESSIBILITY NS_IMETHOD GetAccessible(nsIAccessible** aAccessible); diff --git a/mozilla/layout/html/base/src/nsViewportFrame.cpp b/mozilla/layout/html/base/src/nsViewportFrame.cpp index dcddc6712f9..c2538916a2f 100644 --- a/mozilla/layout/html/base/src/nsViewportFrame.cpp +++ b/mozilla/layout/html/base/src/nsViewportFrame.cpp @@ -295,24 +295,18 @@ ViewportFrame::Reflow(nsPresContext* aPresContext, command->GetType(reflowType); } - PRBool wasHandled = PR_FALSE; if (reflowType != eReflowType_UserDefined && aReflowState.reason == eReflowReason_Incremental) { // Incremental reflow - rv = mFixedContainer.IncrementalReflow(this, aPresContext, reflowState, - reflowState.mComputedWidth, - reflowState.mComputedHeight, - wasHandled); + mFixedContainer.IncrementalReflow(this, aPresContext, reflowState, + reflowState.mComputedWidth, + reflowState.mComputedHeight); } - if (!wasHandled) { - // It's the initial reflow or some other non-incremental reflow or - // IncrementalReflow() didn't handle it. Just reflow all the - // fixed-pos frames. - rv = mFixedContainer.Reflow(this, aPresContext, reflowState, - reflowState.mComputedWidth, - reflowState.mComputedHeight); - } + // Just reflow all the fixed-pos frames. + rv = mFixedContainer.Reflow(this, aPresContext, reflowState, + reflowState.mComputedWidth, + reflowState.mComputedHeight); // If this is an initial reflow, resize reflow, or style change reflow // then do a repaint