diff --git a/mozilla/layout/generic/nsBlockFrame.cpp b/mozilla/layout/generic/nsBlockFrame.cpp index 23cd49e92d8..b85ee3f7b01 100644 --- a/mozilla/layout/generic/nsBlockFrame.cpp +++ b/mozilla/layout/generic/nsBlockFrame.cpp @@ -142,6 +142,45 @@ nsBlockReflowState::Initialize(nsIPresContext* aPresContext, return rv; } +// Recover the block reflow state to what it should be if aLine is about +// to be reflowed. aLine should not be nsnull +nsresult nsBlockReflowState::RecoverState(nsLineData* aLine) +{ + NS_PRECONDITION(nsnull != aLine, "null parameter"); + nsLineData* prevLine = aLine->mPrevLine; + + if (nsnull != prevLine) { + // Compute the running y-offset, and the available height + mY = prevLine->mBounds.YMost(); + if (!mUnconstrainedHeight) { + mAvailSize.height -= mY; + } + + // Compute the kid x-most + for (nsLineData* l = prevLine; nsnull != l; l = l->mPrevLine) { + nscoord xmost = l->mBounds.XMost(); + if (xmost > mKidXMost) { + mKidXMost = xmost; + } + } + + // If the previous line is a block, then factor in its bottom margin + if (prevLine->mIsBlock) { + nsStyleSpacing* spacing; + nsIFrame* kid = prevLine->mFirstChild; + + kid->GetStyleData(kStyleSpacingSID, (nsStyleStruct*&)spacing); + if (spacing->mMargin.bottom < 0) { + mPrevMaxNegBottomMargin = -spacing->mMargin.bottom; + } else { + mPrevMaxPosBottomMargin = spacing->mMargin.bottom; + } + } + } + + return NS_OK; +} + //---------------------------------------------------------------------- nsresult @@ -780,6 +819,52 @@ done: return rv; } +// XXX This is a short-term hack. It assumes that the caller has already recovered +// the state, and that some space has been retrieved from the space manager... +nsresult +nsBlockFrame::ReflowMappedFrom(nsBlockReflowState& aState, nsLineData* aLine) +{ + nsresult rv = NS_OK; + + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + while (nsnull != aLine) { + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, aLine); + if (NS_OK != rv) { + goto done; + } + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, aLine); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + + mLastContentOffset = aLine->mLastContentOffset; + mLastContentIsComplete = PRBool(aLine->mLastContentIsComplete); + aLine = aLine->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + nsresult nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState) { @@ -1194,6 +1279,51 @@ nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, return rv; } +nsLineData* nsBlockFrame::FindLine(nsIFrame* aFrame) +{ + // Find the line that contains the aFrame + nsLineData* line = mLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + for (PRInt32 count = line->mChildCount; count > 0; count--) { + if (child == aFrame) { + return line; + } + + child->GetNextSibling(child); + } + + // Move to the next line + line = line->mNextLine; + } + + return nsnull; +} + +nsLineData* nsBlockFrame::LastLine() +{ + nsLineData* lastLine = mLines; + + // Get the last line + if (nsnull != lastLine) { + while (nsnull != lastLine->mNextLine) { + lastLine = lastLine->mNextLine; + } + } + + return lastLine; +} + +nsresult nsBlockFrame::IncrementalReflowAfter(nsBlockReflowState& aState, + nsLineData* aLine, + nsresult aReflowStatus, + const nsRect& aOldBounds) +{ + // Now just reflow all the lines that follow... + // XXX Obviously this needs to be more efficient + return ReflowMappedFrom(aState, aLine->mNextLine); +} + NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, nsISpaceManager* aSpaceManager, @@ -1219,39 +1349,11 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, // Is the reflow command target at us? if (this == aReflowCommand.GetTarget()) { if (aReflowCommand.GetType() == nsReflowCommand::FrameAppended) { - nsLineData* lastLine = mLines; - - // Get the last line - if (nsnull != lastLine) { - while (nsnull != lastLine->mNextLine) { - lastLine = lastLine->mNextLine; - } - } + nsLineData* lastLine = LastLine(); // Restore the state - if ((nsnull != lastLine) && (nsnull != lastLine->mPrevLine)) { - nsLineData* prevLine = lastLine->mPrevLine; - - state.mY = prevLine->mBounds.YMost(); - if (!state.mUnconstrainedHeight) { - state.mAvailSize.height -= state.mY; - } - - // XXX This isn't really right... - state.mKidXMost = mRect.width - state.mBorderPadding.right; - - // If the previous line is a block, then factor in its bottom margin - if (prevLine->mIsBlock) { - nsStyleSpacing* spacing; - nsIFrame* kid = prevLine->mFirstChild; - - kid->GetStyleData(kStyleSpacingSID, (nsStyleStruct*&)spacing); - if (spacing->mMargin.bottom < 0) { - state.mPrevMaxNegBottomMargin = -spacing->mMargin.bottom; - } else { - state.mPrevMaxPosBottomMargin = spacing->mMargin.bottom; - } - } + if (nsnull != lastLine) { + state.RecoverState(lastLine); } // Reflow unmapped children @@ -1267,38 +1369,65 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, rv = ReflowUnmapped(state); } - // Set return status - aStatus = frComplete; - if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { - rv = NS_OK; - aStatus = frNotComplete; - } + } 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()); + state.RecoverState(line); + + // Get some available space to start reflowing with + GetAvailableSpace(state, state.mY); + + // Reflow the affected line, and all the lines that follow... + // XXX Obviously this needs to be more efficient + rv = ReflowMappedFrom(state, line); } else { NS_NOTYETIMPLEMENTED("unexpected reflow command"); } } else { -#if 0 // The command is passing through us. Get the next frame in the reflow chain nsIFrame* nextFrame = aReflowCommand.GetNext(); // Restore our state as if nextFrame is the next frame to reflow - RecoverState(aPresContext, state, nextFrame); + nsLineData* line = FindLine(nextFrame); + state.RecoverState(line); - // Get the current bounds. We'll need this to adjust the frames that follow - nsRect oldBounds; - nextFrame->GetRect(oldBounds); + // Get some available space to start reflowing with + GetAvailableSpace(state, state.mY); - // Pass along the reflow command - nsRect desiredRect; - aStatus = aReflowCommand.Next(aSpaceManager, desiredRect, aMaxSize, nextFrame); -#endif - NS_NOTYETIMPLEMENTED("unexpected reflow command"); + // Reflow the affected line + nsLineLayout lineLayout(state); + + state.mCurrentLine = &lineLayout; + lineLayout.Initialize(state, line); + + // Have the line handle the incremental reflow + nsRect oldBounds = line->mBounds; + rv = lineLayout.IncrementalReflowFromChild(aReflowCommand, nextFrame); + + // Now place the line. It's possible it won't fit + rv = PlaceLine(state, lineLayout, line); + // XXX The way NS_LINE_LAYOUT_COMPLETE is being used is very confusing... + if (NS_LINE_LAYOUT_COMPLETE == rv) { + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + state.mPrevKidFrame = lineLayout.mPrevKidFrame; + + // Now figure out what to do with the frames that follow + rv = IncrementalReflowAfter(state, line, rv, oldBounds); + } } // Return our desired rect ComputeDesiredRect(state, aMaxSize, aDesiredRect); + // Set return status + aStatus = frComplete; + if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { + rv = NS_OK; + aStatus = frNotComplete; + } + // Now that reflow has finished, remove the cached pointer shell->RemoveCachedData(this); NS_RELEASE(shell); diff --git a/mozilla/layout/generic/nsBlockFrame.h b/mozilla/layout/generic/nsBlockFrame.h index 5dc94ca5086..50dd7088088 100644 --- a/mozilla/layout/generic/nsBlockFrame.h +++ b/mozilla/layout/generic/nsBlockFrame.h @@ -33,7 +33,7 @@ class nsBlockFrame; struct nsBandData; struct nsBlockBandData : public nsBandData { - // Trapezoid's used during band processing + // Trapezoids used during band processing nsBandTrapezoid data[12]; // Bounding rect of available space between any left and right floaters @@ -63,6 +63,8 @@ struct nsBlockReflowState { nsSize* aMaxElementSize, nsBlockFrame* aBlock); + nsresult RecoverState(nsLineData* aLine); + nsIPresContext* mPresContext; nsBlockFrame* mBlock; @@ -249,6 +251,14 @@ protected: const nsSize& aMaxSize, nsRect& aDesiredRect); + nsLineData* LastLine(); + nsLineData* FindLine(nsIFrame* aFrame); + + nsresult IncrementalReflowAfter(nsBlockReflowState& aState, + nsLineData* aLine, + nsresult aReflowStatus, + const nsRect& aOldBounds); + void DestroyLines(); void DrainOverflowList(); @@ -278,6 +288,7 @@ protected: nsLineData* aLine); nsresult ReflowMapped(nsBlockReflowState& aState); + nsresult ReflowMappedFrom(nsBlockReflowState& aState, nsLineData* aLine); nsresult ReflowUnmapped(nsBlockReflowState& aState); diff --git a/mozilla/layout/generic/nsBlockReflowState.cpp b/mozilla/layout/generic/nsBlockReflowState.cpp index 23cd49e92d8..b85ee3f7b01 100644 --- a/mozilla/layout/generic/nsBlockReflowState.cpp +++ b/mozilla/layout/generic/nsBlockReflowState.cpp @@ -142,6 +142,45 @@ nsBlockReflowState::Initialize(nsIPresContext* aPresContext, return rv; } +// Recover the block reflow state to what it should be if aLine is about +// to be reflowed. aLine should not be nsnull +nsresult nsBlockReflowState::RecoverState(nsLineData* aLine) +{ + NS_PRECONDITION(nsnull != aLine, "null parameter"); + nsLineData* prevLine = aLine->mPrevLine; + + if (nsnull != prevLine) { + // Compute the running y-offset, and the available height + mY = prevLine->mBounds.YMost(); + if (!mUnconstrainedHeight) { + mAvailSize.height -= mY; + } + + // Compute the kid x-most + for (nsLineData* l = prevLine; nsnull != l; l = l->mPrevLine) { + nscoord xmost = l->mBounds.XMost(); + if (xmost > mKidXMost) { + mKidXMost = xmost; + } + } + + // If the previous line is a block, then factor in its bottom margin + if (prevLine->mIsBlock) { + nsStyleSpacing* spacing; + nsIFrame* kid = prevLine->mFirstChild; + + kid->GetStyleData(kStyleSpacingSID, (nsStyleStruct*&)spacing); + if (spacing->mMargin.bottom < 0) { + mPrevMaxNegBottomMargin = -spacing->mMargin.bottom; + } else { + mPrevMaxPosBottomMargin = spacing->mMargin.bottom; + } + } + } + + return NS_OK; +} + //---------------------------------------------------------------------- nsresult @@ -780,6 +819,52 @@ done: return rv; } +// XXX This is a short-term hack. It assumes that the caller has already recovered +// the state, and that some space has been retrieved from the space manager... +nsresult +nsBlockFrame::ReflowMappedFrom(nsBlockReflowState& aState, nsLineData* aLine) +{ + nsresult rv = NS_OK; + + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + while (nsnull != aLine) { + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, aLine); + if (NS_OK != rv) { + goto done; + } + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, aLine); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + + mLastContentOffset = aLine->mLastContentOffset; + mLastContentIsComplete = PRBool(aLine->mLastContentIsComplete); + aLine = aLine->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + nsresult nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState) { @@ -1194,6 +1279,51 @@ nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, return rv; } +nsLineData* nsBlockFrame::FindLine(nsIFrame* aFrame) +{ + // Find the line that contains the aFrame + nsLineData* line = mLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + for (PRInt32 count = line->mChildCount; count > 0; count--) { + if (child == aFrame) { + return line; + } + + child->GetNextSibling(child); + } + + // Move to the next line + line = line->mNextLine; + } + + return nsnull; +} + +nsLineData* nsBlockFrame::LastLine() +{ + nsLineData* lastLine = mLines; + + // Get the last line + if (nsnull != lastLine) { + while (nsnull != lastLine->mNextLine) { + lastLine = lastLine->mNextLine; + } + } + + return lastLine; +} + +nsresult nsBlockFrame::IncrementalReflowAfter(nsBlockReflowState& aState, + nsLineData* aLine, + nsresult aReflowStatus, + const nsRect& aOldBounds) +{ + // Now just reflow all the lines that follow... + // XXX Obviously this needs to be more efficient + return ReflowMappedFrom(aState, aLine->mNextLine); +} + NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, nsISpaceManager* aSpaceManager, @@ -1219,39 +1349,11 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, // Is the reflow command target at us? if (this == aReflowCommand.GetTarget()) { if (aReflowCommand.GetType() == nsReflowCommand::FrameAppended) { - nsLineData* lastLine = mLines; - - // Get the last line - if (nsnull != lastLine) { - while (nsnull != lastLine->mNextLine) { - lastLine = lastLine->mNextLine; - } - } + nsLineData* lastLine = LastLine(); // Restore the state - if ((nsnull != lastLine) && (nsnull != lastLine->mPrevLine)) { - nsLineData* prevLine = lastLine->mPrevLine; - - state.mY = prevLine->mBounds.YMost(); - if (!state.mUnconstrainedHeight) { - state.mAvailSize.height -= state.mY; - } - - // XXX This isn't really right... - state.mKidXMost = mRect.width - state.mBorderPadding.right; - - // If the previous line is a block, then factor in its bottom margin - if (prevLine->mIsBlock) { - nsStyleSpacing* spacing; - nsIFrame* kid = prevLine->mFirstChild; - - kid->GetStyleData(kStyleSpacingSID, (nsStyleStruct*&)spacing); - if (spacing->mMargin.bottom < 0) { - state.mPrevMaxNegBottomMargin = -spacing->mMargin.bottom; - } else { - state.mPrevMaxPosBottomMargin = spacing->mMargin.bottom; - } - } + if (nsnull != lastLine) { + state.RecoverState(lastLine); } // Reflow unmapped children @@ -1267,38 +1369,65 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, rv = ReflowUnmapped(state); } - // Set return status - aStatus = frComplete; - if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { - rv = NS_OK; - aStatus = frNotComplete; - } + } 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()); + state.RecoverState(line); + + // Get some available space to start reflowing with + GetAvailableSpace(state, state.mY); + + // Reflow the affected line, and all the lines that follow... + // XXX Obviously this needs to be more efficient + rv = ReflowMappedFrom(state, line); } else { NS_NOTYETIMPLEMENTED("unexpected reflow command"); } } else { -#if 0 // The command is passing through us. Get the next frame in the reflow chain nsIFrame* nextFrame = aReflowCommand.GetNext(); // Restore our state as if nextFrame is the next frame to reflow - RecoverState(aPresContext, state, nextFrame); + nsLineData* line = FindLine(nextFrame); + state.RecoverState(line); - // Get the current bounds. We'll need this to adjust the frames that follow - nsRect oldBounds; - nextFrame->GetRect(oldBounds); + // Get some available space to start reflowing with + GetAvailableSpace(state, state.mY); - // Pass along the reflow command - nsRect desiredRect; - aStatus = aReflowCommand.Next(aSpaceManager, desiredRect, aMaxSize, nextFrame); -#endif - NS_NOTYETIMPLEMENTED("unexpected reflow command"); + // Reflow the affected line + nsLineLayout lineLayout(state); + + state.mCurrentLine = &lineLayout; + lineLayout.Initialize(state, line); + + // Have the line handle the incremental reflow + nsRect oldBounds = line->mBounds; + rv = lineLayout.IncrementalReflowFromChild(aReflowCommand, nextFrame); + + // Now place the line. It's possible it won't fit + rv = PlaceLine(state, lineLayout, line); + // XXX The way NS_LINE_LAYOUT_COMPLETE is being used is very confusing... + if (NS_LINE_LAYOUT_COMPLETE == rv) { + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + state.mPrevKidFrame = lineLayout.mPrevKidFrame; + + // Now figure out what to do with the frames that follow + rv = IncrementalReflowAfter(state, line, rv, oldBounds); + } } // Return our desired rect ComputeDesiredRect(state, aMaxSize, aDesiredRect); + // Set return status + aStatus = frComplete; + if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { + rv = NS_OK; + aStatus = frNotComplete; + } + // Now that reflow has finished, remove the cached pointer shell->RemoveCachedData(this); NS_RELEASE(shell); diff --git a/mozilla/layout/generic/nsBlockReflowState.h b/mozilla/layout/generic/nsBlockReflowState.h index 23cd49e92d8..b85ee3f7b01 100644 --- a/mozilla/layout/generic/nsBlockReflowState.h +++ b/mozilla/layout/generic/nsBlockReflowState.h @@ -142,6 +142,45 @@ nsBlockReflowState::Initialize(nsIPresContext* aPresContext, return rv; } +// Recover the block reflow state to what it should be if aLine is about +// to be reflowed. aLine should not be nsnull +nsresult nsBlockReflowState::RecoverState(nsLineData* aLine) +{ + NS_PRECONDITION(nsnull != aLine, "null parameter"); + nsLineData* prevLine = aLine->mPrevLine; + + if (nsnull != prevLine) { + // Compute the running y-offset, and the available height + mY = prevLine->mBounds.YMost(); + if (!mUnconstrainedHeight) { + mAvailSize.height -= mY; + } + + // Compute the kid x-most + for (nsLineData* l = prevLine; nsnull != l; l = l->mPrevLine) { + nscoord xmost = l->mBounds.XMost(); + if (xmost > mKidXMost) { + mKidXMost = xmost; + } + } + + // If the previous line is a block, then factor in its bottom margin + if (prevLine->mIsBlock) { + nsStyleSpacing* spacing; + nsIFrame* kid = prevLine->mFirstChild; + + kid->GetStyleData(kStyleSpacingSID, (nsStyleStruct*&)spacing); + if (spacing->mMargin.bottom < 0) { + mPrevMaxNegBottomMargin = -spacing->mMargin.bottom; + } else { + mPrevMaxPosBottomMargin = spacing->mMargin.bottom; + } + } + } + + return NS_OK; +} + //---------------------------------------------------------------------- nsresult @@ -780,6 +819,52 @@ done: return rv; } +// XXX This is a short-term hack. It assumes that the caller has already recovered +// the state, and that some space has been retrieved from the space manager... +nsresult +nsBlockFrame::ReflowMappedFrom(nsBlockReflowState& aState, nsLineData* aLine) +{ + nsresult rv = NS_OK; + + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + while (nsnull != aLine) { + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, aLine); + if (NS_OK != rv) { + goto done; + } + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, aLine); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + + mLastContentOffset = aLine->mLastContentOffset; + mLastContentIsComplete = PRBool(aLine->mLastContentIsComplete); + aLine = aLine->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + nsresult nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState) { @@ -1194,6 +1279,51 @@ nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, return rv; } +nsLineData* nsBlockFrame::FindLine(nsIFrame* aFrame) +{ + // Find the line that contains the aFrame + nsLineData* line = mLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + for (PRInt32 count = line->mChildCount; count > 0; count--) { + if (child == aFrame) { + return line; + } + + child->GetNextSibling(child); + } + + // Move to the next line + line = line->mNextLine; + } + + return nsnull; +} + +nsLineData* nsBlockFrame::LastLine() +{ + nsLineData* lastLine = mLines; + + // Get the last line + if (nsnull != lastLine) { + while (nsnull != lastLine->mNextLine) { + lastLine = lastLine->mNextLine; + } + } + + return lastLine; +} + +nsresult nsBlockFrame::IncrementalReflowAfter(nsBlockReflowState& aState, + nsLineData* aLine, + nsresult aReflowStatus, + const nsRect& aOldBounds) +{ + // Now just reflow all the lines that follow... + // XXX Obviously this needs to be more efficient + return ReflowMappedFrom(aState, aLine->mNextLine); +} + NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, nsISpaceManager* aSpaceManager, @@ -1219,39 +1349,11 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, // Is the reflow command target at us? if (this == aReflowCommand.GetTarget()) { if (aReflowCommand.GetType() == nsReflowCommand::FrameAppended) { - nsLineData* lastLine = mLines; - - // Get the last line - if (nsnull != lastLine) { - while (nsnull != lastLine->mNextLine) { - lastLine = lastLine->mNextLine; - } - } + nsLineData* lastLine = LastLine(); // Restore the state - if ((nsnull != lastLine) && (nsnull != lastLine->mPrevLine)) { - nsLineData* prevLine = lastLine->mPrevLine; - - state.mY = prevLine->mBounds.YMost(); - if (!state.mUnconstrainedHeight) { - state.mAvailSize.height -= state.mY; - } - - // XXX This isn't really right... - state.mKidXMost = mRect.width - state.mBorderPadding.right; - - // If the previous line is a block, then factor in its bottom margin - if (prevLine->mIsBlock) { - nsStyleSpacing* spacing; - nsIFrame* kid = prevLine->mFirstChild; - - kid->GetStyleData(kStyleSpacingSID, (nsStyleStruct*&)spacing); - if (spacing->mMargin.bottom < 0) { - state.mPrevMaxNegBottomMargin = -spacing->mMargin.bottom; - } else { - state.mPrevMaxPosBottomMargin = spacing->mMargin.bottom; - } - } + if (nsnull != lastLine) { + state.RecoverState(lastLine); } // Reflow unmapped children @@ -1267,38 +1369,65 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, rv = ReflowUnmapped(state); } - // Set return status - aStatus = frComplete; - if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { - rv = NS_OK; - aStatus = frNotComplete; - } + } 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()); + state.RecoverState(line); + + // Get some available space to start reflowing with + GetAvailableSpace(state, state.mY); + + // Reflow the affected line, and all the lines that follow... + // XXX Obviously this needs to be more efficient + rv = ReflowMappedFrom(state, line); } else { NS_NOTYETIMPLEMENTED("unexpected reflow command"); } } else { -#if 0 // The command is passing through us. Get the next frame in the reflow chain nsIFrame* nextFrame = aReflowCommand.GetNext(); // Restore our state as if nextFrame is the next frame to reflow - RecoverState(aPresContext, state, nextFrame); + nsLineData* line = FindLine(nextFrame); + state.RecoverState(line); - // Get the current bounds. We'll need this to adjust the frames that follow - nsRect oldBounds; - nextFrame->GetRect(oldBounds); + // Get some available space to start reflowing with + GetAvailableSpace(state, state.mY); - // Pass along the reflow command - nsRect desiredRect; - aStatus = aReflowCommand.Next(aSpaceManager, desiredRect, aMaxSize, nextFrame); -#endif - NS_NOTYETIMPLEMENTED("unexpected reflow command"); + // Reflow the affected line + nsLineLayout lineLayout(state); + + state.mCurrentLine = &lineLayout; + lineLayout.Initialize(state, line); + + // Have the line handle the incremental reflow + nsRect oldBounds = line->mBounds; + rv = lineLayout.IncrementalReflowFromChild(aReflowCommand, nextFrame); + + // Now place the line. It's possible it won't fit + rv = PlaceLine(state, lineLayout, line); + // XXX The way NS_LINE_LAYOUT_COMPLETE is being used is very confusing... + if (NS_LINE_LAYOUT_COMPLETE == rv) { + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + state.mPrevKidFrame = lineLayout.mPrevKidFrame; + + // Now figure out what to do with the frames that follow + rv = IncrementalReflowAfter(state, line, rv, oldBounds); + } } // Return our desired rect ComputeDesiredRect(state, aMaxSize, aDesiredRect); + // Set return status + aStatus = frComplete; + if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { + rv = NS_OK; + aStatus = frNotComplete; + } + // Now that reflow has finished, remove the cached pointer shell->RemoveCachedData(this); NS_RELEASE(shell); diff --git a/mozilla/layout/generic/nsLineLayout.cpp b/mozilla/layout/generic/nsLineLayout.cpp index ebc7b86ce64..923060e09f1 100644 --- a/mozilla/layout/generic/nsLineLayout.cpp +++ b/mozilla/layout/generic/nsLineLayout.cpp @@ -28,6 +28,7 @@ #include "nsPlaceholderFrame.h" #include "nsCSSLayout.h" #include "nsCRT.h" +#include "nsReflowCommand.h" #undef NOISY_REFLOW @@ -356,7 +357,7 @@ nsLineLayout::WordBreakReflow() // Return values: <0 for error // 0 == NS_LINE_LAYOUT nsresult -nsLineLayout::ReflowChild() +nsLineLayout::ReflowChild(nsReflowCommand* aReflowCommand) { // Get kid frame's style context nsIStyleContextPtr kidSC; @@ -413,7 +414,20 @@ nsLineLayout::ReflowChild() } mReflowResult = NS_LINE_LAYOUT_REFLOW_RESULT_NOT_AWARE; nscoord dx = mReflowData.mX + kidSpacing->mMargin.left; - if (isBlock) { + if (aReflowCommand) { + nsIFrame* nextFrame; + + mSpaceManager->Translate(dx, mY); + kidReflowStatus = aReflowCommand->Next(mSpaceManager, kidRect, kidAvailSize, nextFrame); + mSpaceManager->Translate(-dx, -mY); + kidRect.x = dx; + kidRect.y = mY; + kidSize.width = kidRect.width; + kidSize.height = kidRect.height; + kidSize.ascent = kidRect.height; + kidSize.descent = 0; + + } else if (isBlock) { mSpaceManager->Translate(dx, mY); rv = mBlock->ReflowBlockChild(mKidFrame, mPresContext, mSpaceManager, kidAvailSize, kidRect, @@ -518,6 +532,94 @@ nsLineLayout::ReflowChild() return rv; } +nsresult +nsLineLayout::IncrementalReflowFromChild(nsReflowCommand& aReflowCommand, + nsIFrame* aChildFrame) +{ +#if 0 + // Get the current bounds. We'll need this to adjust the frames that follow + nsRect oldBounds; + aChildFrame->GetRect(oldBounds); +#endif + + // For the time being reflow all the children, and when we get to aChildFrame + // handle it specially + nsresult reflowStatus = NS_LINE_LAYOUT_COMPLETE; + + mLine->mBounds.x = mReflowData.mX; + mLine->mBounds.y = mY; + mKidFrame = mLine->mFirstChild; + PRInt32 kidNum = 0; + while (kidNum < mLine->mChildCount) { + // XXX Code to avoid reflowing a child goes here + nsresult childReflowStatus; + + if (mKidFrame == aChildFrame) { + childReflowStatus = ReflowChild(&aReflowCommand); + } else { + childReflowStatus = ReflowChild(nsnull); + } + + if (childReflowStatus < 0) { + reflowStatus = childReflowStatus; + goto done; + } + + switch (childReflowStatus) { + default: + case NS_LINE_LAYOUT_COMPLETE: + mPrevKidFrame = mKidFrame; + mKidFrame->GetNextSibling(mKidFrame); + mKidIndex++; + kidNum++; + break; + + case NS_LINE_LAYOUT_NOT_COMPLETE: + reflowStatus = childReflowStatus; + mPrevKidFrame = mKidFrame; + mKidFrame->GetNextSibling(mKidFrame); + kidNum++; + goto split_line; + + case NS_LINE_LAYOUT_BREAK_BEFORE: + reflowStatus = childReflowStatus; + goto split_line; + + case NS_LINE_LAYOUT_BREAK_AFTER: + reflowStatus = childReflowStatus; + mPrevKidFrame = mKidFrame; + mKidFrame->GetNextSibling(mKidFrame); + mKidIndex++; + kidNum++; + + split_line: + reflowStatus = SplitLine(childReflowStatus, mLine->mChildCount - kidNum); + goto done; + } + } + +done: + // Perform alignment operations + if (mLine->mIsBlock) { + mLineHeight = mReflowData.mMaxAscent + mReflowData.mMaxDescent; + } + else { + AlignChildren(); + } + + // Set final bounds of the line + mLine->mBounds.height = mLineHeight; + mLine->mBounds.width = mReflowData.mX - mLine->mBounds.x; + + NS_ASSERTION(((reflowStatus < 0) || + (reflowStatus == NS_LINE_LAYOUT_COMPLETE) || + (reflowStatus == NS_LINE_LAYOUT_NOT_COMPLETE) || + (reflowStatus == NS_LINE_LAYOUT_BREAK_BEFORE) || + (reflowStatus == NS_LINE_LAYOUT_BREAK_AFTER)), + "bad return status from ReflowMapped"); + return reflowStatus; +} + //---------------------------------------------------------------------- nsresult @@ -630,7 +732,7 @@ nsLineLayout::ReflowMapped() while (kidNum < mLine->mChildCount) { // XXX Code to avoid reflowing a child goes here - nsresult childReflowStatus = ReflowChild(); + nsresult childReflowStatus = ReflowChild(nsnull); if (childReflowStatus < 0) { reflowStatus = childReflowStatus; goto done; @@ -769,7 +871,7 @@ nsLineLayout::PullUpChildren() } // Try to reflow it like any other mapped child - nsresult childReflowStatus = ReflowChild(); + nsresult childReflowStatus = ReflowChild(nsnull); if (childReflowStatus < 0) { reflowStatus = childReflowStatus; goto done; @@ -942,7 +1044,7 @@ nsLineLayout::ReflowUnmapped() } // Reflow new child frame - childReflowStatus = ReflowChild(); + childReflowStatus = ReflowChild(nsnull); if (childReflowStatus < 0) { reflowStatus = childReflowStatus; goto done; diff --git a/mozilla/layout/generic/nsLineLayout.h b/mozilla/layout/generic/nsLineLayout.h index 953bc670ff6..193ff161950 100644 --- a/mozilla/layout/generic/nsLineLayout.h +++ b/mozilla/layout/generic/nsLineLayout.h @@ -100,16 +100,16 @@ struct nsLineLayout { */ nsresult ReflowLine(); + nsresult IncrementalReflowFromChild(nsReflowCommand& aReflowCommand, + nsIFrame* aChildFrame); + + // The presentation context nsIPresContext* mPresContext; // The block behind the line nsBlockFrame* mBlock; nsISpaceManager* mSpaceManager; -#if 0 - // XXX I don't think we need this anymore... - PRBool mBlockIsPseudo; -#endif nsIContent* mBlockContent; PRInt32 mKidIndex; @@ -180,7 +180,7 @@ protected: nsresult WordBreakReflow(); - nsresult ReflowChild(); + nsresult ReflowChild(nsReflowCommand* aReflowCommand); nsresult ReflowMapped(); diff --git a/mozilla/layout/html/base/src/nsBlockFrame.cpp b/mozilla/layout/html/base/src/nsBlockFrame.cpp index 23cd49e92d8..b85ee3f7b01 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.cpp +++ b/mozilla/layout/html/base/src/nsBlockFrame.cpp @@ -142,6 +142,45 @@ nsBlockReflowState::Initialize(nsIPresContext* aPresContext, return rv; } +// Recover the block reflow state to what it should be if aLine is about +// to be reflowed. aLine should not be nsnull +nsresult nsBlockReflowState::RecoverState(nsLineData* aLine) +{ + NS_PRECONDITION(nsnull != aLine, "null parameter"); + nsLineData* prevLine = aLine->mPrevLine; + + if (nsnull != prevLine) { + // Compute the running y-offset, and the available height + mY = prevLine->mBounds.YMost(); + if (!mUnconstrainedHeight) { + mAvailSize.height -= mY; + } + + // Compute the kid x-most + for (nsLineData* l = prevLine; nsnull != l; l = l->mPrevLine) { + nscoord xmost = l->mBounds.XMost(); + if (xmost > mKidXMost) { + mKidXMost = xmost; + } + } + + // If the previous line is a block, then factor in its bottom margin + if (prevLine->mIsBlock) { + nsStyleSpacing* spacing; + nsIFrame* kid = prevLine->mFirstChild; + + kid->GetStyleData(kStyleSpacingSID, (nsStyleStruct*&)spacing); + if (spacing->mMargin.bottom < 0) { + mPrevMaxNegBottomMargin = -spacing->mMargin.bottom; + } else { + mPrevMaxPosBottomMargin = spacing->mMargin.bottom; + } + } + } + + return NS_OK; +} + //---------------------------------------------------------------------- nsresult @@ -780,6 +819,52 @@ done: return rv; } +// XXX This is a short-term hack. It assumes that the caller has already recovered +// the state, and that some space has been retrieved from the space manager... +nsresult +nsBlockFrame::ReflowMappedFrom(nsBlockReflowState& aState, nsLineData* aLine) +{ + nsresult rv = NS_OK; + + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + while (nsnull != aLine) { + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, aLine); + if (NS_OK != rv) { + goto done; + } + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, aLine); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + + mLastContentOffset = aLine->mLastContentOffset; + mLastContentIsComplete = PRBool(aLine->mLastContentIsComplete); + aLine = aLine->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + nsresult nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState) { @@ -1194,6 +1279,51 @@ nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, return rv; } +nsLineData* nsBlockFrame::FindLine(nsIFrame* aFrame) +{ + // Find the line that contains the aFrame + nsLineData* line = mLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + for (PRInt32 count = line->mChildCount; count > 0; count--) { + if (child == aFrame) { + return line; + } + + child->GetNextSibling(child); + } + + // Move to the next line + line = line->mNextLine; + } + + return nsnull; +} + +nsLineData* nsBlockFrame::LastLine() +{ + nsLineData* lastLine = mLines; + + // Get the last line + if (nsnull != lastLine) { + while (nsnull != lastLine->mNextLine) { + lastLine = lastLine->mNextLine; + } + } + + return lastLine; +} + +nsresult nsBlockFrame::IncrementalReflowAfter(nsBlockReflowState& aState, + nsLineData* aLine, + nsresult aReflowStatus, + const nsRect& aOldBounds) +{ + // Now just reflow all the lines that follow... + // XXX Obviously this needs to be more efficient + return ReflowMappedFrom(aState, aLine->mNextLine); +} + NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, nsISpaceManager* aSpaceManager, @@ -1219,39 +1349,11 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, // Is the reflow command target at us? if (this == aReflowCommand.GetTarget()) { if (aReflowCommand.GetType() == nsReflowCommand::FrameAppended) { - nsLineData* lastLine = mLines; - - // Get the last line - if (nsnull != lastLine) { - while (nsnull != lastLine->mNextLine) { - lastLine = lastLine->mNextLine; - } - } + nsLineData* lastLine = LastLine(); // Restore the state - if ((nsnull != lastLine) && (nsnull != lastLine->mPrevLine)) { - nsLineData* prevLine = lastLine->mPrevLine; - - state.mY = prevLine->mBounds.YMost(); - if (!state.mUnconstrainedHeight) { - state.mAvailSize.height -= state.mY; - } - - // XXX This isn't really right... - state.mKidXMost = mRect.width - state.mBorderPadding.right; - - // If the previous line is a block, then factor in its bottom margin - if (prevLine->mIsBlock) { - nsStyleSpacing* spacing; - nsIFrame* kid = prevLine->mFirstChild; - - kid->GetStyleData(kStyleSpacingSID, (nsStyleStruct*&)spacing); - if (spacing->mMargin.bottom < 0) { - state.mPrevMaxNegBottomMargin = -spacing->mMargin.bottom; - } else { - state.mPrevMaxPosBottomMargin = spacing->mMargin.bottom; - } - } + if (nsnull != lastLine) { + state.RecoverState(lastLine); } // Reflow unmapped children @@ -1267,38 +1369,65 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, rv = ReflowUnmapped(state); } - // Set return status - aStatus = frComplete; - if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { - rv = NS_OK; - aStatus = frNotComplete; - } + } 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()); + state.RecoverState(line); + + // Get some available space to start reflowing with + GetAvailableSpace(state, state.mY); + + // Reflow the affected line, and all the lines that follow... + // XXX Obviously this needs to be more efficient + rv = ReflowMappedFrom(state, line); } else { NS_NOTYETIMPLEMENTED("unexpected reflow command"); } } else { -#if 0 // The command is passing through us. Get the next frame in the reflow chain nsIFrame* nextFrame = aReflowCommand.GetNext(); // Restore our state as if nextFrame is the next frame to reflow - RecoverState(aPresContext, state, nextFrame); + nsLineData* line = FindLine(nextFrame); + state.RecoverState(line); - // Get the current bounds. We'll need this to adjust the frames that follow - nsRect oldBounds; - nextFrame->GetRect(oldBounds); + // Get some available space to start reflowing with + GetAvailableSpace(state, state.mY); - // Pass along the reflow command - nsRect desiredRect; - aStatus = aReflowCommand.Next(aSpaceManager, desiredRect, aMaxSize, nextFrame); -#endif - NS_NOTYETIMPLEMENTED("unexpected reflow command"); + // Reflow the affected line + nsLineLayout lineLayout(state); + + state.mCurrentLine = &lineLayout; + lineLayout.Initialize(state, line); + + // Have the line handle the incremental reflow + nsRect oldBounds = line->mBounds; + rv = lineLayout.IncrementalReflowFromChild(aReflowCommand, nextFrame); + + // Now place the line. It's possible it won't fit + rv = PlaceLine(state, lineLayout, line); + // XXX The way NS_LINE_LAYOUT_COMPLETE is being used is very confusing... + if (NS_LINE_LAYOUT_COMPLETE == rv) { + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + state.mPrevKidFrame = lineLayout.mPrevKidFrame; + + // Now figure out what to do with the frames that follow + rv = IncrementalReflowAfter(state, line, rv, oldBounds); + } } // Return our desired rect ComputeDesiredRect(state, aMaxSize, aDesiredRect); + // Set return status + aStatus = frComplete; + if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { + rv = NS_OK; + aStatus = frNotComplete; + } + // Now that reflow has finished, remove the cached pointer shell->RemoveCachedData(this); NS_RELEASE(shell); diff --git a/mozilla/layout/html/base/src/nsBlockFrame.h b/mozilla/layout/html/base/src/nsBlockFrame.h index 5dc94ca5086..50dd7088088 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.h +++ b/mozilla/layout/html/base/src/nsBlockFrame.h @@ -33,7 +33,7 @@ class nsBlockFrame; struct nsBandData; struct nsBlockBandData : public nsBandData { - // Trapezoid's used during band processing + // Trapezoids used during band processing nsBandTrapezoid data[12]; // Bounding rect of available space between any left and right floaters @@ -63,6 +63,8 @@ struct nsBlockReflowState { nsSize* aMaxElementSize, nsBlockFrame* aBlock); + nsresult RecoverState(nsLineData* aLine); + nsIPresContext* mPresContext; nsBlockFrame* mBlock; @@ -249,6 +251,14 @@ protected: const nsSize& aMaxSize, nsRect& aDesiredRect); + nsLineData* LastLine(); + nsLineData* FindLine(nsIFrame* aFrame); + + nsresult IncrementalReflowAfter(nsBlockReflowState& aState, + nsLineData* aLine, + nsresult aReflowStatus, + const nsRect& aOldBounds); + void DestroyLines(); void DrainOverflowList(); @@ -278,6 +288,7 @@ protected: nsLineData* aLine); nsresult ReflowMapped(nsBlockReflowState& aState); + nsresult ReflowMappedFrom(nsBlockReflowState& aState, nsLineData* aLine); nsresult ReflowUnmapped(nsBlockReflowState& aState); diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.cpp b/mozilla/layout/html/base/src/nsBlockReflowState.cpp index 23cd49e92d8..b85ee3f7b01 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.cpp +++ b/mozilla/layout/html/base/src/nsBlockReflowState.cpp @@ -142,6 +142,45 @@ nsBlockReflowState::Initialize(nsIPresContext* aPresContext, return rv; } +// Recover the block reflow state to what it should be if aLine is about +// to be reflowed. aLine should not be nsnull +nsresult nsBlockReflowState::RecoverState(nsLineData* aLine) +{ + NS_PRECONDITION(nsnull != aLine, "null parameter"); + nsLineData* prevLine = aLine->mPrevLine; + + if (nsnull != prevLine) { + // Compute the running y-offset, and the available height + mY = prevLine->mBounds.YMost(); + if (!mUnconstrainedHeight) { + mAvailSize.height -= mY; + } + + // Compute the kid x-most + for (nsLineData* l = prevLine; nsnull != l; l = l->mPrevLine) { + nscoord xmost = l->mBounds.XMost(); + if (xmost > mKidXMost) { + mKidXMost = xmost; + } + } + + // If the previous line is a block, then factor in its bottom margin + if (prevLine->mIsBlock) { + nsStyleSpacing* spacing; + nsIFrame* kid = prevLine->mFirstChild; + + kid->GetStyleData(kStyleSpacingSID, (nsStyleStruct*&)spacing); + if (spacing->mMargin.bottom < 0) { + mPrevMaxNegBottomMargin = -spacing->mMargin.bottom; + } else { + mPrevMaxPosBottomMargin = spacing->mMargin.bottom; + } + } + } + + return NS_OK; +} + //---------------------------------------------------------------------- nsresult @@ -780,6 +819,52 @@ done: return rv; } +// XXX This is a short-term hack. It assumes that the caller has already recovered +// the state, and that some space has been retrieved from the space manager... +nsresult +nsBlockFrame::ReflowMappedFrom(nsBlockReflowState& aState, nsLineData* aLine) +{ + nsresult rv = NS_OK; + + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + while (nsnull != aLine) { + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, aLine); + if (NS_OK != rv) { + goto done; + } + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, aLine); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + + mLastContentOffset = aLine->mLastContentOffset; + mLastContentIsComplete = PRBool(aLine->mLastContentIsComplete); + aLine = aLine->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + nsresult nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState) { @@ -1194,6 +1279,51 @@ nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, return rv; } +nsLineData* nsBlockFrame::FindLine(nsIFrame* aFrame) +{ + // Find the line that contains the aFrame + nsLineData* line = mLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + for (PRInt32 count = line->mChildCount; count > 0; count--) { + if (child == aFrame) { + return line; + } + + child->GetNextSibling(child); + } + + // Move to the next line + line = line->mNextLine; + } + + return nsnull; +} + +nsLineData* nsBlockFrame::LastLine() +{ + nsLineData* lastLine = mLines; + + // Get the last line + if (nsnull != lastLine) { + while (nsnull != lastLine->mNextLine) { + lastLine = lastLine->mNextLine; + } + } + + return lastLine; +} + +nsresult nsBlockFrame::IncrementalReflowAfter(nsBlockReflowState& aState, + nsLineData* aLine, + nsresult aReflowStatus, + const nsRect& aOldBounds) +{ + // Now just reflow all the lines that follow... + // XXX Obviously this needs to be more efficient + return ReflowMappedFrom(aState, aLine->mNextLine); +} + NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, nsISpaceManager* aSpaceManager, @@ -1219,39 +1349,11 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, // Is the reflow command target at us? if (this == aReflowCommand.GetTarget()) { if (aReflowCommand.GetType() == nsReflowCommand::FrameAppended) { - nsLineData* lastLine = mLines; - - // Get the last line - if (nsnull != lastLine) { - while (nsnull != lastLine->mNextLine) { - lastLine = lastLine->mNextLine; - } - } + nsLineData* lastLine = LastLine(); // Restore the state - if ((nsnull != lastLine) && (nsnull != lastLine->mPrevLine)) { - nsLineData* prevLine = lastLine->mPrevLine; - - state.mY = prevLine->mBounds.YMost(); - if (!state.mUnconstrainedHeight) { - state.mAvailSize.height -= state.mY; - } - - // XXX This isn't really right... - state.mKidXMost = mRect.width - state.mBorderPadding.right; - - // If the previous line is a block, then factor in its bottom margin - if (prevLine->mIsBlock) { - nsStyleSpacing* spacing; - nsIFrame* kid = prevLine->mFirstChild; - - kid->GetStyleData(kStyleSpacingSID, (nsStyleStruct*&)spacing); - if (spacing->mMargin.bottom < 0) { - state.mPrevMaxNegBottomMargin = -spacing->mMargin.bottom; - } else { - state.mPrevMaxPosBottomMargin = spacing->mMargin.bottom; - } - } + if (nsnull != lastLine) { + state.RecoverState(lastLine); } // Reflow unmapped children @@ -1267,38 +1369,65 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, rv = ReflowUnmapped(state); } - // Set return status - aStatus = frComplete; - if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { - rv = NS_OK; - aStatus = frNotComplete; - } + } 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()); + state.RecoverState(line); + + // Get some available space to start reflowing with + GetAvailableSpace(state, state.mY); + + // Reflow the affected line, and all the lines that follow... + // XXX Obviously this needs to be more efficient + rv = ReflowMappedFrom(state, line); } else { NS_NOTYETIMPLEMENTED("unexpected reflow command"); } } else { -#if 0 // The command is passing through us. Get the next frame in the reflow chain nsIFrame* nextFrame = aReflowCommand.GetNext(); // Restore our state as if nextFrame is the next frame to reflow - RecoverState(aPresContext, state, nextFrame); + nsLineData* line = FindLine(nextFrame); + state.RecoverState(line); - // Get the current bounds. We'll need this to adjust the frames that follow - nsRect oldBounds; - nextFrame->GetRect(oldBounds); + // Get some available space to start reflowing with + GetAvailableSpace(state, state.mY); - // Pass along the reflow command - nsRect desiredRect; - aStatus = aReflowCommand.Next(aSpaceManager, desiredRect, aMaxSize, nextFrame); -#endif - NS_NOTYETIMPLEMENTED("unexpected reflow command"); + // Reflow the affected line + nsLineLayout lineLayout(state); + + state.mCurrentLine = &lineLayout; + lineLayout.Initialize(state, line); + + // Have the line handle the incremental reflow + nsRect oldBounds = line->mBounds; + rv = lineLayout.IncrementalReflowFromChild(aReflowCommand, nextFrame); + + // Now place the line. It's possible it won't fit + rv = PlaceLine(state, lineLayout, line); + // XXX The way NS_LINE_LAYOUT_COMPLETE is being used is very confusing... + if (NS_LINE_LAYOUT_COMPLETE == rv) { + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + state.mPrevKidFrame = lineLayout.mPrevKidFrame; + + // Now figure out what to do with the frames that follow + rv = IncrementalReflowAfter(state, line, rv, oldBounds); + } } // Return our desired rect ComputeDesiredRect(state, aMaxSize, aDesiredRect); + // Set return status + aStatus = frComplete; + if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { + rv = NS_OK; + aStatus = frNotComplete; + } + // Now that reflow has finished, remove the cached pointer shell->RemoveCachedData(this); NS_RELEASE(shell); diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.h b/mozilla/layout/html/base/src/nsBlockReflowState.h index 23cd49e92d8..b85ee3f7b01 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.h +++ b/mozilla/layout/html/base/src/nsBlockReflowState.h @@ -142,6 +142,45 @@ nsBlockReflowState::Initialize(nsIPresContext* aPresContext, return rv; } +// Recover the block reflow state to what it should be if aLine is about +// to be reflowed. aLine should not be nsnull +nsresult nsBlockReflowState::RecoverState(nsLineData* aLine) +{ + NS_PRECONDITION(nsnull != aLine, "null parameter"); + nsLineData* prevLine = aLine->mPrevLine; + + if (nsnull != prevLine) { + // Compute the running y-offset, and the available height + mY = prevLine->mBounds.YMost(); + if (!mUnconstrainedHeight) { + mAvailSize.height -= mY; + } + + // Compute the kid x-most + for (nsLineData* l = prevLine; nsnull != l; l = l->mPrevLine) { + nscoord xmost = l->mBounds.XMost(); + if (xmost > mKidXMost) { + mKidXMost = xmost; + } + } + + // If the previous line is a block, then factor in its bottom margin + if (prevLine->mIsBlock) { + nsStyleSpacing* spacing; + nsIFrame* kid = prevLine->mFirstChild; + + kid->GetStyleData(kStyleSpacingSID, (nsStyleStruct*&)spacing); + if (spacing->mMargin.bottom < 0) { + mPrevMaxNegBottomMargin = -spacing->mMargin.bottom; + } else { + mPrevMaxPosBottomMargin = spacing->mMargin.bottom; + } + } + } + + return NS_OK; +} + //---------------------------------------------------------------------- nsresult @@ -780,6 +819,52 @@ done: return rv; } +// XXX This is a short-term hack. It assumes that the caller has already recovered +// the state, and that some space has been retrieved from the space manager... +nsresult +nsBlockFrame::ReflowMappedFrom(nsBlockReflowState& aState, nsLineData* aLine) +{ + nsresult rv = NS_OK; + + nsLineLayout lineLayout(aState); + aState.mCurrentLine = &lineLayout; + while (nsnull != aLine) { + // Initialize the line layout for this line + rv = lineLayout.Initialize(aState, aLine); + if (NS_OK != rv) { + goto done; + } + lineLayout.mPrevKidFrame = aState.mPrevKidFrame; + + // Reflow the line + nsresult lineReflowStatus = lineLayout.ReflowLine(); + if (lineReflowStatus < 0) { + // Some kind of hard error + rv = lineReflowStatus; + goto done; + } + mChildCount += lineLayout.mNewFrames; + + // Now place it. It's possible it won't fit. + rv = PlaceLine(aState, lineLayout, aLine); + if (NS_LINE_LAYOUT_COMPLETE != rv) { + goto done; + } + + mLastContentOffset = aLine->mLastContentOffset; + mLastContentIsComplete = PRBool(aLine->mLastContentIsComplete); + aLine = aLine->mNextLine; + aState.mPrevKidFrame = lineLayout.mPrevKidFrame; + } + +done: + aState.mCurrentLine = nsnull; +#ifdef NS_DEBUG + VerifyLines(PR_TRUE); +#endif + return rv; +} + nsresult nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState) { @@ -1194,6 +1279,51 @@ nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext, return rv; } +nsLineData* nsBlockFrame::FindLine(nsIFrame* aFrame) +{ + // Find the line that contains the aFrame + nsLineData* line = mLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + for (PRInt32 count = line->mChildCount; count > 0; count--) { + if (child == aFrame) { + return line; + } + + child->GetNextSibling(child); + } + + // Move to the next line + line = line->mNextLine; + } + + return nsnull; +} + +nsLineData* nsBlockFrame::LastLine() +{ + nsLineData* lastLine = mLines; + + // Get the last line + if (nsnull != lastLine) { + while (nsnull != lastLine->mNextLine) { + lastLine = lastLine->mNextLine; + } + } + + return lastLine; +} + +nsresult nsBlockFrame::IncrementalReflowAfter(nsBlockReflowState& aState, + nsLineData* aLine, + nsresult aReflowStatus, + const nsRect& aOldBounds) +{ + // Now just reflow all the lines that follow... + // XXX Obviously this needs to be more efficient + return ReflowMappedFrom(aState, aLine->mNextLine); +} + NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, nsISpaceManager* aSpaceManager, @@ -1219,39 +1349,11 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, // Is the reflow command target at us? if (this == aReflowCommand.GetTarget()) { if (aReflowCommand.GetType() == nsReflowCommand::FrameAppended) { - nsLineData* lastLine = mLines; - - // Get the last line - if (nsnull != lastLine) { - while (nsnull != lastLine->mNextLine) { - lastLine = lastLine->mNextLine; - } - } + nsLineData* lastLine = LastLine(); // Restore the state - if ((nsnull != lastLine) && (nsnull != lastLine->mPrevLine)) { - nsLineData* prevLine = lastLine->mPrevLine; - - state.mY = prevLine->mBounds.YMost(); - if (!state.mUnconstrainedHeight) { - state.mAvailSize.height -= state.mY; - } - - // XXX This isn't really right... - state.mKidXMost = mRect.width - state.mBorderPadding.right; - - // If the previous line is a block, then factor in its bottom margin - if (prevLine->mIsBlock) { - nsStyleSpacing* spacing; - nsIFrame* kid = prevLine->mFirstChild; - - kid->GetStyleData(kStyleSpacingSID, (nsStyleStruct*&)spacing); - if (spacing->mMargin.bottom < 0) { - state.mPrevMaxNegBottomMargin = -spacing->mMargin.bottom; - } else { - state.mPrevMaxPosBottomMargin = spacing->mMargin.bottom; - } - } + if (nsnull != lastLine) { + state.RecoverState(lastLine); } // Reflow unmapped children @@ -1267,38 +1369,65 @@ nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext, rv = ReflowUnmapped(state); } - // Set return status - aStatus = frComplete; - if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { - rv = NS_OK; - aStatus = frNotComplete; - } + } 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()); + state.RecoverState(line); + + // Get some available space to start reflowing with + GetAvailableSpace(state, state.mY); + + // Reflow the affected line, and all the lines that follow... + // XXX Obviously this needs to be more efficient + rv = ReflowMappedFrom(state, line); } else { NS_NOTYETIMPLEMENTED("unexpected reflow command"); } } else { -#if 0 // The command is passing through us. Get the next frame in the reflow chain nsIFrame* nextFrame = aReflowCommand.GetNext(); // Restore our state as if nextFrame is the next frame to reflow - RecoverState(aPresContext, state, nextFrame); + nsLineData* line = FindLine(nextFrame); + state.RecoverState(line); - // Get the current bounds. We'll need this to adjust the frames that follow - nsRect oldBounds; - nextFrame->GetRect(oldBounds); + // Get some available space to start reflowing with + GetAvailableSpace(state, state.mY); - // Pass along the reflow command - nsRect desiredRect; - aStatus = aReflowCommand.Next(aSpaceManager, desiredRect, aMaxSize, nextFrame); -#endif - NS_NOTYETIMPLEMENTED("unexpected reflow command"); + // Reflow the affected line + nsLineLayout lineLayout(state); + + state.mCurrentLine = &lineLayout; + lineLayout.Initialize(state, line); + + // Have the line handle the incremental reflow + nsRect oldBounds = line->mBounds; + rv = lineLayout.IncrementalReflowFromChild(aReflowCommand, nextFrame); + + // Now place the line. It's possible it won't fit + rv = PlaceLine(state, lineLayout, line); + // XXX The way NS_LINE_LAYOUT_COMPLETE is being used is very confusing... + if (NS_LINE_LAYOUT_COMPLETE == rv) { + mLastContentOffset = line->mLastContentOffset; + mLastContentIsComplete = PRBool(line->mLastContentIsComplete); + state.mPrevKidFrame = lineLayout.mPrevKidFrame; + + // Now figure out what to do with the frames that follow + rv = IncrementalReflowAfter(state, line, rv, oldBounds); + } } // Return our desired rect ComputeDesiredRect(state, aMaxSize, aDesiredRect); + // Set return status + aStatus = frComplete; + if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) { + rv = NS_OK; + aStatus = frNotComplete; + } + // Now that reflow has finished, remove the cached pointer shell->RemoveCachedData(this); NS_RELEASE(shell); diff --git a/mozilla/layout/html/base/src/nsBodyFrame.cpp b/mozilla/layout/html/base/src/nsBodyFrame.cpp index f89e267c386..fd4f985d29c 100644 --- a/mozilla/layout/html/base/src/nsBodyFrame.cpp +++ b/mozilla/layout/html/base/src/nsBodyFrame.cpp @@ -272,14 +272,13 @@ NS_METHOD nsBodyFrame::IncrementalReflow(nsIPresContext* aPresContext, nsSize columnMaxSize = GetColumnAvailSpace(aPresContext, mySpacing, aMaxSize); - // Pass the command along to our child frame - nsIRunaround* reflowRunaround; - nsRect desiredRect; + // Pass the command along to our column pseudo frame + nsRect desiredRect; + nsIFrame* nextFrame; NS_ASSERTION(nsnull != mFirstChild, "no first child"); - mFirstChild->QueryInterface(kIRunaroundIID, (void**)&reflowRunaround); - reflowRunaround->IncrementalReflow(aPresContext, mSpaceManager, - columnMaxSize, desiredRect, aReflowCommand, aStatus); + aStatus = aReflowCommand.Next(mSpaceManager, desiredRect, columnMaxSize, + nextFrame); // Place and size the frame desiredRect.x += leftInset; diff --git a/mozilla/layout/html/base/src/nsLineLayout.cpp b/mozilla/layout/html/base/src/nsLineLayout.cpp index ebc7b86ce64..923060e09f1 100644 --- a/mozilla/layout/html/base/src/nsLineLayout.cpp +++ b/mozilla/layout/html/base/src/nsLineLayout.cpp @@ -28,6 +28,7 @@ #include "nsPlaceholderFrame.h" #include "nsCSSLayout.h" #include "nsCRT.h" +#include "nsReflowCommand.h" #undef NOISY_REFLOW @@ -356,7 +357,7 @@ nsLineLayout::WordBreakReflow() // Return values: <0 for error // 0 == NS_LINE_LAYOUT nsresult -nsLineLayout::ReflowChild() +nsLineLayout::ReflowChild(nsReflowCommand* aReflowCommand) { // Get kid frame's style context nsIStyleContextPtr kidSC; @@ -413,7 +414,20 @@ nsLineLayout::ReflowChild() } mReflowResult = NS_LINE_LAYOUT_REFLOW_RESULT_NOT_AWARE; nscoord dx = mReflowData.mX + kidSpacing->mMargin.left; - if (isBlock) { + if (aReflowCommand) { + nsIFrame* nextFrame; + + mSpaceManager->Translate(dx, mY); + kidReflowStatus = aReflowCommand->Next(mSpaceManager, kidRect, kidAvailSize, nextFrame); + mSpaceManager->Translate(-dx, -mY); + kidRect.x = dx; + kidRect.y = mY; + kidSize.width = kidRect.width; + kidSize.height = kidRect.height; + kidSize.ascent = kidRect.height; + kidSize.descent = 0; + + } else if (isBlock) { mSpaceManager->Translate(dx, mY); rv = mBlock->ReflowBlockChild(mKidFrame, mPresContext, mSpaceManager, kidAvailSize, kidRect, @@ -518,6 +532,94 @@ nsLineLayout::ReflowChild() return rv; } +nsresult +nsLineLayout::IncrementalReflowFromChild(nsReflowCommand& aReflowCommand, + nsIFrame* aChildFrame) +{ +#if 0 + // Get the current bounds. We'll need this to adjust the frames that follow + nsRect oldBounds; + aChildFrame->GetRect(oldBounds); +#endif + + // For the time being reflow all the children, and when we get to aChildFrame + // handle it specially + nsresult reflowStatus = NS_LINE_LAYOUT_COMPLETE; + + mLine->mBounds.x = mReflowData.mX; + mLine->mBounds.y = mY; + mKidFrame = mLine->mFirstChild; + PRInt32 kidNum = 0; + while (kidNum < mLine->mChildCount) { + // XXX Code to avoid reflowing a child goes here + nsresult childReflowStatus; + + if (mKidFrame == aChildFrame) { + childReflowStatus = ReflowChild(&aReflowCommand); + } else { + childReflowStatus = ReflowChild(nsnull); + } + + if (childReflowStatus < 0) { + reflowStatus = childReflowStatus; + goto done; + } + + switch (childReflowStatus) { + default: + case NS_LINE_LAYOUT_COMPLETE: + mPrevKidFrame = mKidFrame; + mKidFrame->GetNextSibling(mKidFrame); + mKidIndex++; + kidNum++; + break; + + case NS_LINE_LAYOUT_NOT_COMPLETE: + reflowStatus = childReflowStatus; + mPrevKidFrame = mKidFrame; + mKidFrame->GetNextSibling(mKidFrame); + kidNum++; + goto split_line; + + case NS_LINE_LAYOUT_BREAK_BEFORE: + reflowStatus = childReflowStatus; + goto split_line; + + case NS_LINE_LAYOUT_BREAK_AFTER: + reflowStatus = childReflowStatus; + mPrevKidFrame = mKidFrame; + mKidFrame->GetNextSibling(mKidFrame); + mKidIndex++; + kidNum++; + + split_line: + reflowStatus = SplitLine(childReflowStatus, mLine->mChildCount - kidNum); + goto done; + } + } + +done: + // Perform alignment operations + if (mLine->mIsBlock) { + mLineHeight = mReflowData.mMaxAscent + mReflowData.mMaxDescent; + } + else { + AlignChildren(); + } + + // Set final bounds of the line + mLine->mBounds.height = mLineHeight; + mLine->mBounds.width = mReflowData.mX - mLine->mBounds.x; + + NS_ASSERTION(((reflowStatus < 0) || + (reflowStatus == NS_LINE_LAYOUT_COMPLETE) || + (reflowStatus == NS_LINE_LAYOUT_NOT_COMPLETE) || + (reflowStatus == NS_LINE_LAYOUT_BREAK_BEFORE) || + (reflowStatus == NS_LINE_LAYOUT_BREAK_AFTER)), + "bad return status from ReflowMapped"); + return reflowStatus; +} + //---------------------------------------------------------------------- nsresult @@ -630,7 +732,7 @@ nsLineLayout::ReflowMapped() while (kidNum < mLine->mChildCount) { // XXX Code to avoid reflowing a child goes here - nsresult childReflowStatus = ReflowChild(); + nsresult childReflowStatus = ReflowChild(nsnull); if (childReflowStatus < 0) { reflowStatus = childReflowStatus; goto done; @@ -769,7 +871,7 @@ nsLineLayout::PullUpChildren() } // Try to reflow it like any other mapped child - nsresult childReflowStatus = ReflowChild(); + nsresult childReflowStatus = ReflowChild(nsnull); if (childReflowStatus < 0) { reflowStatus = childReflowStatus; goto done; @@ -942,7 +1044,7 @@ nsLineLayout::ReflowUnmapped() } // Reflow new child frame - childReflowStatus = ReflowChild(); + childReflowStatus = ReflowChild(nsnull); if (childReflowStatus < 0) { reflowStatus = childReflowStatus; goto done; diff --git a/mozilla/layout/html/base/src/nsLineLayout.h b/mozilla/layout/html/base/src/nsLineLayout.h index 953bc670ff6..193ff161950 100644 --- a/mozilla/layout/html/base/src/nsLineLayout.h +++ b/mozilla/layout/html/base/src/nsLineLayout.h @@ -100,16 +100,16 @@ struct nsLineLayout { */ nsresult ReflowLine(); + nsresult IncrementalReflowFromChild(nsReflowCommand& aReflowCommand, + nsIFrame* aChildFrame); + + // The presentation context nsIPresContext* mPresContext; // The block behind the line nsBlockFrame* mBlock; nsISpaceManager* mSpaceManager; -#if 0 - // XXX I don't think we need this anymore... - PRBool mBlockIsPseudo; -#endif nsIContent* mBlockContent; PRInt32 mKidIndex; @@ -180,7 +180,7 @@ protected: nsresult WordBreakReflow(); - nsresult ReflowChild(); + nsresult ReflowChild(nsReflowCommand* aReflowCommand); nsresult ReflowMapped();