From d5112be2e648d8e9f743e7271ff19fe867f1de19 Mon Sep 17 00:00:00 2001 From: "kipp%netscape.com" Date: Thu, 5 Nov 1998 19:33:01 +0000 Subject: [PATCH] Fixed a few more floater bugs; fixed incremental reflow bug with regards to BR tags (clear=all for example); support :first-line properly; work in progress on :first-letter git-svn-id: svn://10.0.0.236/trunk@14153 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/layout/generic/nsBlockFrame.cpp | 318 +++++++++++++++++- mozilla/layout/generic/nsBlockReflowState.cpp | 318 +++++++++++++++++- mozilla/layout/generic/nsBlockReflowState.h | 318 +++++++++++++++++- mozilla/layout/html/base/src/nsBlockFrame.cpp | 318 +++++++++++++++++- .../html/base/src/nsBlockReflowState.cpp | 318 +++++++++++++++++- .../layout/html/base/src/nsBlockReflowState.h | 318 +++++++++++++++++- 6 files changed, 1848 insertions(+), 60 deletions(-) diff --git a/mozilla/layout/generic/nsBlockFrame.cpp b/mozilla/layout/generic/nsBlockFrame.cpp index fbf5b615c7c..c57fa681885 100644 --- a/mozilla/layout/generic/nsBlockFrame.cpp +++ b/mozilla/layout/generic/nsBlockFrame.cpp @@ -49,12 +49,46 @@ #include "nsIHTMLContent.h" #include "nsHTMLImage.h" +// XXX temporary for :first-letter support +#include "nsITextContent.h" +static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID);/* XXX */ + +#ifdef NS_DEBUG +#undef NOISY_FIRST_LINE +#undef REALLY_NOISY_FIRST_LINE +#else +#undef NOISY_FIRST_LINE +#endif + +#ifdef REALLY_NOISY_FIRST_LINE +static void +DumpStyleGeneaology(nsIFrame* aFrame, const char* gap) +{ + fputs(gap, stdout); + aFrame->ListTag(stdout); + printf(": "); + nsIStyleContext* sc; + aFrame->GetStyleContext(sc); + while (nsnull != sc) { + nsIStyleContext* psc; + printf("%p ", sc); + psc = sc->GetParent(); + NS_RELEASE(sc); + sc = psc; + } + printf("\n"); +} +#endif + /* 52b33130-0b99-11d2-932e-00805f8add32 */ #define NS_BLOCK_FRAME_CID \ { 0x52b33130, 0x0b99, 0x11d2, {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID; +// 11-03-98: low hanging memory fruit: get rid of text-runs that have +// only one piece of text in them! + // 09-15-98: make sure that the outer container of the block (e.g. the // body sets up the outer top margin feed in properly so that the top // margin collapses properly with the body margin. Note that for the @@ -133,7 +167,7 @@ struct nsBlockReflowState : public nsFrameReflowState { void PlaceFloater(nsPlaceholderFrame* aFloater, PRBool& aIsLeftFloater); - void PlaceFloaters(nsVoidArray* aFloaters); + void PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem); void ClearFloaters(PRUint8 aBreakType); @@ -154,6 +188,7 @@ struct nsBlockReflowState : public nsFrameReflowState { PRUintn mPrevMarginFlags; // previous margin flag bits (above and beyond the carried-out-margin flags) #define HAVE_CARRIED_MARGIN 0x100 +#define ALREADY_APPLIED_BOTTOM_MARGIN 0x200 nscoord mBottomEdge; // maximum Y @@ -222,6 +257,8 @@ public: // nsIFrame NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList); + NS_IMETHOD ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext); NS_IMETHOD FirstChild(nsIFrame*& aFirstChild) const; NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext); NS_IMETHOD IsSplittable(nsSplittableType& aIsSplittable) const; @@ -280,6 +317,8 @@ public: virtual PRBool DeleteChildsNextInFlow(nsIPresContext& aPresContext, nsIFrame* aNextInFlow); + void RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame); + void SetFlags(PRUint32 aFlags) { mFlags = aFlags; } @@ -390,6 +429,9 @@ public: PRBool IsChild(nsIFrame* aFrame); #endif + nsIStyleContext* mFirstLineStyle; + nsIStyleContext* mFirstLetterStyle; + LineData* mLines; LineData* mOverflowLines; @@ -871,6 +913,7 @@ struct LineData { mCombinedArea.SetRect(0,0,0,0); mCarriedOutTopMargin = 0; mCarriedOutBottomMargin = 0; + mBreakType = NS_STYLE_CLEAR_NONE; } ~LineData(); @@ -1005,6 +1048,7 @@ struct LineData { nsIFrame* mFirstChild; PRUint16 mChildCount; PRUint16 mState; + PRUint8 mBreakType; nsRect mBounds; nsRect mCombinedArea; nscoord mCarriedOutTopMargin; @@ -1466,6 +1510,8 @@ nsBlockFrame::nsBlockFrame(nsIContent* aContent, nsIFrame* aParent) nsBlockFrame::~nsBlockFrame() { + NS_IF_RELEASE(mFirstLineStyle); + NS_IF_RELEASE(mFirstLetterStyle); nsTextRun::DeleteTextRuns(mTextRuns); } @@ -1493,6 +1539,81 @@ nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr); } +NS_IMETHODIMP +nsBlockFrame::ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext) +{ + nsIStyleContext* oldContext = mStyleContext; + nsresult rv = nsFrame::ReResolveStyleContext(aPresContext, aParentContext); + if (NS_OK != rv) { + return rv; + } + if (oldContext != mStyleContext) { + // Re-resolve the :first-line pseudo style context + if (nsnull == mPrevInFlow) { + nsIStyleContext* newFirstLineStyle = + aPresContext->ProbePseudoStyleContextFor(mContent, + nsHTMLAtoms::firstLinePseudo, + mStyleContext); + if (newFirstLineStyle != mFirstLineStyle) { + NS_IF_RELEASE(mFirstLineStyle); + mFirstLineStyle = newFirstLineStyle; + } + else { + NS_IF_RELEASE(newFirstLineStyle); + } + + // Re-resolve the :first-letter pseudo style context + nsIStyleContext* newFirstLetterStyle = + aPresContext->ProbePseudoStyleContextFor(mContent, + nsHTMLAtoms::firstLetterPseudo, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + if (newFirstLetterStyle != mFirstLetterStyle) { + NS_IF_RELEASE(mFirstLetterStyle); + mFirstLetterStyle = newFirstLetterStyle; + } + else { + NS_IF_RELEASE(newFirstLetterStyle); + } + } + + // Update the child frames on each line + LineData* line = mLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + PRInt32 n = line->mChildCount; + while ((--n >= 0) && NS_SUCCEEDED(rv)) { + if (line == mLines) { + rv = child->ReResolveStyleContext(aPresContext, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + } + else { + rv = child->ReResolveStyleContext(aPresContext, mStyleContext); + } + child->GetNextSibling(child); + } + line = line->mNext; + } + + // Update the child frames on each overflow line too + line = mOverflowLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + PRInt32 n = line->mChildCount; + while ((--n >= 0) && NS_SUCCEEDED(rv)) { + rv = child->ReResolveStyleContext(aPresContext, mStyleContext); + child->GetNextSibling(child); + } + line = line->mNext; + } + } + return rv; +} + NS_IMETHODIMP nsBlockFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) { @@ -1527,11 +1648,23 @@ nsBlockFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) // it to the flow now. const nsStyleList* styleList; GetStyleData(eStyleStruct_List, (const nsStyleStruct*&) styleList); -//XXX ListTag(stdout); printf(": lsp=%d\n", styleList->mListStylePosition); if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == styleList->mListStylePosition) { InsertNewFrame(aPresContext, this, mBullet, nsnull); } } + + // Lookup up the two pseudo style contexts + if (nsnull == mPrevInFlow) { + mFirstLineStyle = aPresContext. + ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLinePseudo, + mStyleContext); + mFirstLetterStyle = aPresContext. + ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLetterPseudo, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + } + return NS_OK; } @@ -1744,7 +1877,6 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext, nsInlineReflow inlineReflow(state.mLineLayout, state, this, PR_TRUE); state.mInlineReflow = &inlineReflow; state.mLineLayout.PushInline(&inlineReflow); -// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F'); nsresult rv = NS_OK; if (eReflowReason_Initial == state.reason) { @@ -2302,10 +2434,40 @@ nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState) } if (nsnull != lastCleanLine) { - // Place any floaters the line has + // Unplace any floaters on the lastCleanLine and every line that + // follows it (which shouldn't be much given that this is + // frame-appended reflow) + LineData* line = lastCleanLine; + while (nsnull != line) { + line->UnplaceFloaters(aState.mSpaceManager); + line = line->mNext; + } + + // Place any floaters the last clean line has if (nsnull != lastCleanLine->mFloaters) { aState.mCurrentLine = lastCleanLine; - aState.PlaceFloaters(lastCleanLine->mFloaters); + aState.PlaceFloaters(lastCleanLine->mFloaters, PR_TRUE); + } + + // Apply any clear-after semantics the line might have + nscoord y0, dy; + switch (lastCleanLine->mBreakType) { + case NS_STYLE_CLEAR_LEFT: + case NS_STYLE_CLEAR_RIGHT: + case NS_STYLE_CLEAR_LEFT_AND_RIGHT: + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, + ("line %p: ClearFloaters: y=%d, break-type=%d\n", + lastCleanLine, aState.mY, lastCleanLine->mBreakType)); + y0 = aState.mY; + aState.ClearFloaters(lastCleanLine->mBreakType); + dy = aState.mY - y0; + if (dy > aState.mPrevBottomMargin) { + aState.mPrevBottomMargin = dy; + aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, + (" cleared floaters, now y=%d\n", aState.mY)); + break; } } @@ -2393,6 +2555,7 @@ nsBlockFrame::FrameInsertedReflow(nsBlockReflowState& aState) // other lines can be moved; recover state before first dirty line. // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2512,6 +2675,7 @@ nsBlockFrame::FrameRemovedReflow(nsBlockReflowState& aState) // other lines can be moved; recover state before first dirty line. // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2538,6 +2702,7 @@ nsBlockFrame::ChildIncrementalReflow(nsBlockReflowState& aState) #endif // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2547,6 +2712,7 @@ nsresult nsBlockFrame::StyleChangedReflow(nsBlockReflowState& aState) { // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2882,6 +3048,15 @@ nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState, } aState.mInlineReflow->Init(x, aState.mY, availWidth, availHeight); aState.mInlineReflowPrepared = PR_TRUE; + + // Setup the first-letter-style-ok flag + nsLineLayout& lineLayout = aState.mLineLayout; + if (mFirstLetterStyle && (0 == lineLayout.GetLineNumber())) { + lineLayout.SetFirstLetterStyleOK(PR_TRUE); + } + else { + lineLayout.SetFirstLetterStyleOK(PR_FALSE); + } } // XXX switch to two versions: inline vs. block? @@ -3201,8 +3376,11 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Refresh our available space in case a floater was placed by our // child. // XXX expensive! + // XXX unnecessary because the child block will contain the floater! aState.GetAvailableSpace(); + aLine->mBreakType = NS_STYLE_CLEAR_NONE; + if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) { // Some of the block fit. We need to have the block frame // continued, so we make sure that it has a next-in-flow now. @@ -3260,6 +3438,50 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, PrepareInlineReflow(aState, aFrame, PR_FALSE); } + // When reflowing a frame that is on the first-line, check and see + // if a special style context should be placed in the context chain. + PRBool reflowingFirstLetter = PR_FALSE; + if ((nsnull == mPrevInFlow) && + (0 == aState.mLineLayout.GetLineNumber())) { + if (nsnull != mFirstLineStyle) { + // Update the child frames style to inherit from the first-line + // style. + // XXX add code to check first and only do it if it needs doing! +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, ""); +#endif +#ifdef NOISY_FIRST_LINE + aFrame->ListTag(stdout); + printf(": adding in first-line style\n"); +#endif + aFrame->ReResolveStyleContext(&aState.mPresContext, mFirstLineStyle); +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, " "); +#endif + } + if ((nsnull != mFirstLetterStyle) && + aState.mLineLayout.GetFirstLetterStyleOK()) { + // XXX For now, we only allow text objects to have :first-letter + // style. + nsIContent* c; + nsresult rv = aFrame->GetContent(c); + if (NS_SUCCEEDED(rv) && (nsnull != c)) { + nsITextContent* tc; + nsresult rv = c->QueryInterface(kITextContentIID, (void**)&tc); + if (NS_SUCCEEDED(rv)) { + // XXX add code to check first and only do it if it needs doing! + // Update the child frames style to inherit from the + // first-letter style. + aFrame->ReResolveStyleContext(&aState.mPresContext, + mFirstLetterStyle); + reflowingFirstLetter = PR_TRUE; + NS_RELEASE(tc); + } + NS_RELEASE(c); + } + } + } + PRBool isFirstChild = (aLine == mLines) && (aFrame == aLine->mFirstChild); aState.mInlineReflow->SetIsFirstChild(isFirstChild); @@ -3292,6 +3514,17 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, aLine->mChildCount++; } aFrame->GetNextSibling(aFrame); + +#if 0 + // XXX Special hackery to keep going if we just split because of a + // :first-letter situation. + if (reflowingFirstLetter) { + if (aState.mInlineReflow->GetAvailWidth() > 0) { + aLine->SetLastContentIsComplete();/* XXX what to do??? */ + return PR_TRUE; + } + } +#endif } else { if (NS_INLINE_IS_BREAK_AFTER(aReflowResult)) { @@ -3331,6 +3564,37 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, return PR_FALSE; } +void +nsBlockFrame::RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame) +{ +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, ""); +#endif + + nsIStyleContext* kidSC; + aFrame->GetStyleContext(kidSC); + if (nsnull != kidSC) { + nsIStyleContext* kidParentSC; + kidParentSC = kidSC->GetParent(); + if ((kidParentSC == mFirstLineStyle) || + (kidParentSC == mFirstLetterStyle)) { +#ifdef NOISY_FIRST_LINE + printf(" "); + aFrame->ListTag(stdout); + printf(": removing first-line style\n"); +#endif + + aFrame->ReResolveStyleContext(&aPresContext, mStyleContext); + +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, " "); +#endif + } + NS_IF_RELEASE(kidParentSC); + NS_RELEASE(kidSC); + } +} + // XXX alloc lines using free-list in aState // XXX refactor this since the split NEVER has to deal with blocks @@ -3344,8 +3608,8 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, PRInt32 pushCount = aLine->ChildCount() - aState.mInlineReflow->GetCurrentFrameNum(); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, - ("nsBlockFrame::SplitLine: pushing %d frames", - pushCount)); + ("nsBlockFrame::SplitLine: pushing %d frames", + pushCount)); if (0 != pushCount) { NS_ASSERTION(nsnull != aFrame, "whoops"); LineData* to = aLine->mNext; @@ -3382,6 +3646,19 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, // Let inline reflow know that some frames are no longer part of // its state. aState.mInlineReflow->ChangeFrameCount(aLine->ChildCount()); + + // Repair style contexts when frames move to the next line and + // this is the first line. + if (((nsnull != mFirstLineStyle) || (nsnull != mFirstLetterStyle)) && + (0 == aState.mLineLayout.GetLineNumber())) { + // Take the pseudo-style out of the style context chain + nsIFrame* kid = to->mFirstChild; + PRInt32 n = pushCount; + while (--n >= 0) { + RestoreStyleFor(aState.mPresContext, kid); + kid->GetNextSibling(kid); + } + } } return NS_OK; } @@ -3671,7 +3948,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Any below current line floaters to place? if (0 != aState.mPendingFloaters.Count()) { - aState.PlaceFloaters(&aState.mPendingFloaters); + aState.PlaceFloaters(&aState.mPendingFloaters, PR_FALSE); aState.mPendingFloaters.Clear(); // XXX Factor in the height of the floaters as well when considering @@ -3682,8 +3959,10 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Based on the last child we reflowed reflow status, we may need to // clear past any floaters. + aLine->mBreakType = NS_STYLE_CLEAR_NONE; if (NS_INLINE_IS_BREAK_AFTER(aReflowStatus)) { PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(aReflowStatus); + aLine->mBreakType = breakType; // Apply break to the line switch (breakType) { default: @@ -3694,6 +3973,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("nsBlockFrame::PlaceLine: clearing floaters=%d", breakType)); +#if XXX { nscoord y0 = aState.mY; aState.ClearFloaters(breakType); @@ -3702,6 +3982,18 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aLine->mCarriedOutBottomMargin = dy; } } +#else + aState.ClearFloaters(breakType); +#if 0 + y0 = aState.mY; + aState.ClearFloaters(lastCleanLine->mBreakType); + dy = aState.mY - y0; + if (dy > aState.mPrevBottomMargin) { + aState.mPrevBottomMargin = dy; + aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } +#endif +#endif break; } // XXX page breaks, etc, need to be passed upwards too! @@ -4273,6 +4565,12 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder) ("nsBlockReflowState::AddFloater: IsLeftMostChild, placeHolder=%p", aPlaceholder)); + // Flush out pending bottom margin before placing floater + if (0 != mPrevBottomMargin) { + mY += mPrevBottomMargin; + mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } + // Because we are in the middle of reflowing a placeholder frame // within a line (and possibly nested in an inline frame or two // that's a child of our block) we need to restore the space @@ -4456,7 +4754,7 @@ nsBlockReflowState::PlaceFloater(nsPlaceholderFrame* aPlaceholder, * Place below-current-line floaters. */ void -nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters) +nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem) { NS_PRECONDITION(aFloaters->Count() > 0, "no floaters"); @@ -4464,7 +4762,7 @@ nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters) for (PRInt32 i = 0; i < numFloaters; i++) { nsPlaceholderFrame* placeholderFrame = (nsPlaceholderFrame*) aFloaters->ElementAt(i); - if (IsLeftMostChild(placeholderFrame)) { + if (!aAllOfThem && IsLeftMostChild(placeholderFrame)) { // Left-most children are placed during the line's reflow continue; } diff --git a/mozilla/layout/generic/nsBlockReflowState.cpp b/mozilla/layout/generic/nsBlockReflowState.cpp index fbf5b615c7c..c57fa681885 100644 --- a/mozilla/layout/generic/nsBlockReflowState.cpp +++ b/mozilla/layout/generic/nsBlockReflowState.cpp @@ -49,12 +49,46 @@ #include "nsIHTMLContent.h" #include "nsHTMLImage.h" +// XXX temporary for :first-letter support +#include "nsITextContent.h" +static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID);/* XXX */ + +#ifdef NS_DEBUG +#undef NOISY_FIRST_LINE +#undef REALLY_NOISY_FIRST_LINE +#else +#undef NOISY_FIRST_LINE +#endif + +#ifdef REALLY_NOISY_FIRST_LINE +static void +DumpStyleGeneaology(nsIFrame* aFrame, const char* gap) +{ + fputs(gap, stdout); + aFrame->ListTag(stdout); + printf(": "); + nsIStyleContext* sc; + aFrame->GetStyleContext(sc); + while (nsnull != sc) { + nsIStyleContext* psc; + printf("%p ", sc); + psc = sc->GetParent(); + NS_RELEASE(sc); + sc = psc; + } + printf("\n"); +} +#endif + /* 52b33130-0b99-11d2-932e-00805f8add32 */ #define NS_BLOCK_FRAME_CID \ { 0x52b33130, 0x0b99, 0x11d2, {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID; +// 11-03-98: low hanging memory fruit: get rid of text-runs that have +// only one piece of text in them! + // 09-15-98: make sure that the outer container of the block (e.g. the // body sets up the outer top margin feed in properly so that the top // margin collapses properly with the body margin. Note that for the @@ -133,7 +167,7 @@ struct nsBlockReflowState : public nsFrameReflowState { void PlaceFloater(nsPlaceholderFrame* aFloater, PRBool& aIsLeftFloater); - void PlaceFloaters(nsVoidArray* aFloaters); + void PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem); void ClearFloaters(PRUint8 aBreakType); @@ -154,6 +188,7 @@ struct nsBlockReflowState : public nsFrameReflowState { PRUintn mPrevMarginFlags; // previous margin flag bits (above and beyond the carried-out-margin flags) #define HAVE_CARRIED_MARGIN 0x100 +#define ALREADY_APPLIED_BOTTOM_MARGIN 0x200 nscoord mBottomEdge; // maximum Y @@ -222,6 +257,8 @@ public: // nsIFrame NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList); + NS_IMETHOD ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext); NS_IMETHOD FirstChild(nsIFrame*& aFirstChild) const; NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext); NS_IMETHOD IsSplittable(nsSplittableType& aIsSplittable) const; @@ -280,6 +317,8 @@ public: virtual PRBool DeleteChildsNextInFlow(nsIPresContext& aPresContext, nsIFrame* aNextInFlow); + void RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame); + void SetFlags(PRUint32 aFlags) { mFlags = aFlags; } @@ -390,6 +429,9 @@ public: PRBool IsChild(nsIFrame* aFrame); #endif + nsIStyleContext* mFirstLineStyle; + nsIStyleContext* mFirstLetterStyle; + LineData* mLines; LineData* mOverflowLines; @@ -871,6 +913,7 @@ struct LineData { mCombinedArea.SetRect(0,0,0,0); mCarriedOutTopMargin = 0; mCarriedOutBottomMargin = 0; + mBreakType = NS_STYLE_CLEAR_NONE; } ~LineData(); @@ -1005,6 +1048,7 @@ struct LineData { nsIFrame* mFirstChild; PRUint16 mChildCount; PRUint16 mState; + PRUint8 mBreakType; nsRect mBounds; nsRect mCombinedArea; nscoord mCarriedOutTopMargin; @@ -1466,6 +1510,8 @@ nsBlockFrame::nsBlockFrame(nsIContent* aContent, nsIFrame* aParent) nsBlockFrame::~nsBlockFrame() { + NS_IF_RELEASE(mFirstLineStyle); + NS_IF_RELEASE(mFirstLetterStyle); nsTextRun::DeleteTextRuns(mTextRuns); } @@ -1493,6 +1539,81 @@ nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr); } +NS_IMETHODIMP +nsBlockFrame::ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext) +{ + nsIStyleContext* oldContext = mStyleContext; + nsresult rv = nsFrame::ReResolveStyleContext(aPresContext, aParentContext); + if (NS_OK != rv) { + return rv; + } + if (oldContext != mStyleContext) { + // Re-resolve the :first-line pseudo style context + if (nsnull == mPrevInFlow) { + nsIStyleContext* newFirstLineStyle = + aPresContext->ProbePseudoStyleContextFor(mContent, + nsHTMLAtoms::firstLinePseudo, + mStyleContext); + if (newFirstLineStyle != mFirstLineStyle) { + NS_IF_RELEASE(mFirstLineStyle); + mFirstLineStyle = newFirstLineStyle; + } + else { + NS_IF_RELEASE(newFirstLineStyle); + } + + // Re-resolve the :first-letter pseudo style context + nsIStyleContext* newFirstLetterStyle = + aPresContext->ProbePseudoStyleContextFor(mContent, + nsHTMLAtoms::firstLetterPseudo, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + if (newFirstLetterStyle != mFirstLetterStyle) { + NS_IF_RELEASE(mFirstLetterStyle); + mFirstLetterStyle = newFirstLetterStyle; + } + else { + NS_IF_RELEASE(newFirstLetterStyle); + } + } + + // Update the child frames on each line + LineData* line = mLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + PRInt32 n = line->mChildCount; + while ((--n >= 0) && NS_SUCCEEDED(rv)) { + if (line == mLines) { + rv = child->ReResolveStyleContext(aPresContext, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + } + else { + rv = child->ReResolveStyleContext(aPresContext, mStyleContext); + } + child->GetNextSibling(child); + } + line = line->mNext; + } + + // Update the child frames on each overflow line too + line = mOverflowLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + PRInt32 n = line->mChildCount; + while ((--n >= 0) && NS_SUCCEEDED(rv)) { + rv = child->ReResolveStyleContext(aPresContext, mStyleContext); + child->GetNextSibling(child); + } + line = line->mNext; + } + } + return rv; +} + NS_IMETHODIMP nsBlockFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) { @@ -1527,11 +1648,23 @@ nsBlockFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) // it to the flow now. const nsStyleList* styleList; GetStyleData(eStyleStruct_List, (const nsStyleStruct*&) styleList); -//XXX ListTag(stdout); printf(": lsp=%d\n", styleList->mListStylePosition); if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == styleList->mListStylePosition) { InsertNewFrame(aPresContext, this, mBullet, nsnull); } } + + // Lookup up the two pseudo style contexts + if (nsnull == mPrevInFlow) { + mFirstLineStyle = aPresContext. + ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLinePseudo, + mStyleContext); + mFirstLetterStyle = aPresContext. + ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLetterPseudo, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + } + return NS_OK; } @@ -1744,7 +1877,6 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext, nsInlineReflow inlineReflow(state.mLineLayout, state, this, PR_TRUE); state.mInlineReflow = &inlineReflow; state.mLineLayout.PushInline(&inlineReflow); -// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F'); nsresult rv = NS_OK; if (eReflowReason_Initial == state.reason) { @@ -2302,10 +2434,40 @@ nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState) } if (nsnull != lastCleanLine) { - // Place any floaters the line has + // Unplace any floaters on the lastCleanLine and every line that + // follows it (which shouldn't be much given that this is + // frame-appended reflow) + LineData* line = lastCleanLine; + while (nsnull != line) { + line->UnplaceFloaters(aState.mSpaceManager); + line = line->mNext; + } + + // Place any floaters the last clean line has if (nsnull != lastCleanLine->mFloaters) { aState.mCurrentLine = lastCleanLine; - aState.PlaceFloaters(lastCleanLine->mFloaters); + aState.PlaceFloaters(lastCleanLine->mFloaters, PR_TRUE); + } + + // Apply any clear-after semantics the line might have + nscoord y0, dy; + switch (lastCleanLine->mBreakType) { + case NS_STYLE_CLEAR_LEFT: + case NS_STYLE_CLEAR_RIGHT: + case NS_STYLE_CLEAR_LEFT_AND_RIGHT: + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, + ("line %p: ClearFloaters: y=%d, break-type=%d\n", + lastCleanLine, aState.mY, lastCleanLine->mBreakType)); + y0 = aState.mY; + aState.ClearFloaters(lastCleanLine->mBreakType); + dy = aState.mY - y0; + if (dy > aState.mPrevBottomMargin) { + aState.mPrevBottomMargin = dy; + aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, + (" cleared floaters, now y=%d\n", aState.mY)); + break; } } @@ -2393,6 +2555,7 @@ nsBlockFrame::FrameInsertedReflow(nsBlockReflowState& aState) // other lines can be moved; recover state before first dirty line. // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2512,6 +2675,7 @@ nsBlockFrame::FrameRemovedReflow(nsBlockReflowState& aState) // other lines can be moved; recover state before first dirty line. // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2538,6 +2702,7 @@ nsBlockFrame::ChildIncrementalReflow(nsBlockReflowState& aState) #endif // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2547,6 +2712,7 @@ nsresult nsBlockFrame::StyleChangedReflow(nsBlockReflowState& aState) { // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2882,6 +3048,15 @@ nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState, } aState.mInlineReflow->Init(x, aState.mY, availWidth, availHeight); aState.mInlineReflowPrepared = PR_TRUE; + + // Setup the first-letter-style-ok flag + nsLineLayout& lineLayout = aState.mLineLayout; + if (mFirstLetterStyle && (0 == lineLayout.GetLineNumber())) { + lineLayout.SetFirstLetterStyleOK(PR_TRUE); + } + else { + lineLayout.SetFirstLetterStyleOK(PR_FALSE); + } } // XXX switch to two versions: inline vs. block? @@ -3201,8 +3376,11 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Refresh our available space in case a floater was placed by our // child. // XXX expensive! + // XXX unnecessary because the child block will contain the floater! aState.GetAvailableSpace(); + aLine->mBreakType = NS_STYLE_CLEAR_NONE; + if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) { // Some of the block fit. We need to have the block frame // continued, so we make sure that it has a next-in-flow now. @@ -3260,6 +3438,50 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, PrepareInlineReflow(aState, aFrame, PR_FALSE); } + // When reflowing a frame that is on the first-line, check and see + // if a special style context should be placed in the context chain. + PRBool reflowingFirstLetter = PR_FALSE; + if ((nsnull == mPrevInFlow) && + (0 == aState.mLineLayout.GetLineNumber())) { + if (nsnull != mFirstLineStyle) { + // Update the child frames style to inherit from the first-line + // style. + // XXX add code to check first and only do it if it needs doing! +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, ""); +#endif +#ifdef NOISY_FIRST_LINE + aFrame->ListTag(stdout); + printf(": adding in first-line style\n"); +#endif + aFrame->ReResolveStyleContext(&aState.mPresContext, mFirstLineStyle); +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, " "); +#endif + } + if ((nsnull != mFirstLetterStyle) && + aState.mLineLayout.GetFirstLetterStyleOK()) { + // XXX For now, we only allow text objects to have :first-letter + // style. + nsIContent* c; + nsresult rv = aFrame->GetContent(c); + if (NS_SUCCEEDED(rv) && (nsnull != c)) { + nsITextContent* tc; + nsresult rv = c->QueryInterface(kITextContentIID, (void**)&tc); + if (NS_SUCCEEDED(rv)) { + // XXX add code to check first and only do it if it needs doing! + // Update the child frames style to inherit from the + // first-letter style. + aFrame->ReResolveStyleContext(&aState.mPresContext, + mFirstLetterStyle); + reflowingFirstLetter = PR_TRUE; + NS_RELEASE(tc); + } + NS_RELEASE(c); + } + } + } + PRBool isFirstChild = (aLine == mLines) && (aFrame == aLine->mFirstChild); aState.mInlineReflow->SetIsFirstChild(isFirstChild); @@ -3292,6 +3514,17 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, aLine->mChildCount++; } aFrame->GetNextSibling(aFrame); + +#if 0 + // XXX Special hackery to keep going if we just split because of a + // :first-letter situation. + if (reflowingFirstLetter) { + if (aState.mInlineReflow->GetAvailWidth() > 0) { + aLine->SetLastContentIsComplete();/* XXX what to do??? */ + return PR_TRUE; + } + } +#endif } else { if (NS_INLINE_IS_BREAK_AFTER(aReflowResult)) { @@ -3331,6 +3564,37 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, return PR_FALSE; } +void +nsBlockFrame::RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame) +{ +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, ""); +#endif + + nsIStyleContext* kidSC; + aFrame->GetStyleContext(kidSC); + if (nsnull != kidSC) { + nsIStyleContext* kidParentSC; + kidParentSC = kidSC->GetParent(); + if ((kidParentSC == mFirstLineStyle) || + (kidParentSC == mFirstLetterStyle)) { +#ifdef NOISY_FIRST_LINE + printf(" "); + aFrame->ListTag(stdout); + printf(": removing first-line style\n"); +#endif + + aFrame->ReResolveStyleContext(&aPresContext, mStyleContext); + +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, " "); +#endif + } + NS_IF_RELEASE(kidParentSC); + NS_RELEASE(kidSC); + } +} + // XXX alloc lines using free-list in aState // XXX refactor this since the split NEVER has to deal with blocks @@ -3344,8 +3608,8 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, PRInt32 pushCount = aLine->ChildCount() - aState.mInlineReflow->GetCurrentFrameNum(); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, - ("nsBlockFrame::SplitLine: pushing %d frames", - pushCount)); + ("nsBlockFrame::SplitLine: pushing %d frames", + pushCount)); if (0 != pushCount) { NS_ASSERTION(nsnull != aFrame, "whoops"); LineData* to = aLine->mNext; @@ -3382,6 +3646,19 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, // Let inline reflow know that some frames are no longer part of // its state. aState.mInlineReflow->ChangeFrameCount(aLine->ChildCount()); + + // Repair style contexts when frames move to the next line and + // this is the first line. + if (((nsnull != mFirstLineStyle) || (nsnull != mFirstLetterStyle)) && + (0 == aState.mLineLayout.GetLineNumber())) { + // Take the pseudo-style out of the style context chain + nsIFrame* kid = to->mFirstChild; + PRInt32 n = pushCount; + while (--n >= 0) { + RestoreStyleFor(aState.mPresContext, kid); + kid->GetNextSibling(kid); + } + } } return NS_OK; } @@ -3671,7 +3948,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Any below current line floaters to place? if (0 != aState.mPendingFloaters.Count()) { - aState.PlaceFloaters(&aState.mPendingFloaters); + aState.PlaceFloaters(&aState.mPendingFloaters, PR_FALSE); aState.mPendingFloaters.Clear(); // XXX Factor in the height of the floaters as well when considering @@ -3682,8 +3959,10 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Based on the last child we reflowed reflow status, we may need to // clear past any floaters. + aLine->mBreakType = NS_STYLE_CLEAR_NONE; if (NS_INLINE_IS_BREAK_AFTER(aReflowStatus)) { PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(aReflowStatus); + aLine->mBreakType = breakType; // Apply break to the line switch (breakType) { default: @@ -3694,6 +3973,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("nsBlockFrame::PlaceLine: clearing floaters=%d", breakType)); +#if XXX { nscoord y0 = aState.mY; aState.ClearFloaters(breakType); @@ -3702,6 +3982,18 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aLine->mCarriedOutBottomMargin = dy; } } +#else + aState.ClearFloaters(breakType); +#if 0 + y0 = aState.mY; + aState.ClearFloaters(lastCleanLine->mBreakType); + dy = aState.mY - y0; + if (dy > aState.mPrevBottomMargin) { + aState.mPrevBottomMargin = dy; + aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } +#endif +#endif break; } // XXX page breaks, etc, need to be passed upwards too! @@ -4273,6 +4565,12 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder) ("nsBlockReflowState::AddFloater: IsLeftMostChild, placeHolder=%p", aPlaceholder)); + // Flush out pending bottom margin before placing floater + if (0 != mPrevBottomMargin) { + mY += mPrevBottomMargin; + mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } + // Because we are in the middle of reflowing a placeholder frame // within a line (and possibly nested in an inline frame or two // that's a child of our block) we need to restore the space @@ -4456,7 +4754,7 @@ nsBlockReflowState::PlaceFloater(nsPlaceholderFrame* aPlaceholder, * Place below-current-line floaters. */ void -nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters) +nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem) { NS_PRECONDITION(aFloaters->Count() > 0, "no floaters"); @@ -4464,7 +4762,7 @@ nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters) for (PRInt32 i = 0; i < numFloaters; i++) { nsPlaceholderFrame* placeholderFrame = (nsPlaceholderFrame*) aFloaters->ElementAt(i); - if (IsLeftMostChild(placeholderFrame)) { + if (!aAllOfThem && IsLeftMostChild(placeholderFrame)) { // Left-most children are placed during the line's reflow continue; } diff --git a/mozilla/layout/generic/nsBlockReflowState.h b/mozilla/layout/generic/nsBlockReflowState.h index fbf5b615c7c..c57fa681885 100644 --- a/mozilla/layout/generic/nsBlockReflowState.h +++ b/mozilla/layout/generic/nsBlockReflowState.h @@ -49,12 +49,46 @@ #include "nsIHTMLContent.h" #include "nsHTMLImage.h" +// XXX temporary for :first-letter support +#include "nsITextContent.h" +static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID);/* XXX */ + +#ifdef NS_DEBUG +#undef NOISY_FIRST_LINE +#undef REALLY_NOISY_FIRST_LINE +#else +#undef NOISY_FIRST_LINE +#endif + +#ifdef REALLY_NOISY_FIRST_LINE +static void +DumpStyleGeneaology(nsIFrame* aFrame, const char* gap) +{ + fputs(gap, stdout); + aFrame->ListTag(stdout); + printf(": "); + nsIStyleContext* sc; + aFrame->GetStyleContext(sc); + while (nsnull != sc) { + nsIStyleContext* psc; + printf("%p ", sc); + psc = sc->GetParent(); + NS_RELEASE(sc); + sc = psc; + } + printf("\n"); +} +#endif + /* 52b33130-0b99-11d2-932e-00805f8add32 */ #define NS_BLOCK_FRAME_CID \ { 0x52b33130, 0x0b99, 0x11d2, {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID; +// 11-03-98: low hanging memory fruit: get rid of text-runs that have +// only one piece of text in them! + // 09-15-98: make sure that the outer container of the block (e.g. the // body sets up the outer top margin feed in properly so that the top // margin collapses properly with the body margin. Note that for the @@ -133,7 +167,7 @@ struct nsBlockReflowState : public nsFrameReflowState { void PlaceFloater(nsPlaceholderFrame* aFloater, PRBool& aIsLeftFloater); - void PlaceFloaters(nsVoidArray* aFloaters); + void PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem); void ClearFloaters(PRUint8 aBreakType); @@ -154,6 +188,7 @@ struct nsBlockReflowState : public nsFrameReflowState { PRUintn mPrevMarginFlags; // previous margin flag bits (above and beyond the carried-out-margin flags) #define HAVE_CARRIED_MARGIN 0x100 +#define ALREADY_APPLIED_BOTTOM_MARGIN 0x200 nscoord mBottomEdge; // maximum Y @@ -222,6 +257,8 @@ public: // nsIFrame NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList); + NS_IMETHOD ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext); NS_IMETHOD FirstChild(nsIFrame*& aFirstChild) const; NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext); NS_IMETHOD IsSplittable(nsSplittableType& aIsSplittable) const; @@ -280,6 +317,8 @@ public: virtual PRBool DeleteChildsNextInFlow(nsIPresContext& aPresContext, nsIFrame* aNextInFlow); + void RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame); + void SetFlags(PRUint32 aFlags) { mFlags = aFlags; } @@ -390,6 +429,9 @@ public: PRBool IsChild(nsIFrame* aFrame); #endif + nsIStyleContext* mFirstLineStyle; + nsIStyleContext* mFirstLetterStyle; + LineData* mLines; LineData* mOverflowLines; @@ -871,6 +913,7 @@ struct LineData { mCombinedArea.SetRect(0,0,0,0); mCarriedOutTopMargin = 0; mCarriedOutBottomMargin = 0; + mBreakType = NS_STYLE_CLEAR_NONE; } ~LineData(); @@ -1005,6 +1048,7 @@ struct LineData { nsIFrame* mFirstChild; PRUint16 mChildCount; PRUint16 mState; + PRUint8 mBreakType; nsRect mBounds; nsRect mCombinedArea; nscoord mCarriedOutTopMargin; @@ -1466,6 +1510,8 @@ nsBlockFrame::nsBlockFrame(nsIContent* aContent, nsIFrame* aParent) nsBlockFrame::~nsBlockFrame() { + NS_IF_RELEASE(mFirstLineStyle); + NS_IF_RELEASE(mFirstLetterStyle); nsTextRun::DeleteTextRuns(mTextRuns); } @@ -1493,6 +1539,81 @@ nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr); } +NS_IMETHODIMP +nsBlockFrame::ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext) +{ + nsIStyleContext* oldContext = mStyleContext; + nsresult rv = nsFrame::ReResolveStyleContext(aPresContext, aParentContext); + if (NS_OK != rv) { + return rv; + } + if (oldContext != mStyleContext) { + // Re-resolve the :first-line pseudo style context + if (nsnull == mPrevInFlow) { + nsIStyleContext* newFirstLineStyle = + aPresContext->ProbePseudoStyleContextFor(mContent, + nsHTMLAtoms::firstLinePseudo, + mStyleContext); + if (newFirstLineStyle != mFirstLineStyle) { + NS_IF_RELEASE(mFirstLineStyle); + mFirstLineStyle = newFirstLineStyle; + } + else { + NS_IF_RELEASE(newFirstLineStyle); + } + + // Re-resolve the :first-letter pseudo style context + nsIStyleContext* newFirstLetterStyle = + aPresContext->ProbePseudoStyleContextFor(mContent, + nsHTMLAtoms::firstLetterPseudo, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + if (newFirstLetterStyle != mFirstLetterStyle) { + NS_IF_RELEASE(mFirstLetterStyle); + mFirstLetterStyle = newFirstLetterStyle; + } + else { + NS_IF_RELEASE(newFirstLetterStyle); + } + } + + // Update the child frames on each line + LineData* line = mLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + PRInt32 n = line->mChildCount; + while ((--n >= 0) && NS_SUCCEEDED(rv)) { + if (line == mLines) { + rv = child->ReResolveStyleContext(aPresContext, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + } + else { + rv = child->ReResolveStyleContext(aPresContext, mStyleContext); + } + child->GetNextSibling(child); + } + line = line->mNext; + } + + // Update the child frames on each overflow line too + line = mOverflowLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + PRInt32 n = line->mChildCount; + while ((--n >= 0) && NS_SUCCEEDED(rv)) { + rv = child->ReResolveStyleContext(aPresContext, mStyleContext); + child->GetNextSibling(child); + } + line = line->mNext; + } + } + return rv; +} + NS_IMETHODIMP nsBlockFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) { @@ -1527,11 +1648,23 @@ nsBlockFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) // it to the flow now. const nsStyleList* styleList; GetStyleData(eStyleStruct_List, (const nsStyleStruct*&) styleList); -//XXX ListTag(stdout); printf(": lsp=%d\n", styleList->mListStylePosition); if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == styleList->mListStylePosition) { InsertNewFrame(aPresContext, this, mBullet, nsnull); } } + + // Lookup up the two pseudo style contexts + if (nsnull == mPrevInFlow) { + mFirstLineStyle = aPresContext. + ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLinePseudo, + mStyleContext); + mFirstLetterStyle = aPresContext. + ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLetterPseudo, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + } + return NS_OK; } @@ -1744,7 +1877,6 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext, nsInlineReflow inlineReflow(state.mLineLayout, state, this, PR_TRUE); state.mInlineReflow = &inlineReflow; state.mLineLayout.PushInline(&inlineReflow); -// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F'); nsresult rv = NS_OK; if (eReflowReason_Initial == state.reason) { @@ -2302,10 +2434,40 @@ nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState) } if (nsnull != lastCleanLine) { - // Place any floaters the line has + // Unplace any floaters on the lastCleanLine and every line that + // follows it (which shouldn't be much given that this is + // frame-appended reflow) + LineData* line = lastCleanLine; + while (nsnull != line) { + line->UnplaceFloaters(aState.mSpaceManager); + line = line->mNext; + } + + // Place any floaters the last clean line has if (nsnull != lastCleanLine->mFloaters) { aState.mCurrentLine = lastCleanLine; - aState.PlaceFloaters(lastCleanLine->mFloaters); + aState.PlaceFloaters(lastCleanLine->mFloaters, PR_TRUE); + } + + // Apply any clear-after semantics the line might have + nscoord y0, dy; + switch (lastCleanLine->mBreakType) { + case NS_STYLE_CLEAR_LEFT: + case NS_STYLE_CLEAR_RIGHT: + case NS_STYLE_CLEAR_LEFT_AND_RIGHT: + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, + ("line %p: ClearFloaters: y=%d, break-type=%d\n", + lastCleanLine, aState.mY, lastCleanLine->mBreakType)); + y0 = aState.mY; + aState.ClearFloaters(lastCleanLine->mBreakType); + dy = aState.mY - y0; + if (dy > aState.mPrevBottomMargin) { + aState.mPrevBottomMargin = dy; + aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, + (" cleared floaters, now y=%d\n", aState.mY)); + break; } } @@ -2393,6 +2555,7 @@ nsBlockFrame::FrameInsertedReflow(nsBlockReflowState& aState) // other lines can be moved; recover state before first dirty line. // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2512,6 +2675,7 @@ nsBlockFrame::FrameRemovedReflow(nsBlockReflowState& aState) // other lines can be moved; recover state before first dirty line. // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2538,6 +2702,7 @@ nsBlockFrame::ChildIncrementalReflow(nsBlockReflowState& aState) #endif // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2547,6 +2712,7 @@ nsresult nsBlockFrame::StyleChangedReflow(nsBlockReflowState& aState) { // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2882,6 +3048,15 @@ nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState, } aState.mInlineReflow->Init(x, aState.mY, availWidth, availHeight); aState.mInlineReflowPrepared = PR_TRUE; + + // Setup the first-letter-style-ok flag + nsLineLayout& lineLayout = aState.mLineLayout; + if (mFirstLetterStyle && (0 == lineLayout.GetLineNumber())) { + lineLayout.SetFirstLetterStyleOK(PR_TRUE); + } + else { + lineLayout.SetFirstLetterStyleOK(PR_FALSE); + } } // XXX switch to two versions: inline vs. block? @@ -3201,8 +3376,11 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Refresh our available space in case a floater was placed by our // child. // XXX expensive! + // XXX unnecessary because the child block will contain the floater! aState.GetAvailableSpace(); + aLine->mBreakType = NS_STYLE_CLEAR_NONE; + if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) { // Some of the block fit. We need to have the block frame // continued, so we make sure that it has a next-in-flow now. @@ -3260,6 +3438,50 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, PrepareInlineReflow(aState, aFrame, PR_FALSE); } + // When reflowing a frame that is on the first-line, check and see + // if a special style context should be placed in the context chain. + PRBool reflowingFirstLetter = PR_FALSE; + if ((nsnull == mPrevInFlow) && + (0 == aState.mLineLayout.GetLineNumber())) { + if (nsnull != mFirstLineStyle) { + // Update the child frames style to inherit from the first-line + // style. + // XXX add code to check first and only do it if it needs doing! +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, ""); +#endif +#ifdef NOISY_FIRST_LINE + aFrame->ListTag(stdout); + printf(": adding in first-line style\n"); +#endif + aFrame->ReResolveStyleContext(&aState.mPresContext, mFirstLineStyle); +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, " "); +#endif + } + if ((nsnull != mFirstLetterStyle) && + aState.mLineLayout.GetFirstLetterStyleOK()) { + // XXX For now, we only allow text objects to have :first-letter + // style. + nsIContent* c; + nsresult rv = aFrame->GetContent(c); + if (NS_SUCCEEDED(rv) && (nsnull != c)) { + nsITextContent* tc; + nsresult rv = c->QueryInterface(kITextContentIID, (void**)&tc); + if (NS_SUCCEEDED(rv)) { + // XXX add code to check first and only do it if it needs doing! + // Update the child frames style to inherit from the + // first-letter style. + aFrame->ReResolveStyleContext(&aState.mPresContext, + mFirstLetterStyle); + reflowingFirstLetter = PR_TRUE; + NS_RELEASE(tc); + } + NS_RELEASE(c); + } + } + } + PRBool isFirstChild = (aLine == mLines) && (aFrame == aLine->mFirstChild); aState.mInlineReflow->SetIsFirstChild(isFirstChild); @@ -3292,6 +3514,17 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, aLine->mChildCount++; } aFrame->GetNextSibling(aFrame); + +#if 0 + // XXX Special hackery to keep going if we just split because of a + // :first-letter situation. + if (reflowingFirstLetter) { + if (aState.mInlineReflow->GetAvailWidth() > 0) { + aLine->SetLastContentIsComplete();/* XXX what to do??? */ + return PR_TRUE; + } + } +#endif } else { if (NS_INLINE_IS_BREAK_AFTER(aReflowResult)) { @@ -3331,6 +3564,37 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, return PR_FALSE; } +void +nsBlockFrame::RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame) +{ +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, ""); +#endif + + nsIStyleContext* kidSC; + aFrame->GetStyleContext(kidSC); + if (nsnull != kidSC) { + nsIStyleContext* kidParentSC; + kidParentSC = kidSC->GetParent(); + if ((kidParentSC == mFirstLineStyle) || + (kidParentSC == mFirstLetterStyle)) { +#ifdef NOISY_FIRST_LINE + printf(" "); + aFrame->ListTag(stdout); + printf(": removing first-line style\n"); +#endif + + aFrame->ReResolveStyleContext(&aPresContext, mStyleContext); + +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, " "); +#endif + } + NS_IF_RELEASE(kidParentSC); + NS_RELEASE(kidSC); + } +} + // XXX alloc lines using free-list in aState // XXX refactor this since the split NEVER has to deal with blocks @@ -3344,8 +3608,8 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, PRInt32 pushCount = aLine->ChildCount() - aState.mInlineReflow->GetCurrentFrameNum(); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, - ("nsBlockFrame::SplitLine: pushing %d frames", - pushCount)); + ("nsBlockFrame::SplitLine: pushing %d frames", + pushCount)); if (0 != pushCount) { NS_ASSERTION(nsnull != aFrame, "whoops"); LineData* to = aLine->mNext; @@ -3382,6 +3646,19 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, // Let inline reflow know that some frames are no longer part of // its state. aState.mInlineReflow->ChangeFrameCount(aLine->ChildCount()); + + // Repair style contexts when frames move to the next line and + // this is the first line. + if (((nsnull != mFirstLineStyle) || (nsnull != mFirstLetterStyle)) && + (0 == aState.mLineLayout.GetLineNumber())) { + // Take the pseudo-style out of the style context chain + nsIFrame* kid = to->mFirstChild; + PRInt32 n = pushCount; + while (--n >= 0) { + RestoreStyleFor(aState.mPresContext, kid); + kid->GetNextSibling(kid); + } + } } return NS_OK; } @@ -3671,7 +3948,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Any below current line floaters to place? if (0 != aState.mPendingFloaters.Count()) { - aState.PlaceFloaters(&aState.mPendingFloaters); + aState.PlaceFloaters(&aState.mPendingFloaters, PR_FALSE); aState.mPendingFloaters.Clear(); // XXX Factor in the height of the floaters as well when considering @@ -3682,8 +3959,10 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Based on the last child we reflowed reflow status, we may need to // clear past any floaters. + aLine->mBreakType = NS_STYLE_CLEAR_NONE; if (NS_INLINE_IS_BREAK_AFTER(aReflowStatus)) { PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(aReflowStatus); + aLine->mBreakType = breakType; // Apply break to the line switch (breakType) { default: @@ -3694,6 +3973,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("nsBlockFrame::PlaceLine: clearing floaters=%d", breakType)); +#if XXX { nscoord y0 = aState.mY; aState.ClearFloaters(breakType); @@ -3702,6 +3982,18 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aLine->mCarriedOutBottomMargin = dy; } } +#else + aState.ClearFloaters(breakType); +#if 0 + y0 = aState.mY; + aState.ClearFloaters(lastCleanLine->mBreakType); + dy = aState.mY - y0; + if (dy > aState.mPrevBottomMargin) { + aState.mPrevBottomMargin = dy; + aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } +#endif +#endif break; } // XXX page breaks, etc, need to be passed upwards too! @@ -4273,6 +4565,12 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder) ("nsBlockReflowState::AddFloater: IsLeftMostChild, placeHolder=%p", aPlaceholder)); + // Flush out pending bottom margin before placing floater + if (0 != mPrevBottomMargin) { + mY += mPrevBottomMargin; + mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } + // Because we are in the middle of reflowing a placeholder frame // within a line (and possibly nested in an inline frame or two // that's a child of our block) we need to restore the space @@ -4456,7 +4754,7 @@ nsBlockReflowState::PlaceFloater(nsPlaceholderFrame* aPlaceholder, * Place below-current-line floaters. */ void -nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters) +nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem) { NS_PRECONDITION(aFloaters->Count() > 0, "no floaters"); @@ -4464,7 +4762,7 @@ nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters) for (PRInt32 i = 0; i < numFloaters; i++) { nsPlaceholderFrame* placeholderFrame = (nsPlaceholderFrame*) aFloaters->ElementAt(i); - if (IsLeftMostChild(placeholderFrame)) { + if (!aAllOfThem && IsLeftMostChild(placeholderFrame)) { // Left-most children are placed during the line's reflow continue; } diff --git a/mozilla/layout/html/base/src/nsBlockFrame.cpp b/mozilla/layout/html/base/src/nsBlockFrame.cpp index fbf5b615c7c..c57fa681885 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.cpp +++ b/mozilla/layout/html/base/src/nsBlockFrame.cpp @@ -49,12 +49,46 @@ #include "nsIHTMLContent.h" #include "nsHTMLImage.h" +// XXX temporary for :first-letter support +#include "nsITextContent.h" +static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID);/* XXX */ + +#ifdef NS_DEBUG +#undef NOISY_FIRST_LINE +#undef REALLY_NOISY_FIRST_LINE +#else +#undef NOISY_FIRST_LINE +#endif + +#ifdef REALLY_NOISY_FIRST_LINE +static void +DumpStyleGeneaology(nsIFrame* aFrame, const char* gap) +{ + fputs(gap, stdout); + aFrame->ListTag(stdout); + printf(": "); + nsIStyleContext* sc; + aFrame->GetStyleContext(sc); + while (nsnull != sc) { + nsIStyleContext* psc; + printf("%p ", sc); + psc = sc->GetParent(); + NS_RELEASE(sc); + sc = psc; + } + printf("\n"); +} +#endif + /* 52b33130-0b99-11d2-932e-00805f8add32 */ #define NS_BLOCK_FRAME_CID \ { 0x52b33130, 0x0b99, 0x11d2, {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID; +// 11-03-98: low hanging memory fruit: get rid of text-runs that have +// only one piece of text in them! + // 09-15-98: make sure that the outer container of the block (e.g. the // body sets up the outer top margin feed in properly so that the top // margin collapses properly with the body margin. Note that for the @@ -133,7 +167,7 @@ struct nsBlockReflowState : public nsFrameReflowState { void PlaceFloater(nsPlaceholderFrame* aFloater, PRBool& aIsLeftFloater); - void PlaceFloaters(nsVoidArray* aFloaters); + void PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem); void ClearFloaters(PRUint8 aBreakType); @@ -154,6 +188,7 @@ struct nsBlockReflowState : public nsFrameReflowState { PRUintn mPrevMarginFlags; // previous margin flag bits (above and beyond the carried-out-margin flags) #define HAVE_CARRIED_MARGIN 0x100 +#define ALREADY_APPLIED_BOTTOM_MARGIN 0x200 nscoord mBottomEdge; // maximum Y @@ -222,6 +257,8 @@ public: // nsIFrame NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList); + NS_IMETHOD ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext); NS_IMETHOD FirstChild(nsIFrame*& aFirstChild) const; NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext); NS_IMETHOD IsSplittable(nsSplittableType& aIsSplittable) const; @@ -280,6 +317,8 @@ public: virtual PRBool DeleteChildsNextInFlow(nsIPresContext& aPresContext, nsIFrame* aNextInFlow); + void RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame); + void SetFlags(PRUint32 aFlags) { mFlags = aFlags; } @@ -390,6 +429,9 @@ public: PRBool IsChild(nsIFrame* aFrame); #endif + nsIStyleContext* mFirstLineStyle; + nsIStyleContext* mFirstLetterStyle; + LineData* mLines; LineData* mOverflowLines; @@ -871,6 +913,7 @@ struct LineData { mCombinedArea.SetRect(0,0,0,0); mCarriedOutTopMargin = 0; mCarriedOutBottomMargin = 0; + mBreakType = NS_STYLE_CLEAR_NONE; } ~LineData(); @@ -1005,6 +1048,7 @@ struct LineData { nsIFrame* mFirstChild; PRUint16 mChildCount; PRUint16 mState; + PRUint8 mBreakType; nsRect mBounds; nsRect mCombinedArea; nscoord mCarriedOutTopMargin; @@ -1466,6 +1510,8 @@ nsBlockFrame::nsBlockFrame(nsIContent* aContent, nsIFrame* aParent) nsBlockFrame::~nsBlockFrame() { + NS_IF_RELEASE(mFirstLineStyle); + NS_IF_RELEASE(mFirstLetterStyle); nsTextRun::DeleteTextRuns(mTextRuns); } @@ -1493,6 +1539,81 @@ nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr); } +NS_IMETHODIMP +nsBlockFrame::ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext) +{ + nsIStyleContext* oldContext = mStyleContext; + nsresult rv = nsFrame::ReResolveStyleContext(aPresContext, aParentContext); + if (NS_OK != rv) { + return rv; + } + if (oldContext != mStyleContext) { + // Re-resolve the :first-line pseudo style context + if (nsnull == mPrevInFlow) { + nsIStyleContext* newFirstLineStyle = + aPresContext->ProbePseudoStyleContextFor(mContent, + nsHTMLAtoms::firstLinePseudo, + mStyleContext); + if (newFirstLineStyle != mFirstLineStyle) { + NS_IF_RELEASE(mFirstLineStyle); + mFirstLineStyle = newFirstLineStyle; + } + else { + NS_IF_RELEASE(newFirstLineStyle); + } + + // Re-resolve the :first-letter pseudo style context + nsIStyleContext* newFirstLetterStyle = + aPresContext->ProbePseudoStyleContextFor(mContent, + nsHTMLAtoms::firstLetterPseudo, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + if (newFirstLetterStyle != mFirstLetterStyle) { + NS_IF_RELEASE(mFirstLetterStyle); + mFirstLetterStyle = newFirstLetterStyle; + } + else { + NS_IF_RELEASE(newFirstLetterStyle); + } + } + + // Update the child frames on each line + LineData* line = mLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + PRInt32 n = line->mChildCount; + while ((--n >= 0) && NS_SUCCEEDED(rv)) { + if (line == mLines) { + rv = child->ReResolveStyleContext(aPresContext, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + } + else { + rv = child->ReResolveStyleContext(aPresContext, mStyleContext); + } + child->GetNextSibling(child); + } + line = line->mNext; + } + + // Update the child frames on each overflow line too + line = mOverflowLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + PRInt32 n = line->mChildCount; + while ((--n >= 0) && NS_SUCCEEDED(rv)) { + rv = child->ReResolveStyleContext(aPresContext, mStyleContext); + child->GetNextSibling(child); + } + line = line->mNext; + } + } + return rv; +} + NS_IMETHODIMP nsBlockFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) { @@ -1527,11 +1648,23 @@ nsBlockFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) // it to the flow now. const nsStyleList* styleList; GetStyleData(eStyleStruct_List, (const nsStyleStruct*&) styleList); -//XXX ListTag(stdout); printf(": lsp=%d\n", styleList->mListStylePosition); if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == styleList->mListStylePosition) { InsertNewFrame(aPresContext, this, mBullet, nsnull); } } + + // Lookup up the two pseudo style contexts + if (nsnull == mPrevInFlow) { + mFirstLineStyle = aPresContext. + ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLinePseudo, + mStyleContext); + mFirstLetterStyle = aPresContext. + ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLetterPseudo, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + } + return NS_OK; } @@ -1744,7 +1877,6 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext, nsInlineReflow inlineReflow(state.mLineLayout, state, this, PR_TRUE); state.mInlineReflow = &inlineReflow; state.mLineLayout.PushInline(&inlineReflow); -// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F'); nsresult rv = NS_OK; if (eReflowReason_Initial == state.reason) { @@ -2302,10 +2434,40 @@ nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState) } if (nsnull != lastCleanLine) { - // Place any floaters the line has + // Unplace any floaters on the lastCleanLine and every line that + // follows it (which shouldn't be much given that this is + // frame-appended reflow) + LineData* line = lastCleanLine; + while (nsnull != line) { + line->UnplaceFloaters(aState.mSpaceManager); + line = line->mNext; + } + + // Place any floaters the last clean line has if (nsnull != lastCleanLine->mFloaters) { aState.mCurrentLine = lastCleanLine; - aState.PlaceFloaters(lastCleanLine->mFloaters); + aState.PlaceFloaters(lastCleanLine->mFloaters, PR_TRUE); + } + + // Apply any clear-after semantics the line might have + nscoord y0, dy; + switch (lastCleanLine->mBreakType) { + case NS_STYLE_CLEAR_LEFT: + case NS_STYLE_CLEAR_RIGHT: + case NS_STYLE_CLEAR_LEFT_AND_RIGHT: + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, + ("line %p: ClearFloaters: y=%d, break-type=%d\n", + lastCleanLine, aState.mY, lastCleanLine->mBreakType)); + y0 = aState.mY; + aState.ClearFloaters(lastCleanLine->mBreakType); + dy = aState.mY - y0; + if (dy > aState.mPrevBottomMargin) { + aState.mPrevBottomMargin = dy; + aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, + (" cleared floaters, now y=%d\n", aState.mY)); + break; } } @@ -2393,6 +2555,7 @@ nsBlockFrame::FrameInsertedReflow(nsBlockReflowState& aState) // other lines can be moved; recover state before first dirty line. // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2512,6 +2675,7 @@ nsBlockFrame::FrameRemovedReflow(nsBlockReflowState& aState) // other lines can be moved; recover state before first dirty line. // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2538,6 +2702,7 @@ nsBlockFrame::ChildIncrementalReflow(nsBlockReflowState& aState) #endif // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2547,6 +2712,7 @@ nsresult nsBlockFrame::StyleChangedReflow(nsBlockReflowState& aState) { // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2882,6 +3048,15 @@ nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState, } aState.mInlineReflow->Init(x, aState.mY, availWidth, availHeight); aState.mInlineReflowPrepared = PR_TRUE; + + // Setup the first-letter-style-ok flag + nsLineLayout& lineLayout = aState.mLineLayout; + if (mFirstLetterStyle && (0 == lineLayout.GetLineNumber())) { + lineLayout.SetFirstLetterStyleOK(PR_TRUE); + } + else { + lineLayout.SetFirstLetterStyleOK(PR_FALSE); + } } // XXX switch to two versions: inline vs. block? @@ -3201,8 +3376,11 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Refresh our available space in case a floater was placed by our // child. // XXX expensive! + // XXX unnecessary because the child block will contain the floater! aState.GetAvailableSpace(); + aLine->mBreakType = NS_STYLE_CLEAR_NONE; + if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) { // Some of the block fit. We need to have the block frame // continued, so we make sure that it has a next-in-flow now. @@ -3260,6 +3438,50 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, PrepareInlineReflow(aState, aFrame, PR_FALSE); } + // When reflowing a frame that is on the first-line, check and see + // if a special style context should be placed in the context chain. + PRBool reflowingFirstLetter = PR_FALSE; + if ((nsnull == mPrevInFlow) && + (0 == aState.mLineLayout.GetLineNumber())) { + if (nsnull != mFirstLineStyle) { + // Update the child frames style to inherit from the first-line + // style. + // XXX add code to check first and only do it if it needs doing! +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, ""); +#endif +#ifdef NOISY_FIRST_LINE + aFrame->ListTag(stdout); + printf(": adding in first-line style\n"); +#endif + aFrame->ReResolveStyleContext(&aState.mPresContext, mFirstLineStyle); +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, " "); +#endif + } + if ((nsnull != mFirstLetterStyle) && + aState.mLineLayout.GetFirstLetterStyleOK()) { + // XXX For now, we only allow text objects to have :first-letter + // style. + nsIContent* c; + nsresult rv = aFrame->GetContent(c); + if (NS_SUCCEEDED(rv) && (nsnull != c)) { + nsITextContent* tc; + nsresult rv = c->QueryInterface(kITextContentIID, (void**)&tc); + if (NS_SUCCEEDED(rv)) { + // XXX add code to check first and only do it if it needs doing! + // Update the child frames style to inherit from the + // first-letter style. + aFrame->ReResolveStyleContext(&aState.mPresContext, + mFirstLetterStyle); + reflowingFirstLetter = PR_TRUE; + NS_RELEASE(tc); + } + NS_RELEASE(c); + } + } + } + PRBool isFirstChild = (aLine == mLines) && (aFrame == aLine->mFirstChild); aState.mInlineReflow->SetIsFirstChild(isFirstChild); @@ -3292,6 +3514,17 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, aLine->mChildCount++; } aFrame->GetNextSibling(aFrame); + +#if 0 + // XXX Special hackery to keep going if we just split because of a + // :first-letter situation. + if (reflowingFirstLetter) { + if (aState.mInlineReflow->GetAvailWidth() > 0) { + aLine->SetLastContentIsComplete();/* XXX what to do??? */ + return PR_TRUE; + } + } +#endif } else { if (NS_INLINE_IS_BREAK_AFTER(aReflowResult)) { @@ -3331,6 +3564,37 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, return PR_FALSE; } +void +nsBlockFrame::RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame) +{ +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, ""); +#endif + + nsIStyleContext* kidSC; + aFrame->GetStyleContext(kidSC); + if (nsnull != kidSC) { + nsIStyleContext* kidParentSC; + kidParentSC = kidSC->GetParent(); + if ((kidParentSC == mFirstLineStyle) || + (kidParentSC == mFirstLetterStyle)) { +#ifdef NOISY_FIRST_LINE + printf(" "); + aFrame->ListTag(stdout); + printf(": removing first-line style\n"); +#endif + + aFrame->ReResolveStyleContext(&aPresContext, mStyleContext); + +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, " "); +#endif + } + NS_IF_RELEASE(kidParentSC); + NS_RELEASE(kidSC); + } +} + // XXX alloc lines using free-list in aState // XXX refactor this since the split NEVER has to deal with blocks @@ -3344,8 +3608,8 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, PRInt32 pushCount = aLine->ChildCount() - aState.mInlineReflow->GetCurrentFrameNum(); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, - ("nsBlockFrame::SplitLine: pushing %d frames", - pushCount)); + ("nsBlockFrame::SplitLine: pushing %d frames", + pushCount)); if (0 != pushCount) { NS_ASSERTION(nsnull != aFrame, "whoops"); LineData* to = aLine->mNext; @@ -3382,6 +3646,19 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, // Let inline reflow know that some frames are no longer part of // its state. aState.mInlineReflow->ChangeFrameCount(aLine->ChildCount()); + + // Repair style contexts when frames move to the next line and + // this is the first line. + if (((nsnull != mFirstLineStyle) || (nsnull != mFirstLetterStyle)) && + (0 == aState.mLineLayout.GetLineNumber())) { + // Take the pseudo-style out of the style context chain + nsIFrame* kid = to->mFirstChild; + PRInt32 n = pushCount; + while (--n >= 0) { + RestoreStyleFor(aState.mPresContext, kid); + kid->GetNextSibling(kid); + } + } } return NS_OK; } @@ -3671,7 +3948,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Any below current line floaters to place? if (0 != aState.mPendingFloaters.Count()) { - aState.PlaceFloaters(&aState.mPendingFloaters); + aState.PlaceFloaters(&aState.mPendingFloaters, PR_FALSE); aState.mPendingFloaters.Clear(); // XXX Factor in the height of the floaters as well when considering @@ -3682,8 +3959,10 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Based on the last child we reflowed reflow status, we may need to // clear past any floaters. + aLine->mBreakType = NS_STYLE_CLEAR_NONE; if (NS_INLINE_IS_BREAK_AFTER(aReflowStatus)) { PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(aReflowStatus); + aLine->mBreakType = breakType; // Apply break to the line switch (breakType) { default: @@ -3694,6 +3973,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("nsBlockFrame::PlaceLine: clearing floaters=%d", breakType)); +#if XXX { nscoord y0 = aState.mY; aState.ClearFloaters(breakType); @@ -3702,6 +3982,18 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aLine->mCarriedOutBottomMargin = dy; } } +#else + aState.ClearFloaters(breakType); +#if 0 + y0 = aState.mY; + aState.ClearFloaters(lastCleanLine->mBreakType); + dy = aState.mY - y0; + if (dy > aState.mPrevBottomMargin) { + aState.mPrevBottomMargin = dy; + aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } +#endif +#endif break; } // XXX page breaks, etc, need to be passed upwards too! @@ -4273,6 +4565,12 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder) ("nsBlockReflowState::AddFloater: IsLeftMostChild, placeHolder=%p", aPlaceholder)); + // Flush out pending bottom margin before placing floater + if (0 != mPrevBottomMargin) { + mY += mPrevBottomMargin; + mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } + // Because we are in the middle of reflowing a placeholder frame // within a line (and possibly nested in an inline frame or two // that's a child of our block) we need to restore the space @@ -4456,7 +4754,7 @@ nsBlockReflowState::PlaceFloater(nsPlaceholderFrame* aPlaceholder, * Place below-current-line floaters. */ void -nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters) +nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem) { NS_PRECONDITION(aFloaters->Count() > 0, "no floaters"); @@ -4464,7 +4762,7 @@ nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters) for (PRInt32 i = 0; i < numFloaters; i++) { nsPlaceholderFrame* placeholderFrame = (nsPlaceholderFrame*) aFloaters->ElementAt(i); - if (IsLeftMostChild(placeholderFrame)) { + if (!aAllOfThem && IsLeftMostChild(placeholderFrame)) { // Left-most children are placed during the line's reflow continue; } diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.cpp b/mozilla/layout/html/base/src/nsBlockReflowState.cpp index fbf5b615c7c..c57fa681885 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.cpp +++ b/mozilla/layout/html/base/src/nsBlockReflowState.cpp @@ -49,12 +49,46 @@ #include "nsIHTMLContent.h" #include "nsHTMLImage.h" +// XXX temporary for :first-letter support +#include "nsITextContent.h" +static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID);/* XXX */ + +#ifdef NS_DEBUG +#undef NOISY_FIRST_LINE +#undef REALLY_NOISY_FIRST_LINE +#else +#undef NOISY_FIRST_LINE +#endif + +#ifdef REALLY_NOISY_FIRST_LINE +static void +DumpStyleGeneaology(nsIFrame* aFrame, const char* gap) +{ + fputs(gap, stdout); + aFrame->ListTag(stdout); + printf(": "); + nsIStyleContext* sc; + aFrame->GetStyleContext(sc); + while (nsnull != sc) { + nsIStyleContext* psc; + printf("%p ", sc); + psc = sc->GetParent(); + NS_RELEASE(sc); + sc = psc; + } + printf("\n"); +} +#endif + /* 52b33130-0b99-11d2-932e-00805f8add32 */ #define NS_BLOCK_FRAME_CID \ { 0x52b33130, 0x0b99, 0x11d2, {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID; +// 11-03-98: low hanging memory fruit: get rid of text-runs that have +// only one piece of text in them! + // 09-15-98: make sure that the outer container of the block (e.g. the // body sets up the outer top margin feed in properly so that the top // margin collapses properly with the body margin. Note that for the @@ -133,7 +167,7 @@ struct nsBlockReflowState : public nsFrameReflowState { void PlaceFloater(nsPlaceholderFrame* aFloater, PRBool& aIsLeftFloater); - void PlaceFloaters(nsVoidArray* aFloaters); + void PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem); void ClearFloaters(PRUint8 aBreakType); @@ -154,6 +188,7 @@ struct nsBlockReflowState : public nsFrameReflowState { PRUintn mPrevMarginFlags; // previous margin flag bits (above and beyond the carried-out-margin flags) #define HAVE_CARRIED_MARGIN 0x100 +#define ALREADY_APPLIED_BOTTOM_MARGIN 0x200 nscoord mBottomEdge; // maximum Y @@ -222,6 +257,8 @@ public: // nsIFrame NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList); + NS_IMETHOD ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext); NS_IMETHOD FirstChild(nsIFrame*& aFirstChild) const; NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext); NS_IMETHOD IsSplittable(nsSplittableType& aIsSplittable) const; @@ -280,6 +317,8 @@ public: virtual PRBool DeleteChildsNextInFlow(nsIPresContext& aPresContext, nsIFrame* aNextInFlow); + void RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame); + void SetFlags(PRUint32 aFlags) { mFlags = aFlags; } @@ -390,6 +429,9 @@ public: PRBool IsChild(nsIFrame* aFrame); #endif + nsIStyleContext* mFirstLineStyle; + nsIStyleContext* mFirstLetterStyle; + LineData* mLines; LineData* mOverflowLines; @@ -871,6 +913,7 @@ struct LineData { mCombinedArea.SetRect(0,0,0,0); mCarriedOutTopMargin = 0; mCarriedOutBottomMargin = 0; + mBreakType = NS_STYLE_CLEAR_NONE; } ~LineData(); @@ -1005,6 +1048,7 @@ struct LineData { nsIFrame* mFirstChild; PRUint16 mChildCount; PRUint16 mState; + PRUint8 mBreakType; nsRect mBounds; nsRect mCombinedArea; nscoord mCarriedOutTopMargin; @@ -1466,6 +1510,8 @@ nsBlockFrame::nsBlockFrame(nsIContent* aContent, nsIFrame* aParent) nsBlockFrame::~nsBlockFrame() { + NS_IF_RELEASE(mFirstLineStyle); + NS_IF_RELEASE(mFirstLetterStyle); nsTextRun::DeleteTextRuns(mTextRuns); } @@ -1493,6 +1539,81 @@ nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr); } +NS_IMETHODIMP +nsBlockFrame::ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext) +{ + nsIStyleContext* oldContext = mStyleContext; + nsresult rv = nsFrame::ReResolveStyleContext(aPresContext, aParentContext); + if (NS_OK != rv) { + return rv; + } + if (oldContext != mStyleContext) { + // Re-resolve the :first-line pseudo style context + if (nsnull == mPrevInFlow) { + nsIStyleContext* newFirstLineStyle = + aPresContext->ProbePseudoStyleContextFor(mContent, + nsHTMLAtoms::firstLinePseudo, + mStyleContext); + if (newFirstLineStyle != mFirstLineStyle) { + NS_IF_RELEASE(mFirstLineStyle); + mFirstLineStyle = newFirstLineStyle; + } + else { + NS_IF_RELEASE(newFirstLineStyle); + } + + // Re-resolve the :first-letter pseudo style context + nsIStyleContext* newFirstLetterStyle = + aPresContext->ProbePseudoStyleContextFor(mContent, + nsHTMLAtoms::firstLetterPseudo, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + if (newFirstLetterStyle != mFirstLetterStyle) { + NS_IF_RELEASE(mFirstLetterStyle); + mFirstLetterStyle = newFirstLetterStyle; + } + else { + NS_IF_RELEASE(newFirstLetterStyle); + } + } + + // Update the child frames on each line + LineData* line = mLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + PRInt32 n = line->mChildCount; + while ((--n >= 0) && NS_SUCCEEDED(rv)) { + if (line == mLines) { + rv = child->ReResolveStyleContext(aPresContext, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + } + else { + rv = child->ReResolveStyleContext(aPresContext, mStyleContext); + } + child->GetNextSibling(child); + } + line = line->mNext; + } + + // Update the child frames on each overflow line too + line = mOverflowLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + PRInt32 n = line->mChildCount; + while ((--n >= 0) && NS_SUCCEEDED(rv)) { + rv = child->ReResolveStyleContext(aPresContext, mStyleContext); + child->GetNextSibling(child); + } + line = line->mNext; + } + } + return rv; +} + NS_IMETHODIMP nsBlockFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) { @@ -1527,11 +1648,23 @@ nsBlockFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) // it to the flow now. const nsStyleList* styleList; GetStyleData(eStyleStruct_List, (const nsStyleStruct*&) styleList); -//XXX ListTag(stdout); printf(": lsp=%d\n", styleList->mListStylePosition); if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == styleList->mListStylePosition) { InsertNewFrame(aPresContext, this, mBullet, nsnull); } } + + // Lookup up the two pseudo style contexts + if (nsnull == mPrevInFlow) { + mFirstLineStyle = aPresContext. + ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLinePseudo, + mStyleContext); + mFirstLetterStyle = aPresContext. + ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLetterPseudo, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + } + return NS_OK; } @@ -1744,7 +1877,6 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext, nsInlineReflow inlineReflow(state.mLineLayout, state, this, PR_TRUE); state.mInlineReflow = &inlineReflow; state.mLineLayout.PushInline(&inlineReflow); -// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F'); nsresult rv = NS_OK; if (eReflowReason_Initial == state.reason) { @@ -2302,10 +2434,40 @@ nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState) } if (nsnull != lastCleanLine) { - // Place any floaters the line has + // Unplace any floaters on the lastCleanLine and every line that + // follows it (which shouldn't be much given that this is + // frame-appended reflow) + LineData* line = lastCleanLine; + while (nsnull != line) { + line->UnplaceFloaters(aState.mSpaceManager); + line = line->mNext; + } + + // Place any floaters the last clean line has if (nsnull != lastCleanLine->mFloaters) { aState.mCurrentLine = lastCleanLine; - aState.PlaceFloaters(lastCleanLine->mFloaters); + aState.PlaceFloaters(lastCleanLine->mFloaters, PR_TRUE); + } + + // Apply any clear-after semantics the line might have + nscoord y0, dy; + switch (lastCleanLine->mBreakType) { + case NS_STYLE_CLEAR_LEFT: + case NS_STYLE_CLEAR_RIGHT: + case NS_STYLE_CLEAR_LEFT_AND_RIGHT: + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, + ("line %p: ClearFloaters: y=%d, break-type=%d\n", + lastCleanLine, aState.mY, lastCleanLine->mBreakType)); + y0 = aState.mY; + aState.ClearFloaters(lastCleanLine->mBreakType); + dy = aState.mY - y0; + if (dy > aState.mPrevBottomMargin) { + aState.mPrevBottomMargin = dy; + aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, + (" cleared floaters, now y=%d\n", aState.mY)); + break; } } @@ -2393,6 +2555,7 @@ nsBlockFrame::FrameInsertedReflow(nsBlockReflowState& aState) // other lines can be moved; recover state before first dirty line. // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2512,6 +2675,7 @@ nsBlockFrame::FrameRemovedReflow(nsBlockReflowState& aState) // other lines can be moved; recover state before first dirty line. // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2538,6 +2702,7 @@ nsBlockFrame::ChildIncrementalReflow(nsBlockReflowState& aState) #endif // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2547,6 +2712,7 @@ nsresult nsBlockFrame::StyleChangedReflow(nsBlockReflowState& aState) { // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2882,6 +3048,15 @@ nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState, } aState.mInlineReflow->Init(x, aState.mY, availWidth, availHeight); aState.mInlineReflowPrepared = PR_TRUE; + + // Setup the first-letter-style-ok flag + nsLineLayout& lineLayout = aState.mLineLayout; + if (mFirstLetterStyle && (0 == lineLayout.GetLineNumber())) { + lineLayout.SetFirstLetterStyleOK(PR_TRUE); + } + else { + lineLayout.SetFirstLetterStyleOK(PR_FALSE); + } } // XXX switch to two versions: inline vs. block? @@ -3201,8 +3376,11 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Refresh our available space in case a floater was placed by our // child. // XXX expensive! + // XXX unnecessary because the child block will contain the floater! aState.GetAvailableSpace(); + aLine->mBreakType = NS_STYLE_CLEAR_NONE; + if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) { // Some of the block fit. We need to have the block frame // continued, so we make sure that it has a next-in-flow now. @@ -3260,6 +3438,50 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, PrepareInlineReflow(aState, aFrame, PR_FALSE); } + // When reflowing a frame that is on the first-line, check and see + // if a special style context should be placed in the context chain. + PRBool reflowingFirstLetter = PR_FALSE; + if ((nsnull == mPrevInFlow) && + (0 == aState.mLineLayout.GetLineNumber())) { + if (nsnull != mFirstLineStyle) { + // Update the child frames style to inherit from the first-line + // style. + // XXX add code to check first and only do it if it needs doing! +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, ""); +#endif +#ifdef NOISY_FIRST_LINE + aFrame->ListTag(stdout); + printf(": adding in first-line style\n"); +#endif + aFrame->ReResolveStyleContext(&aState.mPresContext, mFirstLineStyle); +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, " "); +#endif + } + if ((nsnull != mFirstLetterStyle) && + aState.mLineLayout.GetFirstLetterStyleOK()) { + // XXX For now, we only allow text objects to have :first-letter + // style. + nsIContent* c; + nsresult rv = aFrame->GetContent(c); + if (NS_SUCCEEDED(rv) && (nsnull != c)) { + nsITextContent* tc; + nsresult rv = c->QueryInterface(kITextContentIID, (void**)&tc); + if (NS_SUCCEEDED(rv)) { + // XXX add code to check first and only do it if it needs doing! + // Update the child frames style to inherit from the + // first-letter style. + aFrame->ReResolveStyleContext(&aState.mPresContext, + mFirstLetterStyle); + reflowingFirstLetter = PR_TRUE; + NS_RELEASE(tc); + } + NS_RELEASE(c); + } + } + } + PRBool isFirstChild = (aLine == mLines) && (aFrame == aLine->mFirstChild); aState.mInlineReflow->SetIsFirstChild(isFirstChild); @@ -3292,6 +3514,17 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, aLine->mChildCount++; } aFrame->GetNextSibling(aFrame); + +#if 0 + // XXX Special hackery to keep going if we just split because of a + // :first-letter situation. + if (reflowingFirstLetter) { + if (aState.mInlineReflow->GetAvailWidth() > 0) { + aLine->SetLastContentIsComplete();/* XXX what to do??? */ + return PR_TRUE; + } + } +#endif } else { if (NS_INLINE_IS_BREAK_AFTER(aReflowResult)) { @@ -3331,6 +3564,37 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, return PR_FALSE; } +void +nsBlockFrame::RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame) +{ +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, ""); +#endif + + nsIStyleContext* kidSC; + aFrame->GetStyleContext(kidSC); + if (nsnull != kidSC) { + nsIStyleContext* kidParentSC; + kidParentSC = kidSC->GetParent(); + if ((kidParentSC == mFirstLineStyle) || + (kidParentSC == mFirstLetterStyle)) { +#ifdef NOISY_FIRST_LINE + printf(" "); + aFrame->ListTag(stdout); + printf(": removing first-line style\n"); +#endif + + aFrame->ReResolveStyleContext(&aPresContext, mStyleContext); + +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, " "); +#endif + } + NS_IF_RELEASE(kidParentSC); + NS_RELEASE(kidSC); + } +} + // XXX alloc lines using free-list in aState // XXX refactor this since the split NEVER has to deal with blocks @@ -3344,8 +3608,8 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, PRInt32 pushCount = aLine->ChildCount() - aState.mInlineReflow->GetCurrentFrameNum(); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, - ("nsBlockFrame::SplitLine: pushing %d frames", - pushCount)); + ("nsBlockFrame::SplitLine: pushing %d frames", + pushCount)); if (0 != pushCount) { NS_ASSERTION(nsnull != aFrame, "whoops"); LineData* to = aLine->mNext; @@ -3382,6 +3646,19 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, // Let inline reflow know that some frames are no longer part of // its state. aState.mInlineReflow->ChangeFrameCount(aLine->ChildCount()); + + // Repair style contexts when frames move to the next line and + // this is the first line. + if (((nsnull != mFirstLineStyle) || (nsnull != mFirstLetterStyle)) && + (0 == aState.mLineLayout.GetLineNumber())) { + // Take the pseudo-style out of the style context chain + nsIFrame* kid = to->mFirstChild; + PRInt32 n = pushCount; + while (--n >= 0) { + RestoreStyleFor(aState.mPresContext, kid); + kid->GetNextSibling(kid); + } + } } return NS_OK; } @@ -3671,7 +3948,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Any below current line floaters to place? if (0 != aState.mPendingFloaters.Count()) { - aState.PlaceFloaters(&aState.mPendingFloaters); + aState.PlaceFloaters(&aState.mPendingFloaters, PR_FALSE); aState.mPendingFloaters.Clear(); // XXX Factor in the height of the floaters as well when considering @@ -3682,8 +3959,10 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Based on the last child we reflowed reflow status, we may need to // clear past any floaters. + aLine->mBreakType = NS_STYLE_CLEAR_NONE; if (NS_INLINE_IS_BREAK_AFTER(aReflowStatus)) { PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(aReflowStatus); + aLine->mBreakType = breakType; // Apply break to the line switch (breakType) { default: @@ -3694,6 +3973,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("nsBlockFrame::PlaceLine: clearing floaters=%d", breakType)); +#if XXX { nscoord y0 = aState.mY; aState.ClearFloaters(breakType); @@ -3702,6 +3982,18 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aLine->mCarriedOutBottomMargin = dy; } } +#else + aState.ClearFloaters(breakType); +#if 0 + y0 = aState.mY; + aState.ClearFloaters(lastCleanLine->mBreakType); + dy = aState.mY - y0; + if (dy > aState.mPrevBottomMargin) { + aState.mPrevBottomMargin = dy; + aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } +#endif +#endif break; } // XXX page breaks, etc, need to be passed upwards too! @@ -4273,6 +4565,12 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder) ("nsBlockReflowState::AddFloater: IsLeftMostChild, placeHolder=%p", aPlaceholder)); + // Flush out pending bottom margin before placing floater + if (0 != mPrevBottomMargin) { + mY += mPrevBottomMargin; + mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } + // Because we are in the middle of reflowing a placeholder frame // within a line (and possibly nested in an inline frame or two // that's a child of our block) we need to restore the space @@ -4456,7 +4754,7 @@ nsBlockReflowState::PlaceFloater(nsPlaceholderFrame* aPlaceholder, * Place below-current-line floaters. */ void -nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters) +nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem) { NS_PRECONDITION(aFloaters->Count() > 0, "no floaters"); @@ -4464,7 +4762,7 @@ nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters) for (PRInt32 i = 0; i < numFloaters; i++) { nsPlaceholderFrame* placeholderFrame = (nsPlaceholderFrame*) aFloaters->ElementAt(i); - if (IsLeftMostChild(placeholderFrame)) { + if (!aAllOfThem && IsLeftMostChild(placeholderFrame)) { // Left-most children are placed during the line's reflow continue; } diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.h b/mozilla/layout/html/base/src/nsBlockReflowState.h index fbf5b615c7c..c57fa681885 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.h +++ b/mozilla/layout/html/base/src/nsBlockReflowState.h @@ -49,12 +49,46 @@ #include "nsIHTMLContent.h" #include "nsHTMLImage.h" +// XXX temporary for :first-letter support +#include "nsITextContent.h" +static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID);/* XXX */ + +#ifdef NS_DEBUG +#undef NOISY_FIRST_LINE +#undef REALLY_NOISY_FIRST_LINE +#else +#undef NOISY_FIRST_LINE +#endif + +#ifdef REALLY_NOISY_FIRST_LINE +static void +DumpStyleGeneaology(nsIFrame* aFrame, const char* gap) +{ + fputs(gap, stdout); + aFrame->ListTag(stdout); + printf(": "); + nsIStyleContext* sc; + aFrame->GetStyleContext(sc); + while (nsnull != sc) { + nsIStyleContext* psc; + printf("%p ", sc); + psc = sc->GetParent(); + NS_RELEASE(sc); + sc = psc; + } + printf("\n"); +} +#endif + /* 52b33130-0b99-11d2-932e-00805f8add32 */ #define NS_BLOCK_FRAME_CID \ { 0x52b33130, 0x0b99, 0x11d2, {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID; +// 11-03-98: low hanging memory fruit: get rid of text-runs that have +// only one piece of text in them! + // 09-15-98: make sure that the outer container of the block (e.g. the // body sets up the outer top margin feed in properly so that the top // margin collapses properly with the body margin. Note that for the @@ -133,7 +167,7 @@ struct nsBlockReflowState : public nsFrameReflowState { void PlaceFloater(nsPlaceholderFrame* aFloater, PRBool& aIsLeftFloater); - void PlaceFloaters(nsVoidArray* aFloaters); + void PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem); void ClearFloaters(PRUint8 aBreakType); @@ -154,6 +188,7 @@ struct nsBlockReflowState : public nsFrameReflowState { PRUintn mPrevMarginFlags; // previous margin flag bits (above and beyond the carried-out-margin flags) #define HAVE_CARRIED_MARGIN 0x100 +#define ALREADY_APPLIED_BOTTOM_MARGIN 0x200 nscoord mBottomEdge; // maximum Y @@ -222,6 +257,8 @@ public: // nsIFrame NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList); + NS_IMETHOD ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext); NS_IMETHOD FirstChild(nsIFrame*& aFirstChild) const; NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext); NS_IMETHOD IsSplittable(nsSplittableType& aIsSplittable) const; @@ -280,6 +317,8 @@ public: virtual PRBool DeleteChildsNextInFlow(nsIPresContext& aPresContext, nsIFrame* aNextInFlow); + void RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame); + void SetFlags(PRUint32 aFlags) { mFlags = aFlags; } @@ -390,6 +429,9 @@ public: PRBool IsChild(nsIFrame* aFrame); #endif + nsIStyleContext* mFirstLineStyle; + nsIStyleContext* mFirstLetterStyle; + LineData* mLines; LineData* mOverflowLines; @@ -871,6 +913,7 @@ struct LineData { mCombinedArea.SetRect(0,0,0,0); mCarriedOutTopMargin = 0; mCarriedOutBottomMargin = 0; + mBreakType = NS_STYLE_CLEAR_NONE; } ~LineData(); @@ -1005,6 +1048,7 @@ struct LineData { nsIFrame* mFirstChild; PRUint16 mChildCount; PRUint16 mState; + PRUint8 mBreakType; nsRect mBounds; nsRect mCombinedArea; nscoord mCarriedOutTopMargin; @@ -1466,6 +1510,8 @@ nsBlockFrame::nsBlockFrame(nsIContent* aContent, nsIFrame* aParent) nsBlockFrame::~nsBlockFrame() { + NS_IF_RELEASE(mFirstLineStyle); + NS_IF_RELEASE(mFirstLetterStyle); nsTextRun::DeleteTextRuns(mTextRuns); } @@ -1493,6 +1539,81 @@ nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr); } +NS_IMETHODIMP +nsBlockFrame::ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext) +{ + nsIStyleContext* oldContext = mStyleContext; + nsresult rv = nsFrame::ReResolveStyleContext(aPresContext, aParentContext); + if (NS_OK != rv) { + return rv; + } + if (oldContext != mStyleContext) { + // Re-resolve the :first-line pseudo style context + if (nsnull == mPrevInFlow) { + nsIStyleContext* newFirstLineStyle = + aPresContext->ProbePseudoStyleContextFor(mContent, + nsHTMLAtoms::firstLinePseudo, + mStyleContext); + if (newFirstLineStyle != mFirstLineStyle) { + NS_IF_RELEASE(mFirstLineStyle); + mFirstLineStyle = newFirstLineStyle; + } + else { + NS_IF_RELEASE(newFirstLineStyle); + } + + // Re-resolve the :first-letter pseudo style context + nsIStyleContext* newFirstLetterStyle = + aPresContext->ProbePseudoStyleContextFor(mContent, + nsHTMLAtoms::firstLetterPseudo, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + if (newFirstLetterStyle != mFirstLetterStyle) { + NS_IF_RELEASE(mFirstLetterStyle); + mFirstLetterStyle = newFirstLetterStyle; + } + else { + NS_IF_RELEASE(newFirstLetterStyle); + } + } + + // Update the child frames on each line + LineData* line = mLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + PRInt32 n = line->mChildCount; + while ((--n >= 0) && NS_SUCCEEDED(rv)) { + if (line == mLines) { + rv = child->ReResolveStyleContext(aPresContext, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + } + else { + rv = child->ReResolveStyleContext(aPresContext, mStyleContext); + } + child->GetNextSibling(child); + } + line = line->mNext; + } + + // Update the child frames on each overflow line too + line = mOverflowLines; + while (nsnull != line) { + nsIFrame* child = line->mFirstChild; + PRInt32 n = line->mChildCount; + while ((--n >= 0) && NS_SUCCEEDED(rv)) { + rv = child->ReResolveStyleContext(aPresContext, mStyleContext); + child->GetNextSibling(child); + } + line = line->mNext; + } + } + return rv; +} + NS_IMETHODIMP nsBlockFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) { @@ -1527,11 +1648,23 @@ nsBlockFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) // it to the flow now. const nsStyleList* styleList; GetStyleData(eStyleStruct_List, (const nsStyleStruct*&) styleList); -//XXX ListTag(stdout); printf(": lsp=%d\n", styleList->mListStylePosition); if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == styleList->mListStylePosition) { InsertNewFrame(aPresContext, this, mBullet, nsnull); } } + + // Lookup up the two pseudo style contexts + if (nsnull == mPrevInFlow) { + mFirstLineStyle = aPresContext. + ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLinePseudo, + mStyleContext); + mFirstLetterStyle = aPresContext. + ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLetterPseudo, + (nsnull != mFirstLineStyle + ? mFirstLineStyle + : mStyleContext)); + } + return NS_OK; } @@ -1744,7 +1877,6 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext, nsInlineReflow inlineReflow(state.mLineLayout, state, this, PR_TRUE); state.mInlineReflow = &inlineReflow; state.mLineLayout.PushInline(&inlineReflow); -// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F'); nsresult rv = NS_OK; if (eReflowReason_Initial == state.reason) { @@ -2302,10 +2434,40 @@ nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState) } if (nsnull != lastCleanLine) { - // Place any floaters the line has + // Unplace any floaters on the lastCleanLine and every line that + // follows it (which shouldn't be much given that this is + // frame-appended reflow) + LineData* line = lastCleanLine; + while (nsnull != line) { + line->UnplaceFloaters(aState.mSpaceManager); + line = line->mNext; + } + + // Place any floaters the last clean line has if (nsnull != lastCleanLine->mFloaters) { aState.mCurrentLine = lastCleanLine; - aState.PlaceFloaters(lastCleanLine->mFloaters); + aState.PlaceFloaters(lastCleanLine->mFloaters, PR_TRUE); + } + + // Apply any clear-after semantics the line might have + nscoord y0, dy; + switch (lastCleanLine->mBreakType) { + case NS_STYLE_CLEAR_LEFT: + case NS_STYLE_CLEAR_RIGHT: + case NS_STYLE_CLEAR_LEFT_AND_RIGHT: + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, + ("line %p: ClearFloaters: y=%d, break-type=%d\n", + lastCleanLine, aState.mY, lastCleanLine->mBreakType)); + y0 = aState.mY; + aState.ClearFloaters(lastCleanLine->mBreakType); + dy = aState.mY - y0; + if (dy > aState.mPrevBottomMargin) { + aState.mPrevBottomMargin = dy; + aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, + (" cleared floaters, now y=%d\n", aState.mY)); + break; } } @@ -2393,6 +2555,7 @@ nsBlockFrame::FrameInsertedReflow(nsBlockReflowState& aState) // other lines can be moved; recover state before first dirty line. // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2512,6 +2675,7 @@ nsBlockFrame::FrameRemovedReflow(nsBlockReflowState& aState) // other lines can be moved; recover state before first dirty line. // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2538,6 +2702,7 @@ nsBlockFrame::ChildIncrementalReflow(nsBlockReflowState& aState) #endif // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2547,6 +2712,7 @@ nsresult nsBlockFrame::StyleChangedReflow(nsBlockReflowState& aState) { // XXX temporary + aState.mSpaceManager->ClearRegions(); aState.GetAvailableSpace(); aState.mPrevLine = nsnull; return ReflowLinesAt(aState, mLines); @@ -2882,6 +3048,15 @@ nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState, } aState.mInlineReflow->Init(x, aState.mY, availWidth, availHeight); aState.mInlineReflowPrepared = PR_TRUE; + + // Setup the first-letter-style-ok flag + nsLineLayout& lineLayout = aState.mLineLayout; + if (mFirstLetterStyle && (0 == lineLayout.GetLineNumber())) { + lineLayout.SetFirstLetterStyleOK(PR_TRUE); + } + else { + lineLayout.SetFirstLetterStyleOK(PR_FALSE); + } } // XXX switch to two versions: inline vs. block? @@ -3201,8 +3376,11 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Refresh our available space in case a floater was placed by our // child. // XXX expensive! + // XXX unnecessary because the child block will contain the floater! aState.GetAvailableSpace(); + aLine->mBreakType = NS_STYLE_CLEAR_NONE; + if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) { // Some of the block fit. We need to have the block frame // continued, so we make sure that it has a next-in-flow now. @@ -3260,6 +3438,50 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, PrepareInlineReflow(aState, aFrame, PR_FALSE); } + // When reflowing a frame that is on the first-line, check and see + // if a special style context should be placed in the context chain. + PRBool reflowingFirstLetter = PR_FALSE; + if ((nsnull == mPrevInFlow) && + (0 == aState.mLineLayout.GetLineNumber())) { + if (nsnull != mFirstLineStyle) { + // Update the child frames style to inherit from the first-line + // style. + // XXX add code to check first and only do it if it needs doing! +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, ""); +#endif +#ifdef NOISY_FIRST_LINE + aFrame->ListTag(stdout); + printf(": adding in first-line style\n"); +#endif + aFrame->ReResolveStyleContext(&aState.mPresContext, mFirstLineStyle); +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, " "); +#endif + } + if ((nsnull != mFirstLetterStyle) && + aState.mLineLayout.GetFirstLetterStyleOK()) { + // XXX For now, we only allow text objects to have :first-letter + // style. + nsIContent* c; + nsresult rv = aFrame->GetContent(c); + if (NS_SUCCEEDED(rv) && (nsnull != c)) { + nsITextContent* tc; + nsresult rv = c->QueryInterface(kITextContentIID, (void**)&tc); + if (NS_SUCCEEDED(rv)) { + // XXX add code to check first and only do it if it needs doing! + // Update the child frames style to inherit from the + // first-letter style. + aFrame->ReResolveStyleContext(&aState.mPresContext, + mFirstLetterStyle); + reflowingFirstLetter = PR_TRUE; + NS_RELEASE(tc); + } + NS_RELEASE(c); + } + } + } + PRBool isFirstChild = (aLine == mLines) && (aFrame == aLine->mFirstChild); aState.mInlineReflow->SetIsFirstChild(isFirstChild); @@ -3292,6 +3514,17 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, aLine->mChildCount++; } aFrame->GetNextSibling(aFrame); + +#if 0 + // XXX Special hackery to keep going if we just split because of a + // :first-letter situation. + if (reflowingFirstLetter) { + if (aState.mInlineReflow->GetAvailWidth() > 0) { + aLine->SetLastContentIsComplete();/* XXX what to do??? */ + return PR_TRUE; + } + } +#endif } else { if (NS_INLINE_IS_BREAK_AFTER(aReflowResult)) { @@ -3331,6 +3564,37 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, return PR_FALSE; } +void +nsBlockFrame::RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame) +{ +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, ""); +#endif + + nsIStyleContext* kidSC; + aFrame->GetStyleContext(kidSC); + if (nsnull != kidSC) { + nsIStyleContext* kidParentSC; + kidParentSC = kidSC->GetParent(); + if ((kidParentSC == mFirstLineStyle) || + (kidParentSC == mFirstLetterStyle)) { +#ifdef NOISY_FIRST_LINE + printf(" "); + aFrame->ListTag(stdout); + printf(": removing first-line style\n"); +#endif + + aFrame->ReResolveStyleContext(&aPresContext, mStyleContext); + +#ifdef REALLY_NOISY_FIRST_LINE + DumpStyleGeneaology(aFrame, " "); +#endif + } + NS_IF_RELEASE(kidParentSC); + NS_RELEASE(kidSC); + } +} + // XXX alloc lines using free-list in aState // XXX refactor this since the split NEVER has to deal with blocks @@ -3344,8 +3608,8 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, PRInt32 pushCount = aLine->ChildCount() - aState.mInlineReflow->GetCurrentFrameNum(); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, - ("nsBlockFrame::SplitLine: pushing %d frames", - pushCount)); + ("nsBlockFrame::SplitLine: pushing %d frames", + pushCount)); if (0 != pushCount) { NS_ASSERTION(nsnull != aFrame, "whoops"); LineData* to = aLine->mNext; @@ -3382,6 +3646,19 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, // Let inline reflow know that some frames are no longer part of // its state. aState.mInlineReflow->ChangeFrameCount(aLine->ChildCount()); + + // Repair style contexts when frames move to the next line and + // this is the first line. + if (((nsnull != mFirstLineStyle) || (nsnull != mFirstLetterStyle)) && + (0 == aState.mLineLayout.GetLineNumber())) { + // Take the pseudo-style out of the style context chain + nsIFrame* kid = to->mFirstChild; + PRInt32 n = pushCount; + while (--n >= 0) { + RestoreStyleFor(aState.mPresContext, kid); + kid->GetNextSibling(kid); + } + } } return NS_OK; } @@ -3671,7 +3948,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Any below current line floaters to place? if (0 != aState.mPendingFloaters.Count()) { - aState.PlaceFloaters(&aState.mPendingFloaters); + aState.PlaceFloaters(&aState.mPendingFloaters, PR_FALSE); aState.mPendingFloaters.Clear(); // XXX Factor in the height of the floaters as well when considering @@ -3682,8 +3959,10 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Based on the last child we reflowed reflow status, we may need to // clear past any floaters. + aLine->mBreakType = NS_STYLE_CLEAR_NONE; if (NS_INLINE_IS_BREAK_AFTER(aReflowStatus)) { PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(aReflowStatus); + aLine->mBreakType = breakType; // Apply break to the line switch (breakType) { default: @@ -3694,6 +3973,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("nsBlockFrame::PlaceLine: clearing floaters=%d", breakType)); +#if XXX { nscoord y0 = aState.mY; aState.ClearFloaters(breakType); @@ -3702,6 +3982,18 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aLine->mCarriedOutBottomMargin = dy; } } +#else + aState.ClearFloaters(breakType); +#if 0 + y0 = aState.mY; + aState.ClearFloaters(lastCleanLine->mBreakType); + dy = aState.mY - y0; + if (dy > aState.mPrevBottomMargin) { + aState.mPrevBottomMargin = dy; + aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } +#endif +#endif break; } // XXX page breaks, etc, need to be passed upwards too! @@ -4273,6 +4565,12 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder) ("nsBlockReflowState::AddFloater: IsLeftMostChild, placeHolder=%p", aPlaceholder)); + // Flush out pending bottom margin before placing floater + if (0 != mPrevBottomMargin) { + mY += mPrevBottomMargin; + mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN; + } + // Because we are in the middle of reflowing a placeholder frame // within a line (and possibly nested in an inline frame or two // that's a child of our block) we need to restore the space @@ -4456,7 +4754,7 @@ nsBlockReflowState::PlaceFloater(nsPlaceholderFrame* aPlaceholder, * Place below-current-line floaters. */ void -nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters) +nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem) { NS_PRECONDITION(aFloaters->Count() > 0, "no floaters"); @@ -4464,7 +4762,7 @@ nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters) for (PRInt32 i = 0; i < numFloaters; i++) { nsPlaceholderFrame* placeholderFrame = (nsPlaceholderFrame*) aFloaters->ElementAt(i); - if (IsLeftMostChild(placeholderFrame)) { + if (!aAllOfThem && IsLeftMostChild(placeholderFrame)) { // Left-most children are placed during the line's reflow continue; }