From b83e1077b0f9bbfa0d96ae0c947a02b06e7221f9 Mon Sep 17 00:00:00 2001 From: troy Date: Mon, 11 May 1998 18:38:10 +0000 Subject: [PATCH] More incremental reflow work. git-svn-id: svn://10.0.0.236/trunk@1383 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/layout/base/public/nsIFrame.h | 3 +- mozilla/layout/base/src/nsFrame.cpp | 32 ++- mozilla/layout/base/src/nsPresShell.cpp | 4 - mozilla/layout/base/src/nsReflowCommand.h | 9 +- mozilla/layout/generic/nsBlockFrame.cpp | 4 +- mozilla/layout/generic/nsBlockReflowState.cpp | 4 +- mozilla/layout/generic/nsBlockReflowState.h | 4 +- mozilla/layout/generic/nsIFrame.h | 3 +- mozilla/layout/generic/nsInlineFrame.cpp | 230 +++++++++++++++--- .../layout/html/base/src/deadnsInlineFrame.h | 21 +- mozilla/layout/html/base/src/nsBlockFrame.cpp | 4 +- .../html/base/src/nsBlockReflowState.cpp | 4 +- .../layout/html/base/src/nsBlockReflowState.h | 4 +- mozilla/layout/html/base/src/nsBodyFrame.cpp | 10 + .../layout/html/base/src/nsInlineFrame.cpp | 230 +++++++++++++++--- 15 files changed, 462 insertions(+), 104 deletions(-) diff --git a/mozilla/layout/base/public/nsIFrame.h b/mozilla/layout/base/public/nsIFrame.h index b254978b89d..b762b19a924 100644 --- a/mozilla/layout/base/public/nsIFrame.h +++ b/mozilla/layout/base/public/nsIFrame.h @@ -319,8 +319,7 @@ public: /** * This call is invoked when content is changed in the content tree. * The first frame that maps that content is asked to deal with the - * change by generating an incremental reflow command which will be - * to reflow the frame tree. + * change by generating an incremental reflow command. * * @param aIndexInParent the index in the content container where * the new content was deleted. diff --git a/mozilla/layout/base/src/nsFrame.cpp b/mozilla/layout/base/src/nsFrame.cpp index 05e1120d649..e1f648e44b9 100644 --- a/mozilla/layout/base/src/nsFrame.cpp +++ b/mozilla/layout/base/src/nsFrame.cpp @@ -958,13 +958,24 @@ NS_METHOD nsFrame::IncrementalReflow(nsIPresContext* aPresContext, nsReflowCommand& aReflowCommand, ReflowStatus& aStatus) { - NS_ERROR("not a reflow command handler"); - aDesiredSize.width = 0; - aDesiredSize.height = 0; - aDesiredSize.ascent = 0; - aDesiredSize.descent = 0; - aStatus = frComplete; - return NS_ERROR_NOT_IMPLEMENTED; + NS_PRECONDITION(aReflowCommand.GetTarget() == this, "bad target"); + + // The only type of incremental reflow command we expect to get is a + // content changed reflow command + if (aReflowCommand.GetType() == nsReflowCommand.ContentChanged) { + // Generic response is to reflow the child + return ResizeReflow(aPresContext, aDesiredSize, aMaxSize, nsnull, aStatus); + + } else { + NS_ERROR("not a container reflow command handler"); + aDesiredSize.width = 0; + aDesiredSize.height = 0; + aDesiredSize.ascent = 0; + aDesiredSize.descent = 0; + aStatus = frComplete; + return NS_ERROR_NOT_IMPLEMENTED; + } + } NS_METHOD nsFrame::ContentAppended(nsIPresShell* aShell, @@ -1007,10 +1018,9 @@ NS_METHOD nsFrame::ContentChanged(nsIPresShell* aShell, nsIContent* aChild, nsISupports* aSubContent) { - // Generate a reflow command with our geometric parent as the target, - // and us as the child frame - nsReflowCommand* cmd = new nsReflowCommand(aPresContext, mGeometricParent, - nsReflowCommand::ContentChanged, this); + // Generate a reflow command with this frame as the target frame + nsReflowCommand* cmd = new nsReflowCommand(aPresContext, this, + nsReflowCommand::ContentChanged); aShell->AppendReflowCommand(cmd); return NS_OK; } diff --git a/mozilla/layout/base/src/nsPresShell.cpp b/mozilla/layout/base/src/nsPresShell.cpp index 566f8e5caa5..58b55b7a248 100644 --- a/mozilla/layout/base/src/nsPresShell.cpp +++ b/mozilla/layout/base/src/nsPresShell.cpp @@ -525,16 +525,12 @@ PresShell::ContentChanged(nsIContent* aContent, { NS_PRECONDITION(nsnull != mRootFrame, "null root frame"); -#if XXX_enable_once_content_changed_is_limping // Notify the first frame that maps the content. It will generate a reflow // command nsIFrame* frame = FindFrameWithContent(aContent); NS_PRECONDITION(nsnull != frame, "null frame"); frame->ContentChanged(this, mPresContext, aContent, aSubContent); ProcessReflowCommands(); -#else - mResizeReflows++; -#endif } void diff --git a/mozilla/layout/base/src/nsReflowCommand.h b/mozilla/layout/base/src/nsReflowCommand.h index 3e92064624c..472a7c4460d 100644 --- a/mozilla/layout/base/src/nsReflowCommand.h +++ b/mozilla/layout/base/src/nsReflowCommand.h @@ -29,13 +29,16 @@ class nsReflowCommand { public: enum ReflowType { // Reflow commands generated in response to a content insert/delete/append - // notification + // notification. The target of the reflow command is the container frame + // itself FrameAppended, FrameInserted, FrameDeleted, - // This reflow command is used when a leaf node's content changes - // (e.g. some text in a text run, an image's source, etc.) + // This reflow command is used when a leaf node's content changes (e.g. some + // text in a text run, an image's source, etc.). The target of the reflow + // command is the frame that changed (see nsIFrame#ContentChanged() for how + // the target frame is determined). ContentChanged, // When an incremental reflow operation affects a next-in-flow, diff --git a/mozilla/layout/generic/nsBlockFrame.cpp b/mozilla/layout/generic/nsBlockFrame.cpp index b85ee3f7b01..99c9a1fcd0d 100644 --- a/mozilla/layout/generic/nsBlockFrame.cpp +++ b/mozilla/layout/generic/nsBlockFrame.cpp @@ -1368,7 +1368,7 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, else { rv = ReflowUnmapped(state); } - +#if 0 } else if (aReflowCommand.GetType() == nsReflowCommand::ContentChanged) { // Restore our state as if the child that changed is the next frame to reflow nsLineData* line = FindLine(aReflowCommand.GetChildFrame()); @@ -1380,7 +1380,7 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, // Reflow the affected line, and all the lines that follow... // XXX Obviously this needs to be more efficient rv = ReflowMappedFrom(state, line); - +#endif } else { NS_NOTYETIMPLEMENTED("unexpected reflow command"); } diff --git a/mozilla/layout/generic/nsBlockReflowState.cpp b/mozilla/layout/generic/nsBlockReflowState.cpp index b85ee3f7b01..99c9a1fcd0d 100644 --- a/mozilla/layout/generic/nsBlockReflowState.cpp +++ b/mozilla/layout/generic/nsBlockReflowState.cpp @@ -1368,7 +1368,7 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, else { rv = ReflowUnmapped(state); } - +#if 0 } else if (aReflowCommand.GetType() == nsReflowCommand::ContentChanged) { // Restore our state as if the child that changed is the next frame to reflow nsLineData* line = FindLine(aReflowCommand.GetChildFrame()); @@ -1380,7 +1380,7 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, // Reflow the affected line, and all the lines that follow... // XXX Obviously this needs to be more efficient rv = ReflowMappedFrom(state, line); - +#endif } else { NS_NOTYETIMPLEMENTED("unexpected reflow command"); } diff --git a/mozilla/layout/generic/nsBlockReflowState.h b/mozilla/layout/generic/nsBlockReflowState.h index b85ee3f7b01..99c9a1fcd0d 100644 --- a/mozilla/layout/generic/nsBlockReflowState.h +++ b/mozilla/layout/generic/nsBlockReflowState.h @@ -1368,7 +1368,7 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, else { rv = ReflowUnmapped(state); } - +#if 0 } else if (aReflowCommand.GetType() == nsReflowCommand::ContentChanged) { // Restore our state as if the child that changed is the next frame to reflow nsLineData* line = FindLine(aReflowCommand.GetChildFrame()); @@ -1380,7 +1380,7 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, // Reflow the affected line, and all the lines that follow... // XXX Obviously this needs to be more efficient rv = ReflowMappedFrom(state, line); - +#endif } else { NS_NOTYETIMPLEMENTED("unexpected reflow command"); } diff --git a/mozilla/layout/generic/nsIFrame.h b/mozilla/layout/generic/nsIFrame.h index b254978b89d..b762b19a924 100644 --- a/mozilla/layout/generic/nsIFrame.h +++ b/mozilla/layout/generic/nsIFrame.h @@ -319,8 +319,7 @@ public: /** * This call is invoked when content is changed in the content tree. * The first frame that maps that content is asked to deal with the - * change by generating an incremental reflow command which will be - * to reflow the frame tree. + * change by generating an incremental reflow command. * * @param aIndexInParent the index in the content container where * the new content was deleted. diff --git a/mozilla/layout/generic/nsInlineFrame.cpp b/mozilla/layout/generic/nsInlineFrame.cpp index 073a20683ad..d5af90ea4af 100644 --- a/mozilla/layout/generic/nsInlineFrame.cpp +++ b/mozilla/layout/generic/nsInlineFrame.cpp @@ -29,6 +29,7 @@ #include "nsHTMLAtoms.h" #include "nsAbsoluteFrame.h" #include "nsLeafFrame.h" +#include "nsIPtr.h" // XXX To Do: // 2. horizontal child margins @@ -49,6 +50,10 @@ static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID); static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID); static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID); +NS_DEF_PTR(nsIStyleContext); +NS_DEF_PTR(nsIContent); +NS_DEF_PTR(nsIContentDelegate); + class nsInlineState { public: @@ -192,18 +197,22 @@ void nsInlineFrame::PlaceChild(nsIFrame* aChild, * * @param aPresContext presentation context to use * @param aState current inline state + * @param aChildFrame the first child frame to reflow + * @param aChildIndex the first child frame's index in the frame list * @return true if we successfully reflowed all the mapped children and false * otherwise, e.g. we pushed children to the next in flow */ -PRBool nsInlineFrame::ReflowMappedChildren(nsIPresContext* aPresContext, - nsInlineState& aState) +PRBool nsInlineFrame::ReflowMappedChildrenFrom(nsIPresContext* aPresContext, + nsInlineState& aState, + nsIFrame* aChildFrame, + PRInt32 aChildIndex) { #ifdef NS_DEBUG VerifyLastIsComplete(); #endif - NS_PRECONDITION(nsnull != mFirstChild, "no children"); + NS_PRECONDITION(nsnull != aChildFrame, "no children"); - PRInt32 childCount = 0; + PRInt32 childCount = aChildIndex; nsIFrame* prevKidFrame = nsnull; // Remember our original mLastContentIsComplete so that if we end up @@ -215,7 +224,7 @@ PRBool nsInlineFrame::ReflowMappedChildren(nsIPresContext* aPresContext, nsSize* pKidMaxElementSize = (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull; PRBool result = PR_TRUE; - for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) { + for (nsIFrame* kidFrame = aChildFrame; nsnull != kidFrame; ) { nsReflowMetrics kidSize; ReflowStatus status; @@ -259,11 +268,10 @@ PRBool nsInlineFrame::ReflowMappedChildren(nsIPresContext* aPresContext, // frame. This hooks the child into the flow nsIFrame* continuingFrame; - nsIStyleContext* kidSC; - kidFrame->GetStyleContext(aPresContext, kidSC); + nsIStyleContextPtr kidSC; + kidFrame->GetStyleContext(aPresContext, kidSC.AssignRef()); kidFrame->CreateContinuingFrame(aPresContext, this, kidSC, continuingFrame); - NS_RELEASE(kidSC); NS_ASSERTION(nsnull != continuingFrame, "frame creation failed"); // Add the continuing frame to the sibling list @@ -447,11 +455,10 @@ PRBool nsInlineFrame::PullUpChildren(nsIPresContext* aPresContext, // prepares it for reflow. nsIFrame* continuingFrame; - nsIStyleContext* kidSC; - kidFrame->GetStyleContext(aPresContext, kidSC); + nsIStyleContextPtr kidSC; + kidFrame->GetStyleContext(aPresContext, kidSC.AssignRef()); kidFrame->CreateContinuingFrame(aPresContext, this, kidSC, continuingFrame); - NS_RELEASE(kidSC); NS_ASSERTION(nsnull != continuingFrame, "frame creation failed"); // Add the continuing frame to our sibling list and then push @@ -554,7 +561,7 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, LastChild(prevKidFrame); for (;;) { // Get the next content object - nsIContent* kid = mContent->ChildAt(kidIndex); + nsIContentPtr kid = mContent->ChildAt(kidIndex); if (nsnull == kid) { result = frComplete; break; @@ -563,13 +570,11 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, // Make sure we still have room left if (aState.availSize.width <= 0) { // Note: return status was set to frNotComplete above... - NS_RELEASE(kid); break; } // Resolve style for the child - nsIStyleContext* kidStyleContext = - aPresContext->ResolveStyleContextFor(kid, this); + nsIStyleContextPtr kidStyleContext = aPresContext->ResolveStyleContextFor(kid, this); // Figure out how we should treat the child nsIFrame* kidFrame; @@ -591,15 +596,13 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, kidFrame->SetStyleContext(aPresContext, kidStyleContext); } } else if (nsnull == kidPrevInFlow) { - nsIContentDelegate* kidDel; + nsIContentDelegatePtr kidDel; switch (kidDisplay->mDisplay) { case NS_STYLE_DISPLAY_BLOCK: case NS_STYLE_DISPLAY_LIST_ITEM: if (kidIndex != mFirstContentOffset) { // We don't allow block elements to be placed in us anywhere // other than at our left margin. - NS_RELEASE(kidStyleContext); - NS_RELEASE(kid); goto done; } // FALLTHROUGH @@ -608,7 +611,6 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, kidDel = kid->GetDelegate(aPresContext); rv = kidDel->CreateFrame(aPresContext, kid, this, kidStyleContext, kidFrame); - NS_RELEASE(kidDel); break; default: @@ -623,8 +625,6 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, rv = kidPrevInFlow->CreateContinuingFrame(aPresContext, this, kidStyleContext, kidFrame); } - NS_RELEASE(kid); - NS_RELEASE(kidStyleContext); // Try to reflow the child into the available space. It might not // fit or might need continuing. @@ -732,7 +732,7 @@ NS_METHOD nsInlineFrame::ResizeReflow(nsIPresContext* aPresContext, // Reflow any existing frames if (nsnull != mFirstChild) { - reflowMappedOK = ReflowMappedChildren(aPresContext, state); + reflowMappedOK = ReflowMappedChildrenFrom(aPresContext, state, mFirstChild, 0); if (PR_FALSE == reflowMappedOK) { aStatus = frNotComplete; @@ -854,14 +854,14 @@ NS_METHOD nsInlineFrame::GetReflowMetrics(nsIPresContext* aPresContext, * of aSkipChild in our list of children. * If aSkipChild is nsnull then resets the state for appended content. */ -PRIntn nsInlineFrame::RecoverState(nsIPresContext* aPresContext, - nsInlineState& aState, - nsIFrame* aSkipChild) +PRInt32 nsInlineFrame::RecoverState(nsIPresContext* aPresContext, + nsInlineState& aState, + nsIFrame* aSkipChild) { // Get ascent & descent info for all the children up to but not // including aSkipChild. Also compute the x coordinate for where // aSkipChild will be place after it is reflowed. - PRIntn i = 0; + PRInt32 i = 0; nsIFrame* kid = mFirstChild; nscoord x = aState.x; nscoord maxAscent = 0; @@ -885,6 +885,78 @@ PRIntn nsInlineFrame::RecoverState(nsIPresContext* aPresContext, return i; } +// XXX We need to return information about whether our next-in-flow is +// dirty... +nsIFrame::ReflowStatus +nsInlineFrame::IncrementalReflowFrom(nsIPresContext* aPresContext, + nsInlineState& aState, + nsIFrame* aChildFrame, + PRInt32 aChildIndex) +{ + ReflowStatus status = frComplete; + + // Just reflow all the mapped children starting with childFrame. + // XXX This isn't the optimal thing to do... + if (ReflowMappedChildrenFrom(aPresContext, aState, aChildFrame, aChildIndex)) { + if (NextChildOffset() < mContent->ChildCount()) { + // Any space left? + if (aState.availSize.width <= 0) { + // No space left. Don't try to pull-up children + status = frNotComplete; + } else { + // Try and pull-up some children from a next-in-flow + if (!PullUpChildren(aPresContext, aState)) { + // We were not able to pull-up all the child frames from our + // next-in-flow + status = frNotComplete; + } + } + } + } else { + // We were unable to reflow all our mapped frames + status = frNotComplete; + } + + return status; +} + +nsIFrame::ReflowStatus +nsInlineFrame::IncrementalReflowAfter(nsIPresContext* aPresContext, + nsInlineState& aState, + nsIFrame* aChildFrame, + PRInt32 aChildIndex) +{ + ReflowStatus status = frComplete; + nsIFrame* nextFrame; + + aChildFrame->GetNextSibling(nextFrame); + + // Just reflow all the remaining mapped children + // XXX This isn't the optimal thing to do... + if ((nsnull == nextFrame) || + ReflowMappedChildrenFrom(aPresContext, aState, nextFrame, aChildIndex + 1)) { + if (NextChildOffset() < mContent->ChildCount()) { + // Any space left? + if (aState.availSize.width <= 0) { + // No space left. Don't try to pull-up children + status = frNotComplete; + } else { + // Try and pull-up some children from a next-in-flow + if (!PullUpChildren(aPresContext, aState)) { + // We were not able to pull-up all the child frames from our + // next-in-flow + status = frNotComplete; + } + } + } + } else { + // We were unable to reflow all our mapped frames + status = frNotComplete; + } + + return status; +} + NS_METHOD nsInlineFrame::IncrementalReflow(nsIPresContext* aPresContext, nsReflowMetrics& aDesiredSize, const nsSize& aMaxSize, @@ -899,7 +971,9 @@ NS_METHOD nsInlineFrame::IncrementalReflow(nsIPresContext* aPresContext, nsStyleSpacing* styleSpacing = (nsStyleSpacing*)mStyleContext->GetData(kStyleSpacingSID); - nscoord lineHeight; + nscoord lineHeight; + nsIFrame* kidFrame; + PRInt32 kidIndex; nsInlineState state(styleFont, styleSpacing, aMaxSize, nsnull); InitializeState(aPresContext, state); @@ -913,22 +987,112 @@ NS_METHOD nsInlineFrame::IncrementalReflow(nsIPresContext* aPresContext, aStatus = ReflowUnmappedChildren(aPresContext, state); // Vertically align the children - lineHeight = - nsCSSLayout::VerticallyAlignChildren(aPresContext, this, styleFont, - styleSpacing->mBorderPadding.top, - mFirstChild, mChildCount, - state.ascents, state.maxAscent); + lineHeight = nsCSSLayout::VerticallyAlignChildren(aPresContext, this, + styleFont, styleSpacing->mBorderPadding.top, mFirstChild, + mChildCount, state.ascents, state.maxAscent); ComputeFinalSize(aPresContext, state, aDesiredSize); break; +#if 0 + case nsReflowCommand::ContentChanged: + // Recover our state + kidFrame = aReflowCommand.GetChildFrame(); + kidIndex = RecoverState(aPresContext, state, kidFrame); + aStatus = IncrementalReflowFrom(aPresContext, state, kidFrame, kidIndex); + + // Vertically align the children + lineHeight = nsCSSLayout::VerticallyAlignChildren(aPresContext, this, + styleFont, styleSpacing->mBorderPadding.top, mFirstChild, + mChildCount, state.ascents, state.maxAscent); + + ComputeFinalSize(aPresContext, state, aDesiredSize); + break; +#endif + default: NS_NOTYETIMPLEMENTED("unexpected reflow command"); break; } } else { - NS_NOTYETIMPLEMENTED("unexpected reflow command"); + // The command is passing through us. Get the next frame in the reflow chain + nsIFrame* kidFrame = aReflowCommand.GetNext(); + nsReflowMetrics kidSize; + + // Restore our state as if nextFrame is the next frame to reflow + kidIndex = RecoverState(aPresContext, state, kidFrame); + + // Reflow the child into the available space + aStatus = aReflowCommand.Next(kidSize, state.availSize, kidFrame); + + // Did the child fit? + if ((kidSize.width > state.availSize.width) && (kidFrame != mFirstChild)) { + nsIFrame* prevFrame; + + // The child is too wide to fit in the available space, and it's not our + // first child + PrevChild(kidFrame, prevFrame); + PushChildren(kidFrame, prevFrame, mLastContentIsComplete); + SetLastContentOffset(prevFrame); + mChildCount = kidIndex - 1; + aStatus = frNotComplete; + + } else { + // Place and size the child + PlaceChild(kidFrame, kidIndex, state, kidSize, nsnull); + + nsIFrame* kidNextInFlow; + kidFrame->GetNextInFlow(kidNextInFlow); + + // Is the child complete? + if (frComplete == aStatus) { + // Check whether the frame has next-in-flow(s) that are no longer needed + if (nsnull != kidNextInFlow) { + // Remove the next-in-flow(s) + DeleteChildsNextInFlow(kidFrame); + } + + // Adjust the frames that follow + aStatus = IncrementalReflowAfter(aPresContext, state, kidFrame, kidIndex); + + } else { + nsIFrame* nextSibling; + + // No, the child isn't complete + if (nsnull == kidNextInFlow) { + // The child doesn't have a next-in-flow so create a continuing + // frame. + nsIFrame* continuingFrame; + + nsIStyleContextPtr kidSC; + kidFrame->GetStyleContext(aPresContext, kidSC.AssignRef()); + kidFrame->CreateContinuingFrame(aPresContext, this, kidSC, continuingFrame); + + // Link the child into the sibling list + kidFrame->GetNextSibling(nextSibling); + continuingFrame->SetNextSibling(nextSibling); + kidFrame->SetNextSibling(continuingFrame); + } + + // We've used up all of our available space, so push the remaining + // children to the next-in-flow + kidFrame->GetNextSibling(nextSibling); + if (nsnull != nextSibling) { + PushChildren(nextSibling, kidFrame, mLastContentIsComplete); + } + + SetLastContentOffset(kidFrame); + mChildCount = kidIndex; + } + } + + // Vertically align the children + lineHeight = nsCSSLayout::VerticallyAlignChildren(aPresContext, this, + styleFont, styleSpacing->mBorderPadding.top, mFirstChild, + mChildCount, state.ascents, state.maxAscent); + + ComputeFinalSize(aPresContext, state, aDesiredSize); } return NS_OK; } diff --git a/mozilla/layout/html/base/src/deadnsInlineFrame.h b/mozilla/layout/html/base/src/deadnsInlineFrame.h index e51cf436826..683358e8208 100644 --- a/mozilla/layout/html/base/src/deadnsInlineFrame.h +++ b/mozilla/layout/html/base/src/deadnsInlineFrame.h @@ -59,8 +59,10 @@ protected: nsInlineState& aState, nsReflowMetrics& aSize); - PRBool ReflowMappedChildren(nsIPresContext* aPresContext, - nsInlineState& aState); + PRBool ReflowMappedChildrenFrom(nsIPresContext* aPresContext, + nsInlineState& aState, + nsIFrame* aChildFrame, + PRInt32 aChildIndex); PRBool PullUpChildren(nsIPresContext* aPresContext, nsInlineState& aState); @@ -74,8 +76,19 @@ protected: const nsReflowMetrics& aChildSize, const nsSize* aChildMaxElementSize); - PRIntn RecoverState(nsIPresContext* aCX, nsInlineState& aState, - nsIFrame* aSkipChild); + PRInt32 RecoverState(nsIPresContext* aCX, + nsInlineState& aState, + nsIFrame* aSkipChild); + + ReflowStatus IncrementalReflowFrom(nsIPresContext* aPresContext, + nsInlineState& aState, + nsIFrame* aChildFrame, + PRInt32 aChildIndex); + + ReflowStatus IncrementalReflowAfter(nsIPresContext* aPresContext, + nsInlineState& aState, + nsIFrame* aChildFrame, + PRInt32 aChildIndex); ReflowStatus AdjustChildren(nsIPresContext* aPresContext, nsReflowMetrics& aDesiredSize, diff --git a/mozilla/layout/html/base/src/nsBlockFrame.cpp b/mozilla/layout/html/base/src/nsBlockFrame.cpp index b85ee3f7b01..99c9a1fcd0d 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.cpp +++ b/mozilla/layout/html/base/src/nsBlockFrame.cpp @@ -1368,7 +1368,7 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, else { rv = ReflowUnmapped(state); } - +#if 0 } else if (aReflowCommand.GetType() == nsReflowCommand::ContentChanged) { // Restore our state as if the child that changed is the next frame to reflow nsLineData* line = FindLine(aReflowCommand.GetChildFrame()); @@ -1380,7 +1380,7 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, // Reflow the affected line, and all the lines that follow... // XXX Obviously this needs to be more efficient rv = ReflowMappedFrom(state, line); - +#endif } else { NS_NOTYETIMPLEMENTED("unexpected reflow command"); } diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.cpp b/mozilla/layout/html/base/src/nsBlockReflowState.cpp index b85ee3f7b01..99c9a1fcd0d 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.cpp +++ b/mozilla/layout/html/base/src/nsBlockReflowState.cpp @@ -1368,7 +1368,7 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, else { rv = ReflowUnmapped(state); } - +#if 0 } else if (aReflowCommand.GetType() == nsReflowCommand::ContentChanged) { // Restore our state as if the child that changed is the next frame to reflow nsLineData* line = FindLine(aReflowCommand.GetChildFrame()); @@ -1380,7 +1380,7 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, // Reflow the affected line, and all the lines that follow... // XXX Obviously this needs to be more efficient rv = ReflowMappedFrom(state, line); - +#endif } else { NS_NOTYETIMPLEMENTED("unexpected reflow command"); } diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.h b/mozilla/layout/html/base/src/nsBlockReflowState.h index b85ee3f7b01..99c9a1fcd0d 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.h +++ b/mozilla/layout/html/base/src/nsBlockReflowState.h @@ -1368,7 +1368,7 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, else { rv = ReflowUnmapped(state); } - +#if 0 } else if (aReflowCommand.GetType() == nsReflowCommand::ContentChanged) { // Restore our state as if the child that changed is the next frame to reflow nsLineData* line = FindLine(aReflowCommand.GetChildFrame()); @@ -1380,7 +1380,7 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, // Reflow the affected line, and all the lines that follow... // XXX Obviously this needs to be more efficient rv = ReflowMappedFrom(state, line); - +#endif } else { NS_NOTYETIMPLEMENTED("unexpected reflow command"); } diff --git a/mozilla/layout/html/base/src/nsBodyFrame.cpp b/mozilla/layout/html/base/src/nsBodyFrame.cpp index fd4f985d29c..4346cf97872 100644 --- a/mozilla/layout/html/base/src/nsBodyFrame.cpp +++ b/mozilla/layout/html/base/src/nsBodyFrame.cpp @@ -251,6 +251,14 @@ NS_METHOD nsBodyFrame::IncrementalReflow(nsIPresContext* aPresContext, nsReflowCommand& aReflowCommand, ReflowStatus& aStatus) { + // XXX Currently there's a bug that when the body background image dimension + // is resolved we're treating that as a content change rather than just repainting + // the body... + if (aReflowCommand.GetTarget() == this) { + NS_ASSERTION(aReflowCommand.GetType() == nsReflowCommand::ContentChanged, "unexpected type"); + return ResizeReflow(aPresContext, aDesiredSize, aMaxSize, nsnull, aStatus); + } + // Get our border/padding info nsStyleSpacing* mySpacing = (nsStyleSpacing*)mStyleContext->GetData(kStyleSpacingSID); @@ -265,8 +273,10 @@ NS_METHOD nsBodyFrame::IncrementalReflow(nsIPresContext* aPresContext, mSpaceManager->Translate(leftInset, topInset); +#if 0 // The reflow command should never be target for us NS_ASSERTION(aReflowCommand.GetTarget() != this, "bad reflow command target"); +#endif // Compute the child frame's max size nsSize columnMaxSize = GetColumnAvailSpace(aPresContext, mySpacing, diff --git a/mozilla/layout/html/base/src/nsInlineFrame.cpp b/mozilla/layout/html/base/src/nsInlineFrame.cpp index 073a20683ad..d5af90ea4af 100644 --- a/mozilla/layout/html/base/src/nsInlineFrame.cpp +++ b/mozilla/layout/html/base/src/nsInlineFrame.cpp @@ -29,6 +29,7 @@ #include "nsHTMLAtoms.h" #include "nsAbsoluteFrame.h" #include "nsLeafFrame.h" +#include "nsIPtr.h" // XXX To Do: // 2. horizontal child margins @@ -49,6 +50,10 @@ static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID); static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID); static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID); +NS_DEF_PTR(nsIStyleContext); +NS_DEF_PTR(nsIContent); +NS_DEF_PTR(nsIContentDelegate); + class nsInlineState { public: @@ -192,18 +197,22 @@ void nsInlineFrame::PlaceChild(nsIFrame* aChild, * * @param aPresContext presentation context to use * @param aState current inline state + * @param aChildFrame the first child frame to reflow + * @param aChildIndex the first child frame's index in the frame list * @return true if we successfully reflowed all the mapped children and false * otherwise, e.g. we pushed children to the next in flow */ -PRBool nsInlineFrame::ReflowMappedChildren(nsIPresContext* aPresContext, - nsInlineState& aState) +PRBool nsInlineFrame::ReflowMappedChildrenFrom(nsIPresContext* aPresContext, + nsInlineState& aState, + nsIFrame* aChildFrame, + PRInt32 aChildIndex) { #ifdef NS_DEBUG VerifyLastIsComplete(); #endif - NS_PRECONDITION(nsnull != mFirstChild, "no children"); + NS_PRECONDITION(nsnull != aChildFrame, "no children"); - PRInt32 childCount = 0; + PRInt32 childCount = aChildIndex; nsIFrame* prevKidFrame = nsnull; // Remember our original mLastContentIsComplete so that if we end up @@ -215,7 +224,7 @@ PRBool nsInlineFrame::ReflowMappedChildren(nsIPresContext* aPresContext, nsSize* pKidMaxElementSize = (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull; PRBool result = PR_TRUE; - for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) { + for (nsIFrame* kidFrame = aChildFrame; nsnull != kidFrame; ) { nsReflowMetrics kidSize; ReflowStatus status; @@ -259,11 +268,10 @@ PRBool nsInlineFrame::ReflowMappedChildren(nsIPresContext* aPresContext, // frame. This hooks the child into the flow nsIFrame* continuingFrame; - nsIStyleContext* kidSC; - kidFrame->GetStyleContext(aPresContext, kidSC); + nsIStyleContextPtr kidSC; + kidFrame->GetStyleContext(aPresContext, kidSC.AssignRef()); kidFrame->CreateContinuingFrame(aPresContext, this, kidSC, continuingFrame); - NS_RELEASE(kidSC); NS_ASSERTION(nsnull != continuingFrame, "frame creation failed"); // Add the continuing frame to the sibling list @@ -447,11 +455,10 @@ PRBool nsInlineFrame::PullUpChildren(nsIPresContext* aPresContext, // prepares it for reflow. nsIFrame* continuingFrame; - nsIStyleContext* kidSC; - kidFrame->GetStyleContext(aPresContext, kidSC); + nsIStyleContextPtr kidSC; + kidFrame->GetStyleContext(aPresContext, kidSC.AssignRef()); kidFrame->CreateContinuingFrame(aPresContext, this, kidSC, continuingFrame); - NS_RELEASE(kidSC); NS_ASSERTION(nsnull != continuingFrame, "frame creation failed"); // Add the continuing frame to our sibling list and then push @@ -554,7 +561,7 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, LastChild(prevKidFrame); for (;;) { // Get the next content object - nsIContent* kid = mContent->ChildAt(kidIndex); + nsIContentPtr kid = mContent->ChildAt(kidIndex); if (nsnull == kid) { result = frComplete; break; @@ -563,13 +570,11 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, // Make sure we still have room left if (aState.availSize.width <= 0) { // Note: return status was set to frNotComplete above... - NS_RELEASE(kid); break; } // Resolve style for the child - nsIStyleContext* kidStyleContext = - aPresContext->ResolveStyleContextFor(kid, this); + nsIStyleContextPtr kidStyleContext = aPresContext->ResolveStyleContextFor(kid, this); // Figure out how we should treat the child nsIFrame* kidFrame; @@ -591,15 +596,13 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, kidFrame->SetStyleContext(aPresContext, kidStyleContext); } } else if (nsnull == kidPrevInFlow) { - nsIContentDelegate* kidDel; + nsIContentDelegatePtr kidDel; switch (kidDisplay->mDisplay) { case NS_STYLE_DISPLAY_BLOCK: case NS_STYLE_DISPLAY_LIST_ITEM: if (kidIndex != mFirstContentOffset) { // We don't allow block elements to be placed in us anywhere // other than at our left margin. - NS_RELEASE(kidStyleContext); - NS_RELEASE(kid); goto done; } // FALLTHROUGH @@ -608,7 +611,6 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, kidDel = kid->GetDelegate(aPresContext); rv = kidDel->CreateFrame(aPresContext, kid, this, kidStyleContext, kidFrame); - NS_RELEASE(kidDel); break; default: @@ -623,8 +625,6 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, rv = kidPrevInFlow->CreateContinuingFrame(aPresContext, this, kidStyleContext, kidFrame); } - NS_RELEASE(kid); - NS_RELEASE(kidStyleContext); // Try to reflow the child into the available space. It might not // fit or might need continuing. @@ -732,7 +732,7 @@ NS_METHOD nsInlineFrame::ResizeReflow(nsIPresContext* aPresContext, // Reflow any existing frames if (nsnull != mFirstChild) { - reflowMappedOK = ReflowMappedChildren(aPresContext, state); + reflowMappedOK = ReflowMappedChildrenFrom(aPresContext, state, mFirstChild, 0); if (PR_FALSE == reflowMappedOK) { aStatus = frNotComplete; @@ -854,14 +854,14 @@ NS_METHOD nsInlineFrame::GetReflowMetrics(nsIPresContext* aPresContext, * of aSkipChild in our list of children. * If aSkipChild is nsnull then resets the state for appended content. */ -PRIntn nsInlineFrame::RecoverState(nsIPresContext* aPresContext, - nsInlineState& aState, - nsIFrame* aSkipChild) +PRInt32 nsInlineFrame::RecoverState(nsIPresContext* aPresContext, + nsInlineState& aState, + nsIFrame* aSkipChild) { // Get ascent & descent info for all the children up to but not // including aSkipChild. Also compute the x coordinate for where // aSkipChild will be place after it is reflowed. - PRIntn i = 0; + PRInt32 i = 0; nsIFrame* kid = mFirstChild; nscoord x = aState.x; nscoord maxAscent = 0; @@ -885,6 +885,78 @@ PRIntn nsInlineFrame::RecoverState(nsIPresContext* aPresContext, return i; } +// XXX We need to return information about whether our next-in-flow is +// dirty... +nsIFrame::ReflowStatus +nsInlineFrame::IncrementalReflowFrom(nsIPresContext* aPresContext, + nsInlineState& aState, + nsIFrame* aChildFrame, + PRInt32 aChildIndex) +{ + ReflowStatus status = frComplete; + + // Just reflow all the mapped children starting with childFrame. + // XXX This isn't the optimal thing to do... + if (ReflowMappedChildrenFrom(aPresContext, aState, aChildFrame, aChildIndex)) { + if (NextChildOffset() < mContent->ChildCount()) { + // Any space left? + if (aState.availSize.width <= 0) { + // No space left. Don't try to pull-up children + status = frNotComplete; + } else { + // Try and pull-up some children from a next-in-flow + if (!PullUpChildren(aPresContext, aState)) { + // We were not able to pull-up all the child frames from our + // next-in-flow + status = frNotComplete; + } + } + } + } else { + // We were unable to reflow all our mapped frames + status = frNotComplete; + } + + return status; +} + +nsIFrame::ReflowStatus +nsInlineFrame::IncrementalReflowAfter(nsIPresContext* aPresContext, + nsInlineState& aState, + nsIFrame* aChildFrame, + PRInt32 aChildIndex) +{ + ReflowStatus status = frComplete; + nsIFrame* nextFrame; + + aChildFrame->GetNextSibling(nextFrame); + + // Just reflow all the remaining mapped children + // XXX This isn't the optimal thing to do... + if ((nsnull == nextFrame) || + ReflowMappedChildrenFrom(aPresContext, aState, nextFrame, aChildIndex + 1)) { + if (NextChildOffset() < mContent->ChildCount()) { + // Any space left? + if (aState.availSize.width <= 0) { + // No space left. Don't try to pull-up children + status = frNotComplete; + } else { + // Try and pull-up some children from a next-in-flow + if (!PullUpChildren(aPresContext, aState)) { + // We were not able to pull-up all the child frames from our + // next-in-flow + status = frNotComplete; + } + } + } + } else { + // We were unable to reflow all our mapped frames + status = frNotComplete; + } + + return status; +} + NS_METHOD nsInlineFrame::IncrementalReflow(nsIPresContext* aPresContext, nsReflowMetrics& aDesiredSize, const nsSize& aMaxSize, @@ -899,7 +971,9 @@ NS_METHOD nsInlineFrame::IncrementalReflow(nsIPresContext* aPresContext, nsStyleSpacing* styleSpacing = (nsStyleSpacing*)mStyleContext->GetData(kStyleSpacingSID); - nscoord lineHeight; + nscoord lineHeight; + nsIFrame* kidFrame; + PRInt32 kidIndex; nsInlineState state(styleFont, styleSpacing, aMaxSize, nsnull); InitializeState(aPresContext, state); @@ -913,22 +987,112 @@ NS_METHOD nsInlineFrame::IncrementalReflow(nsIPresContext* aPresContext, aStatus = ReflowUnmappedChildren(aPresContext, state); // Vertically align the children - lineHeight = - nsCSSLayout::VerticallyAlignChildren(aPresContext, this, styleFont, - styleSpacing->mBorderPadding.top, - mFirstChild, mChildCount, - state.ascents, state.maxAscent); + lineHeight = nsCSSLayout::VerticallyAlignChildren(aPresContext, this, + styleFont, styleSpacing->mBorderPadding.top, mFirstChild, + mChildCount, state.ascents, state.maxAscent); ComputeFinalSize(aPresContext, state, aDesiredSize); break; +#if 0 + case nsReflowCommand::ContentChanged: + // Recover our state + kidFrame = aReflowCommand.GetChildFrame(); + kidIndex = RecoverState(aPresContext, state, kidFrame); + aStatus = IncrementalReflowFrom(aPresContext, state, kidFrame, kidIndex); + + // Vertically align the children + lineHeight = nsCSSLayout::VerticallyAlignChildren(aPresContext, this, + styleFont, styleSpacing->mBorderPadding.top, mFirstChild, + mChildCount, state.ascents, state.maxAscent); + + ComputeFinalSize(aPresContext, state, aDesiredSize); + break; +#endif + default: NS_NOTYETIMPLEMENTED("unexpected reflow command"); break; } } else { - NS_NOTYETIMPLEMENTED("unexpected reflow command"); + // The command is passing through us. Get the next frame in the reflow chain + nsIFrame* kidFrame = aReflowCommand.GetNext(); + nsReflowMetrics kidSize; + + // Restore our state as if nextFrame is the next frame to reflow + kidIndex = RecoverState(aPresContext, state, kidFrame); + + // Reflow the child into the available space + aStatus = aReflowCommand.Next(kidSize, state.availSize, kidFrame); + + // Did the child fit? + if ((kidSize.width > state.availSize.width) && (kidFrame != mFirstChild)) { + nsIFrame* prevFrame; + + // The child is too wide to fit in the available space, and it's not our + // first child + PrevChild(kidFrame, prevFrame); + PushChildren(kidFrame, prevFrame, mLastContentIsComplete); + SetLastContentOffset(prevFrame); + mChildCount = kidIndex - 1; + aStatus = frNotComplete; + + } else { + // Place and size the child + PlaceChild(kidFrame, kidIndex, state, kidSize, nsnull); + + nsIFrame* kidNextInFlow; + kidFrame->GetNextInFlow(kidNextInFlow); + + // Is the child complete? + if (frComplete == aStatus) { + // Check whether the frame has next-in-flow(s) that are no longer needed + if (nsnull != kidNextInFlow) { + // Remove the next-in-flow(s) + DeleteChildsNextInFlow(kidFrame); + } + + // Adjust the frames that follow + aStatus = IncrementalReflowAfter(aPresContext, state, kidFrame, kidIndex); + + } else { + nsIFrame* nextSibling; + + // No, the child isn't complete + if (nsnull == kidNextInFlow) { + // The child doesn't have a next-in-flow so create a continuing + // frame. + nsIFrame* continuingFrame; + + nsIStyleContextPtr kidSC; + kidFrame->GetStyleContext(aPresContext, kidSC.AssignRef()); + kidFrame->CreateContinuingFrame(aPresContext, this, kidSC, continuingFrame); + + // Link the child into the sibling list + kidFrame->GetNextSibling(nextSibling); + continuingFrame->SetNextSibling(nextSibling); + kidFrame->SetNextSibling(continuingFrame); + } + + // We've used up all of our available space, so push the remaining + // children to the next-in-flow + kidFrame->GetNextSibling(nextSibling); + if (nsnull != nextSibling) { + PushChildren(nextSibling, kidFrame, mLastContentIsComplete); + } + + SetLastContentOffset(kidFrame); + mChildCount = kidIndex; + } + } + + // Vertically align the children + lineHeight = nsCSSLayout::VerticallyAlignChildren(aPresContext, this, + styleFont, styleSpacing->mBorderPadding.top, mFirstChild, + mChildCount, state.ascents, state.maxAscent); + + ComputeFinalSize(aPresContext, state, aDesiredSize); } return NS_OK; }