diff --git a/mozilla/layout/base/Makefile.in b/mozilla/layout/base/Makefile.in index c7532e842eb..9fec278b0ff 100644 --- a/mozilla/layout/base/Makefile.in +++ b/mozilla/layout/base/Makefile.in @@ -27,6 +27,9 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk DIRS = public src +ifdef ENABLE_TESTS +DIRS += tests +endif include $(topsrcdir)/config/rules.mk diff --git a/mozilla/layout/base/nsCaret.cpp b/mozilla/layout/base/nsCaret.cpp index 01eb0531487..488311963db 100644 --- a/mozilla/layout/base/nsCaret.cpp +++ b/mozilla/layout/base/nsCaret.cpp @@ -81,7 +81,7 @@ //------------------------------END OF IBM BIDI-------------------------------- #endif //IBMBIDI -static NS_DEFINE_IID(kLookAndFeelCID, NS_LOOKANDFEEL_CID); +static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID); //----------------------------------------------------------------------------- diff --git a/mozilla/layout/base/nsFrameManager.cpp b/mozilla/layout/base/nsFrameManager.cpp index 4dfd1950d53..74dc31e1c30 100644 --- a/mozilla/layout/base/nsFrameManager.cpp +++ b/mozilla/layout/base/nsFrameManager.cpp @@ -92,7 +92,7 @@ #endif // NEW_CONTEXT_PARENTAGE_INVARIANT // Class IID's -static NS_DEFINE_IID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); +static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); // IID's diff --git a/mozilla/layout/base/public/nsHTMLReflowMetrics.h b/mozilla/layout/base/public/nsHTMLReflowMetrics.h index 036e4d3eeba..026b59242a0 100644 --- a/mozilla/layout/base/public/nsHTMLReflowMetrics.h +++ b/mozilla/layout/base/public/nsHTMLReflowMetrics.h @@ -54,6 +54,65 @@ struct nsSize; #define NS_REFLOW_CALC_BOUNDING_METRICS 0x0002 #endif +/** + * An nsCollapsingMargin represents a vertical collapsing margin between + * blocks as described in section 8.3.1 of CSS2, + * . + * + * All adjacent vertical margins collapse, and the resulting margin is + * the sum of the largest positive margin included and the smallest (most + * negative) negative margin included. + */ +struct nsCollapsingMargin { + private: + nscoord mMostPos; // the largest positive margin included + nscoord mMostNeg; // the smallest negative margin included + + public: + nsCollapsingMargin() + : mMostPos(0), + mMostNeg(0) + { + } + + nsCollapsingMargin(const nsCollapsingMargin& aOther) + : mMostPos(aOther.mMostPos), + mMostNeg(aOther.mMostNeg) + { + } + + nsCollapsingMargin& operator=(const nsCollapsingMargin& aOther) + { + mMostPos = aOther.mMostPos; + mMostNeg = aOther.mMostNeg; + return *this; + } + + void Include(nscoord aCoord) + { + if (aCoord > mMostPos) + mMostPos = aCoord; + else if (aCoord < mMostNeg) + mMostNeg = aCoord; + } + + void Zero() + { + mMostPos = 0; + mMostNeg = 0; + } + + PRBool IsZero() const + { + return (mMostPos == 0) && (mMostNeg == 0); + } + + nscoord get() const + { + return mMostPos + mMostNeg; + } +}; + /** * Reflow metrics used to return the frame's desired size and alignment * information. @@ -83,7 +142,7 @@ struct nsHTMLReflowMetrics { // Carried out bottom margin values. This is the collapsed // (generational) bottom margin value. - nscoord mCarriedOutBottomMargin; + nsCollapsingMargin mCarriedOutBottomMargin; // For frames that have content that overflow their content area // (NS_FRAME_OUTSIDE_CHILDREN) this rectangle represents the total area @@ -104,7 +163,6 @@ struct nsHTMLReflowMetrics { maxElementSize = aMaxElementSize; mMaximumWidth = 0; mFlags = aFlags; - mCarriedOutBottomMargin = 0; mOverflowArea.x = 0; mOverflowArea.y = 0; mOverflowArea.width = 0; diff --git a/mozilla/layout/base/public/nsIFrame.h b/mozilla/layout/base/public/nsIFrame.h index ce08c45d8ea..c042b2279e4 100644 --- a/mozilla/layout/base/public/nsIFrame.h +++ b/mozilla/layout/base/public/nsIFrame.h @@ -1116,6 +1116,21 @@ public: nsIRenderingContext& aRenderingContext, PRBool aCheckVis, PRBool* aIsVisible) = 0; + + /** + * Determine whether the frame is logically empty, i.e., whether the + * layout would be the same whether or not the frame is present. + * Placeholder frames should return true. Block frames should be + * considered empty whenever margins collapse through them, even + * though those margins are relevant. + * + * aIsPre should be ignored by frames to which the 'white-space' + * property applies. + */ + NS_IMETHOD IsEmpty(PRBool aIsQuirkMode, + PRBool aIsPre, + PRBool* aResult) = 0; + #ifdef IBMBIDI /** * retrieve and set Bidi property of this frame diff --git a/mozilla/layout/base/public/nsISpaceManager.h b/mozilla/layout/base/public/nsISpaceManager.h index ad9f0f21512..3898e3d6dc5 100644 --- a/mozilla/layout/base/public/nsISpaceManager.h +++ b/mozilla/layout/base/public/nsISpaceManager.h @@ -214,6 +214,20 @@ public: */ NS_IMETHOD ClearRegions() = 0; + /** + * Methods for dealing with the propagation of float damage during + * reflow. + * + * (Yes, this is bad XPCOM style, but I want to remove the interface + * here -- see bug 102453. Once that's done, these (and many others) + * could be inline functions.) + */ + NS_IMETHOD_(PRBool) HasFloatDamage() = 0; + NS_IMETHOD_(void) IncludeInDamage(nscoord aIntervalBegin, + nscoord aIntervalEnd) = 0; + NS_IMETHOD_(PRBool) IntersectsDamage(nscoord aIntervalBegin, + nscoord aIntervalEnd) = 0; + #ifdef DEBUG /** * Dump the state of the spacemanager out to a file diff --git a/mozilla/layout/base/src/Makefile.in b/mozilla/layout/base/src/Makefile.in index 4effd346d3f..d5cc98baa99 100644 --- a/mozilla/layout/base/src/Makefile.in +++ b/mozilla/layout/base/src/Makefile.in @@ -53,19 +53,20 @@ CPPSRCS = \ nsAutoCopy.cpp \ nsCaret.cpp \ nsCopySupport.cpp \ - nsImageLoader.cpp \ nsFrameList.cpp \ nsFrameTraversal.cpp \ nsFrameUtil.cpp \ nsGalleyContext.cpp \ + nsImageLoader.cpp \ + nsIntervalSet.cpp \ nsLayoutDebugger.cpp \ + nsLayoutHistoryState.cpp \ nsPresContext.cpp \ nsPresState.cpp \ nsPrintContext.cpp \ nsPrintPreviewContext.cpp \ nsSpaceManager.cpp \ nsStyleChangeList.cpp \ - nsLayoutHistoryState.cpp \ $(NULL) ifdef IBMBIDI diff --git a/mozilla/layout/base/src/makefile.win b/mozilla/layout/base/src/makefile.win index 3566789be80..9f7e356ff2e 100644 --- a/mozilla/layout/base/src/makefile.win +++ b/mozilla/layout/base/src/makefile.win @@ -50,46 +50,24 @@ DEFINES=-D_IMPL_NS_LAYOUT -DWIN32_LEAN_AND_MEAN DEFINES = $(DEFINES) -DXP_NEW_SELECTION !endif -CPPSRCS = \ - nsAutoCopy.cpp \ - nsCopySupport.cpp \ - nsImageLoader.cpp \ - nsFrameList.cpp \ - nsFrameTraversal.cpp \ - nsFrameUtil.cpp \ - nsGalleyContext.cpp \ - nsPresContext.cpp \ - nsPresState.cpp \ - nsPrintContext.cpp \ - nsPrintPreviewContext.cpp \ - nsSpaceManager.cpp \ - nsStyleChangeList.cpp \ - nsLayoutDebugger.cpp \ - nsCaret.cpp \ - nsLayoutHistoryState.cpp \ -!ifdef IBMBIDI - nsBidiPresUtils.cpp \ -!endif - $(NULL) - - CPP_OBJS= \ .\$(OBJDIR)\nsAutoCopy.obj \ - .\$(OBJDIR)\nsCopySupport.obj \ - .\$(OBJDIR)\nsImageLoader.obj \ + .\$(OBJDIR)\nsCaret.obj \ + .\$(OBJDIR)\nsCopySupport.obj \ .\$(OBJDIR)\nsFrameList.obj \ - .\$(OBJDIR)\nsFrameTraversal.obj \ + .\$(OBJDIR)\nsFrameTraversal.obj \ .\$(OBJDIR)\nsFrameUtil.obj \ .\$(OBJDIR)\nsGalleyContext.obj \ + .\$(OBJDIR)\nsImageLoader.obj \ + .\$(OBJDIR)\nsIntervalSet.obj \ + .\$(OBJDIR)\nsLayoutDebugger.obj \ + .\$(OBJDIR)\nsLayoutHistoryState.obj \ .\$(OBJDIR)\nsPresContext.obj \ .\$(OBJDIR)\nsPresState.obj \ .\$(OBJDIR)\nsPrintContext.obj \ .\$(OBJDIR)\nsPrintPreviewContext.obj \ .\$(OBJDIR)\nsSpaceManager.obj \ .\$(OBJDIR)\nsStyleChangeList.obj \ - .\$(OBJDIR)\nsLayoutDebugger.obj \ - .\$(OBJDIR)\nsCaret.obj \ - .\$(OBJDIR)\nsLayoutHistoryState.obj \ !ifdef IBMBIDI .\$(OBJDIR)\nsBidiPresUtils.obj \ !endif diff --git a/mozilla/layout/base/src/nsCaret.cpp b/mozilla/layout/base/src/nsCaret.cpp index 01eb0531487..488311963db 100644 --- a/mozilla/layout/base/src/nsCaret.cpp +++ b/mozilla/layout/base/src/nsCaret.cpp @@ -81,7 +81,7 @@ //------------------------------END OF IBM BIDI-------------------------------- #endif //IBMBIDI -static NS_DEFINE_IID(kLookAndFeelCID, NS_LOOKANDFEEL_CID); +static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID); //----------------------------------------------------------------------------- diff --git a/mozilla/layout/base/src/nsIntervalSet.cpp b/mozilla/layout/base/src/nsIntervalSet.cpp new file mode 100644 index 00000000000..32cffc0d0b7 --- /dev/null +++ b/mozilla/layout/base/src/nsIntervalSet.cpp @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +// vim:cindent:ts=8:et:sw=4: +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is nsIntervalSet. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by the Initial Developer are Copyright + * (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * L. David Baron (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIntervalSet.h" +#include + +nsIntervalSet::nsIntervalSet(IntervalSetAlloc aAlloc, IntervalSetFree aFree, + void* aAllocatorClosure) + : mList(nsnull), + mAlloc(aAlloc), + mFree(aFree), + mAllocatorClosure(aAllocatorClosure) +{ + NS_ASSERTION(mAlloc && mFree, "null callback params"); +} + +nsIntervalSet::~nsIntervalSet() +{ + Interval *current = mList; + while (current) { + Interval *trash = current; + current = current->mNext; + FreeInterval(trash); + } +} + +void nsIntervalSet::FreeInterval(nsIntervalSet::Interval *aInterval) +{ + NS_ASSERTION(aInterval, "null interval"); + + aInterval->Interval::~Interval(); + (*mFree)(sizeof(Interval), aInterval, mAllocatorClosure); +} + +void nsIntervalSet::IncludeInterval(coord_type aBegin, coord_type aEnd) +{ + Interval *newInterval = NS_STATIC_CAST(Interval*, + (*mAlloc)(sizeof(Interval), mAllocatorClosure)); + if (!newInterval) { + NS_NOTREACHED("allocation failure"); + return; + } + new(newInterval) Interval(aBegin, aEnd); + + Interval **current = &mList; + while (*current && (*current)->mEnd < aBegin) + current = &(*current)->mNext; + + newInterval->mNext = *current; + *current = newInterval; + + Interval *subsumed = newInterval->mNext; + while (subsumed && subsumed->mBegin <= aEnd) { + newInterval->mEnd = PR_MAX(newInterval->mEnd, subsumed->mEnd); + newInterval->mNext = subsumed->mNext; + FreeInterval(subsumed); + subsumed = newInterval->mNext; + } +} + +PRBool nsIntervalSet::HasPoint(coord_type aPoint) const +{ + Interval *current = mList; + while (current && current->mBegin <= aPoint) { + if (current->mEnd >= aPoint) + return PR_TRUE; + current = current->mNext; + } + return PR_FALSE; +} + +PRBool nsIntervalSet::Intersects(coord_type aBegin, coord_type aEnd) const +{ + Interval *current = mList; + while (current && current->mBegin <= aEnd) { + if (current->mEnd >= aBegin) + return PR_TRUE; + current = current->mNext; + } + return PR_FALSE; +} + +PRBool nsIntervalSet::Contains(coord_type aBegin, coord_type aEnd) const +{ + Interval *current = mList; + while (current && current->mBegin <= aBegin) { + if (current->mEnd >= aEnd) + return PR_TRUE; + current = current->mNext; + } + return PR_FALSE; +} diff --git a/mozilla/layout/base/src/nsIntervalSet.h b/mozilla/layout/base/src/nsIntervalSet.h new file mode 100644 index 00000000000..e0f8a7bfbc0 --- /dev/null +++ b/mozilla/layout/base/src/nsIntervalSet.h @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +// vim:cindent:ts=8:et:sw=4: +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is nsIntervalSet. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by the Initial Developer are Copyright + * (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * L. David Baron (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsIntervalSet_h___ +#define nsIntervalSet_h___ + +#include "prtypes.h" +#include "nsCoord.h" + +typedef void * +(* PR_CALLBACK IntervalSetAlloc)(size_t aBytes, void *aClosure); + +typedef void +(* PR_CALLBACK IntervalSetFree) (size_t aBytes, void *aPtr, void *aClosure); + +/* + * A list-based class (hopefully tree-based when I get around to it) + * for representing a set of ranges on a number-line. + */ +class nsIntervalSet { + +public: + + typedef nscoord coord_type; + + nsIntervalSet(IntervalSetAlloc aAlloc, IntervalSetFree aFree, + void* aAllocatorClosure); + ~nsIntervalSet(); + + /* + * Include the interval [aBegin, aEnd] in the set. + * + * Removal of intervals added is not supported because that would + * require keeping track of the individual intervals that were + * added (nsIntervalMap should do that). It would be simple to + * implement ExcludeInterval if anyone wants it, though. + */ + void IncludeInterval(coord_type aBegin, coord_type aEnd); + + /* + * Is the point aPoint contained within the set of intervals? + */ + PRBool HasPoint(coord_type aPoint) const; + + /* + * Are _some_ points in [aBegin, aEnd] contained within the set + * of intervals? + */ + PRBool Intersects(coord_type aBegin, coord_type aEnd) const; + + /* + * Are _all_ points in [aBegin, aEnd] contained within the set + * of intervals? + */ + PRBool Contains(coord_type aBegin, coord_type aEnd) const; + + PRBool IsEmpty() const + { + return !mList; + } + +private: + + class Interval { + + public: + Interval(coord_type aBegin, coord_type aEnd) + : mBegin(aBegin), + mEnd(aEnd), + mPrev(nsnull), + mNext(nsnull) + { + } + + coord_type mBegin; + coord_type mEnd; + Interval *mPrev; + Interval *mNext; + }; + + void FreeInterval(Interval *aInterval); + + Interval *mList; + IntervalSetAlloc mAlloc; + IntervalSetFree mFree; + void *mAllocatorClosure; + +}; + +#endif // !defined(nsIntervalSet_h___) diff --git a/mozilla/layout/base/src/nsSpaceManager.cpp b/mozilla/layout/base/src/nsSpaceManager.cpp index 0a5f464b23f..cddd37f6eb5 100644 --- a/mozilla/layout/base/src/nsSpaceManager.cpp +++ b/mozilla/layout/base/src/nsSpaceManager.cpp @@ -43,6 +43,7 @@ #include "nsVoidArray.h" #include "nsIFrame.h" #include "nsString.h" +#include "nsIPresShell.h" #ifdef DEBUG #include "nsIFrameDebug.h" #endif @@ -80,13 +81,30 @@ nsSpaceManager::BandList::Clear() } } +///////////////////////////////////////////////////////////////////////////// + +// PresShell Arena allocate callback (for nsIntervalSet use below) +static void* PSArenaAllocCB(size_t aSize, void* aClosure) +{ + void *rv; + NS_STATIC_CAST(nsIPresShell*, aClosure)->AllocateFrame(aSize, &rv); + return rv; +} + +// PresShell Arena free callback (for nsIntervalSet use below) +static void PSArenaFreeCB(size_t aSize, void* aPtr, void* aClosure) +{ + NS_STATIC_CAST(nsIPresShell*, aClosure)->FreeFrame(aSize, aPtr); +} + ///////////////////////////////////////////////////////////////////////////// // nsSpaceManager -nsSpaceManager::nsSpaceManager(nsIFrame* aFrame) - : mFrame(aFrame) +nsSpaceManager::nsSpaceManager(nsIPresShell* aPresShell, nsIFrame* aFrame) + : mFrame(aFrame), + mFloatDamage(PSArenaAllocCB, PSArenaFreeCB, aPresShell) { - NS_INIT_REFCNT(); + NS_INIT_ISUPPORTS(); mX = mY = 0; mFrameInfoMap = nsnull; } @@ -118,7 +136,8 @@ NS_IMPL_RELEASE_WITH_DESTROY(nsSpaceManager, LastRelease()) // static -nsSpaceManager *nsSpaceManager::Create(nsIFrame* aFrame) +nsSpaceManager *nsSpaceManager::Create(nsIPresShell* aPresShell, + nsIFrame* aFrame) { if (sCachedSpaceManagerCount > 0) { // We have cached unused instances of this class, return a cached @@ -129,11 +148,11 @@ nsSpaceManager *nsSpaceManager::Create(nsIFrame* aFrame) // Re-initialize the cached space manager by calling its // constructor (using placement new), the destructor was called // when the space manager was put in the cache. - return new (spaceManager) nsSpaceManager(aFrame); + return new (spaceManager) nsSpaceManager(aPresShell, aFrame); } // The cache is empty, this means we haveto create a new instance. - return new nsSpaceManager(aFrame); + return new nsSpaceManager(aPresShell, aFrame); } @@ -1008,6 +1027,23 @@ nsSpaceManager::ClearRegions() return NS_OK; } +NS_IMETHODIMP_(PRBool) nsSpaceManager::HasFloatDamage() +{ + return !mFloatDamage.IsEmpty(); +} + +NS_IMETHODIMP_(void) nsSpaceManager::IncludeInDamage(nscoord aIntervalBegin, + nscoord aIntervalEnd) +{ + mFloatDamage.IncludeInterval(aIntervalBegin + mY, aIntervalEnd + mY); +} + +NS_IMETHODIMP_(PRBool) nsSpaceManager::IntersectsDamage(nscoord aIntervalBegin, + nscoord aIntervalEnd) +{ + return mFloatDamage.Intersects(aIntervalBegin + mY, aIntervalEnd + mY); +} + #ifdef DEBUG NS_IMETHODIMP nsSpaceManager::List(FILE* out) diff --git a/mozilla/layout/base/src/nsSpaceManager.h b/mozilla/layout/base/src/nsSpaceManager.h index 0bf055222d3..98870ac2719 100644 --- a/mozilla/layout/base/src/nsSpaceManager.h +++ b/mozilla/layout/base/src/nsSpaceManager.h @@ -39,6 +39,9 @@ #include "nsISpaceManager.h" #include "prclist.h" +#include "nsIntervalSet.h" + +class nsIPresShell; #define NS_SPACE_MANAGER_CACHE_SIZE 4 @@ -48,7 +51,8 @@ */ class nsSpaceManager : public nsISpaceManager { public: - static nsSpaceManager *Create(nsIFrame* aFrame); + static nsSpaceManager *Create(nsIPresShell* aPresShell, + nsIFrame* aFrame); static void Shutdown(); // nsISupports @@ -76,13 +80,19 @@ public: NS_IMETHOD ClearRegions(); + NS_IMETHOD_(PRBool) HasFloatDamage(); + NS_IMETHOD_(void) IncludeInDamage(nscoord aIntervalBegin, + nscoord aIntervalEnd); + NS_IMETHOD_(PRBool) IntersectsDamage(nscoord aIntervalBegin, + nscoord aIntervalEnd); + #ifdef DEBUG NS_IMETHOD List(FILE* out); void SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const; #endif protected: - nsSpaceManager(nsIFrame* aFrame); + nsSpaceManager(nsIPresShell* aPresShell, nsIFrame* aFrame); // Structure that maintains information about the region associated // with a particular frame @@ -166,6 +176,7 @@ protected: nscoord mX, mY; // translation from local to global coordinate space BandList mBandList; // header/sentinel for circular linked list of band rects FrameInfo* mFrameInfoMap; + nsIntervalSet mFloatDamage; protected: virtual ~nsSpaceManager(); diff --git a/mozilla/layout/base/tests/Makefile.in b/mozilla/layout/base/tests/Makefile.in index 02fa8d08fa1..a1a4ab32882 100644 --- a/mozilla/layout/base/tests/Makefile.in +++ b/mozilla/layout/base/tests/Makefile.in @@ -24,22 +24,30 @@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ +REQUIRES = xpcom \ + layout \ + gfx \ + $(NULL) + include $(DEPTH)/config/autoconf.mk -PROGRAM = TestSpaceManager +CPPSRCS = \ + TestSpaceManager.cpp \ + $(NULL) -CPPSRCS = TestSpaceManager.cpp +SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=$(BIN_SUFFIX)) -LOCAL_INCLUDES = -I$(srcdir)/../src +LOCAL_INCLUDES = \ + -I$(srcdir)/../src \ + $(NULL) LIBS += \ - $(DIST)/lib/libgkbase_s.a \ - -lgkgfx \ - $(MOZ_JS_LIBS) \ - $(XPCOM_LIBS) \ - $(NSPR_LIBS) \ - $(TK_LIBS) \ - $(NULL) + $(DIST)/lib/libgkbase_s.a \ + -lgkgfx \ + $(MOZ_COMPONENT_LIBS) \ + $(MOZ_JS_LIBS) \ + $(TK_LIBS) \ + $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/mozilla/layout/base/tests/TestSpaceManager.cpp b/mozilla/layout/base/tests/TestSpaceManager.cpp index 8fb788f986b..56bfc59c4c4 100644 --- a/mozilla/layout/base/tests/TestSpaceManager.cpp +++ b/mozilla/layout/base/tests/TestSpaceManager.cpp @@ -44,7 +44,8 @@ class MySpaceManager: public nsSpaceManager { public: - MySpaceManager(nsIFrame* aFrame) : nsSpaceManager(aFrame) {} + MySpaceManager(nsIPresShell* aPresShell, nsIFrame* aFrame) + : nsSpaceManager(aPresShell, aFrame) {} PRBool TestAddBand(); PRBool TestAddBandOverlap(); @@ -810,7 +811,7 @@ PRBool MySpaceManager::TestGetBandData() int main(int argc, char** argv) { // Create a space manager - MySpaceManager* spaceMgr = new MySpaceManager(nsnull); + MySpaceManager* spaceMgr = new MySpaceManager(nsnull, nsnull); NS_ADDREF(spaceMgr); diff --git a/mozilla/layout/forms/nsListControlFrame.cpp b/mozilla/layout/forms/nsListControlFrame.cpp index 440292d4138..e2cb32db0a6 100644 --- a/mozilla/layout/forms/nsListControlFrame.cpp +++ b/mozilla/layout/forms/nsListControlFrame.cpp @@ -3271,6 +3271,14 @@ nsListControlFrame::GetFrameType(nsIAtom** aType) const return NS_OK; } +#ifdef DEBUG +NS_IMETHODIMP +nsListControlFrame::GetFrameName(nsString& aResult) const +{ + return MakeFrameName("ListControl", aResult); +} +#endif + //--------------------------------------------------------- NS_IMETHODIMP nsListControlFrame::GetMaximumSize(nsSize &aSize) diff --git a/mozilla/layout/forms/nsListControlFrame.h b/mozilla/layout/forms/nsListControlFrame.h index 5ff2aa6dae2..6fc6c4d9e92 100644 --- a/mozilla/layout/forms/nsListControlFrame.h +++ b/mozilla/layout/forms/nsListControlFrame.h @@ -222,6 +222,11 @@ public: */ NS_IMETHOD GetFrameType(nsIAtom** aType) const; +#ifdef DEBUG + // nsIFrameDebug + NS_IMETHOD GetFrameName(nsString& aResult) const; +#endif + // nsIFormControlFrame NS_IMETHOD GetType(PRInt32* aType) const; NS_IMETHOD GetName(nsString* aName); diff --git a/mozilla/layout/generic/nsBlockFrame.cpp b/mozilla/layout/generic/nsBlockFrame.cpp index 2c02c4ab715..f2eb958e39c 100644 --- a/mozilla/layout/generic/nsBlockFrame.cpp +++ b/mozilla/layout/generic/nsBlockFrame.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -71,6 +72,7 @@ #include "nsIFocusTracker.h" #include "nsIFrameSelection.h" #include "nsSpaceManager.h" +#include "nsIntervalSet.h" #include "prenv.h" #include "plstr.h" #include "nsGUIEvent.h" @@ -339,7 +341,8 @@ nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) *aInstancePtr = (void*) tmp; return NS_OK; } - if ( aIID.Equals(NS_GET_IID(nsILineIterator)) || aIID.Equals(NS_GET_IID(nsILineIteratorNavigator)) ) + if ( aIID.Equals(NS_GET_IID(nsILineIterator)) || + aIID.Equals(NS_GET_IID(nsILineIteratorNavigator)) ) { nsLineIterator* it = new nsLineIterator; if (!it) { @@ -379,18 +382,18 @@ nsBlockFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) con nsIView* view; GetView(aPresContext, &view); if (nsnull != view) { - fprintf(out, " [view=%p]", view); + fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, view)); } if (nsnull != mNextSibling) { - fprintf(out, " next=%p", mNextSibling); + fprintf(out, " next=%p", NS_STATIC_CAST(void*, mNextSibling)); } // Output the flow linkage if (nsnull != mPrevInFlow) { - fprintf(out, " prev-in-flow=%p", mPrevInFlow); + fprintf(out, " prev-in-flow=%p", NS_STATIC_CAST(void*, mPrevInFlow)); } if (nsnull != mNextInFlow) { - fprintf(out, " next-in-flow=%p", mNextInFlow); + fprintf(out, " next-in-flow=%p", NS_STATIC_CAST(void*, mNextInFlow)); } // Output the rect and state @@ -400,27 +403,28 @@ nsBlockFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) con } PRInt32 numInlineLines = 0; PRInt32 numBlockLines = 0; - if (nsnull != mLines) { - nsLineBox* line = mLines; - while (nsnull != line) { - if (line->IsBlock()) { + if (! mLines.empty()) { + for (const_line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { + if (line->IsBlock()) numBlockLines++; - } - else { + else numInlineLines++; - } - line = line->mNext; } } - fprintf(out, " sc=%p(i=%d,b=%d)<\n", mStyleContext, numInlineLines, numBlockLines); + fprintf(out, " sc=%p(i=%d,b=%d)<\n", + NS_STATIC_CAST(void*, mStyleContext), numInlineLines, numBlockLines); aIndent++; // Output the lines - if (nsnull != mLines) { - nsLineBox* line = mLines; - while (nsnull != line) { + if (! mLines.empty()) { + for (const_line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { line->List(aPresContext, out, aIndent); - line = line->mNext; } } @@ -444,7 +448,7 @@ nsBlockFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) con while (nsnull != kid) { nsIFrameDebug* frameDebug; - if (NS_SUCCEEDED(kid->QueryInterface(NS_GET_IID(nsIFrameDebug), (void**)&frameDebug))) { + if (NS_SUCCEEDED(CallQueryInterface(kid, &frameDebug))) { frameDebug->List(aPresContext, out, aIndent + 1); } kid->GetNextSibling(&kid); @@ -491,12 +495,14 @@ nsBlockFrame::FirstChild(nsIPresContext* aPresContext, return mAbsoluteContainer.FirstChild(this, aListName, aFirstChild); } else if (nsnull == aListName) { - *aFirstChild = (nsnull != mLines) ? mLines->mFirstChild : nsnull; + *aFirstChild = (mLines.empty()) ? nsnull : mLines.front()->mFirstChild; return NS_OK; } else if (aListName == nsLayoutAtoms::overflowList) { - nsLineBox* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); - *aFirstChild = overflowLines ? overflowLines->mFirstChild : nsnull; + nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); + *aFirstChild = overflowLines + ? overflowLines->front()->mFirstChild + : nsnull; return NS_OK; } else if (aListName == nsLayoutAtoms::floaterList) { @@ -689,7 +695,9 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, // only when there are actually floats to manage. Otherwise things // like tables will gain significant bloat. if (NS_BLOCK_SPACE_MGR & mState) { - nsSpaceManager* rawPtr = nsSpaceManager::Create(this); + nsCOMPtr shell; + aPresContext->GetShell(getter_AddRefs(shell)); + nsSpaceManager* rawPtr = nsSpaceManager::Create(shell, this); if (!rawPtr) { return NS_ERROR_OUT_OF_MEMORY; } @@ -710,7 +718,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, if (eReflowReason_Resize != aReflowState.reason) { #ifdef IBMBIDI - if (mLines) { + if (! mLines.empty()) { PRBool bidiEnabled; aPresContext->GetBidiEnabled(&bidiEnabled); if (bidiEnabled) { @@ -719,11 +727,15 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, if (bidiUtils) { PRBool forceReflow; nsresult rc = bidiUtils->Resolve(aPresContext, this, - mLines->mFirstChild, forceReflow); + mLines.front()->mFirstChild, + forceReflow); if (NS_SUCCEEDED(rc) && forceReflow) { // Mark everything dirty // XXXldb This should be done right. - for (nsLineBox* line = mLines; line; line = line->mNext) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { line->MarkDirty(); } } @@ -792,6 +804,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, break; case eReflowReason_StyleChange: + DrainOverflowLines(aPresContext); rv = PrepareStyleChangedReflow(state); break; @@ -998,7 +1011,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, printf(": status=%x (%scomplete) metrics=%d,%d carriedMargin=%d", aStatus, NS_FRAME_IS_COMPLETE(aStatus) ? "" : "not ", aMetrics.width, aMetrics.height, - aMetrics.mCarriedOutBottomMargin); + aMetrics.mCarriedOutBottomMargin.get()); if (mState & NS_FRAME_OUTSIDE_CHILDREN) { printf(" combinedArea={%d,%d,%d,%d}", aMetrics.mOverflowArea.x, @@ -1018,7 +1031,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, PRTime end = PR_Now(); PRInt32 ectc = nsLineBox::GetCtorCount(); - PRInt32 numLines = nsLineBox::ListLength(mLines); + PRInt32 numLines = mLines.size(); if (!numLines) numLines = 1; PRTime delta, perLineDelta, lines; LL_I2L(lines, numLines); @@ -1177,9 +1190,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // exactly one line and that line is not a block line AND that // line doesn't end in a BR of any sort THEN we remain a compact // frame. - if ((nsnull == mLines) || - ((nsnull == mLines->mNext) && !mLines->IsBlock() && - (NS_STYLE_CLEAR_NONE == mLines->GetBreakType()) + if ((mLines.empty()) || + ((mLines.front() == mLines.back()) && !mLines.front()->IsBlock() && + (NS_STYLE_CLEAR_NONE == mLines.front()->GetBreakType()) /*XXX && (computedWidth <= aState.mCompactMarginWidth) */ )) { compact = PR_TRUE; @@ -1252,7 +1265,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, reflowState.reason = eReflowReason_Resize; reflowState.mSpaceManager->ClearRegions(); +#ifdef DEBUG nscoord oldDesiredWidth = aMetrics.width; +#endif nsBlockReflowState state(reflowState, aState.mPresContext, this, aMetrics, NS_BLOCK_MARGIN_ROOT & mState); ReflowDirtyLines(state); @@ -1285,8 +1300,8 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // unless the bottom of the last line adjoins the bottom of our // content area. if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { - if (aState.mY + aState.mPrevBottomMargin != aMetrics.height) { - aState.mPrevBottomMargin = 0; + if (aState.mY + aState.mPrevBottomMargin.get() != aMetrics.height) { + aState.mPrevBottomMargin.Zero(); } } } @@ -1298,7 +1313,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // When we are a bottom-margin root make sure that our last // childs bottom margin is fully applied. // XXX check for a fit - autoHeight += aState.mPrevBottomMargin; + autoHeight += aState.mPrevBottomMargin.get(); } autoHeight += borderPadding.bottom; @@ -1339,8 +1354,14 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // Return bottom margin information - aMetrics.mCarriedOutBottomMargin = - aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? 0 : aState.mPrevBottomMargin; + // rbs says he hit this assertion occasionally (see bug 86947), so + // just set the margin to zero and we'll figure out why later + //NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(), + // "someone else set the margin"); + if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) + aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin; + else + aMetrics.mCarriedOutBottomMargin.Zero(); #ifdef DEBUG_blocks if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) { @@ -1383,8 +1404,10 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // XXX_perf: This can be done incrementally nscoord xa = 0, ya = 0, xb = aMetrics.width, yb = aMetrics.height; if (NS_STYLE_OVERFLOW_HIDDEN != aReflowState.mStyleDisplay->mOverflow) { - nsLineBox* line = mLines; - while (nsnull != line) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { // Compute min and max x/y values for the reflowed frame's // combined areas nsRect lineCombinedArea; @@ -1405,7 +1428,6 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, if (ymost > yb) { yb = ymost; } - line = line->mNext; } // Factor the bullet in; normally the bullet will be factored into @@ -1484,12 +1506,17 @@ nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState) { // Determine the line being impacted PRBool isFloater; - nsLineBox* prevLine; - nsLineBox* line = FindLineFor(aState.mNextRCFrame, &prevLine, &isFloater); - if (! line) { + line_iterator line; + FindLineFor(aState.mNextRCFrame, &isFloater, &line); + if (line == end_lines()) { // This assertion actually fires on lots of pages // (e.g., bugzilla, bugzilla query page), so limit it // to a few people until we fix the problem causing it. + // + // I think waterson explained once why it was happening -- I think + // it has something to do with the interaction of the unconstrained + // reflow in multi-pass reflow with the reflow command's chain, but + // I don't remember the details. #if defined(DEBUG_dbaron) || defined(DEBUG_waterson) NS_NOTREACHED("We don't have a line for the target of the reflow. " "Being inefficient"); @@ -1506,19 +1533,21 @@ nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState) if (line->IsInline()) { aState.SetFlag(BRS_ISINLINEINCRREFLOW, PR_TRUE); - - // Don't mark the prevLine as dirty if BRS_ISINLINEINCRREFLOW - prevLine = nsnull; } - // Figure out which line to mark dirty. - MarkLineDirty(line, prevLine); + // Just mark this line dirty. We never need to mark the + // previous line dirty since either: + // * the line is a block, and there would never be a chance to pull + // something up + // * It's an incremental reflow to something within an inline, which + // we know must be very limited. + line->MarkDirty(); return NS_OK; } nsresult -nsBlockFrame::MarkLineDirty(nsLineBox* aLine, nsLineBox* aPrevLine) +nsBlockFrame::MarkLineDirty(line_iterator aLine) { // Mark aLine dirty aLine->MarkDirty(); @@ -1526,22 +1555,23 @@ nsBlockFrame::MarkLineDirty(nsLineBox* aLine, nsLineBox* aPrevLine) if (gNoisyReflow) { IndentBy(stdout, gNoiseIndent); ListTag(stdout); - printf(": mark line %p dirty\n", aLine); + printf(": mark line %p dirty\n", NS_STATIC_CAST(void*, aLine.get())); } #endif // Mark previous line dirty if its an inline line so that it can // maybe pullup something from the line just affected. // XXX We don't need to do this if aPrevLine ends in a break-after... - // XXXldb Couldn't we additionally limit this to when - // !aLine->IsBlock() ? - if (aPrevLine && !aPrevLine->IsBlock()) { - aPrevLine->MarkDirty(); + if (aLine != mLines.front() && + aLine->IsInline() && + aLine.prev()->IsInline()) { + aLine.prev()->MarkDirty(); #ifdef DEBUG if (gNoisyReflow) { IndentBy(stdout, gNoiseIndent); ListTag(stdout); - printf(": mark prev-line %p dirty\n", aPrevLine); + printf(": mark prev-line %p dirty\n", + NS_STATIC_CAST(void*, aLine.prev().get())); } #endif } @@ -1562,10 +1592,11 @@ nsBlockFrame::UpdateBulletPosition(nsBlockReflowState& aState) if (mBullet && HaveOutsideBullet()) { // We now have an inside bullet, but used to have an outside // bullet. Adjust the frame line list - if (mLines) { + if (! mLines.empty()) { // if we have a line already, then move the bullet to the front of the // first line nsIFrame* child = nsnull; + nsLineBox* firstLine = mLines.front(); #ifdef DEBUG // bullet should not have any siblings if it was an outside bullet @@ -1574,20 +1605,20 @@ nsBlockFrame::UpdateBulletPosition(nsBlockReflowState& aState) NS_ASSERTION(!next, "outside bullet should not have siblings"); #endif // move bullet to front and chain the previous frames, and update the line count - child = mLines->mFirstChild; - mLines->mFirstChild = mBullet; + child = firstLine->mFirstChild; + firstLine->mFirstChild = mBullet; mBullet->SetNextSibling(child); - PRInt32 count = mLines->GetChildCount(); - mLines->SetChildCount(count+1); + PRInt32 count = firstLine->GetChildCount(); + firstLine->SetChildCount(count+1); // dirty it here in case the caller does not - mLines->MarkDirty(); + firstLine->MarkDirty(); } else { // no prior lines, just create a new line for the bullet nsLineBox* line = aState.NewLineBox(mBullet, 1, PR_FALSE); if (!line) { return NS_ERROR_OUT_OF_MEMORY; } - mLines = line; + mLines.push_back(line); } } mState &= ~NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET; @@ -1597,24 +1628,24 @@ nsBlockFrame::UpdateBulletPosition(nsBlockReflowState& aState) // We now have an outside bullet, but used to have an inside // bullet. Take the bullet frame out of the first lines frame // list. - if ((nsnull != mLines) && (mBullet == mLines->mFirstChild)) { + if ((! mLines.empty()) && (mBullet == mLines.front()->mFirstChild)) { nsIFrame* next; mBullet->GetNextSibling(&next); mBullet->SetNextSibling(nsnull); - PRInt32 count = mLines->GetChildCount() - 1; + PRInt32 count = mLines.front()->GetChildCount() - 1; NS_ASSERTION(count >= 0, "empty line w/o bullet"); - mLines->SetChildCount(count); + mLines.front()->SetChildCount(count); if (0 == count) { - nsLineBox* nextLine = mLines->mNext; - aState.FreeLineBox(mLines); - mLines = nextLine; - if (nsnull != nextLine) { - nextLine->MarkDirty(); + nsLineBox* oldFront = mLines.front(); + mLines.pop_front(); + aState.FreeLineBox(oldFront); + if (! mLines.empty()) { + mLines.front()->MarkDirty(); } } else { - mLines->mFirstChild = next; - mLines->MarkDirty(); + mLines.front()->mFirstChild = next; + mLines.front()->MarkDirty(); } } } @@ -1632,10 +1663,11 @@ nsBlockFrame::PrepareStyleChangedReflow(nsBlockReflowState& aState) nsresult rv = UpdateBulletPosition(aState); // Mark everything dirty - nsLineBox* line = mLines; - while (nsnull != line) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { line->MarkDirty(); - line = line->mNext; } return rv; } @@ -1684,7 +1716,6 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) } #endif - nsLineBox* line = mLines; if (tryAndSkipLines) { nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left; @@ -1714,8 +1745,10 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) #endif PRBool wrapping = !aState.GetFlag(BRS_NOWRAP); - while (nsnull != line) { - + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { // We let child blocks make their own decisions the same // way we are here. // @@ -1724,7 +1757,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) if (line->IsBlock() || line->HasPercentageChild() || (wrapping && - ((line->mNext && !line->HasBreak()) || + ((line != mLines.back() && !line->HasBreak()) || line->ResizeReflowOptimizationDisabled() || line->HasFloaters() || line->IsImpactedByFloater() || @@ -1742,7 +1775,8 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) if (gNoisyReflow && !line->IsDirty() && wrapping) { IndentBy(stdout, gNoiseIndent + 1); printf("skipped: line=%p next=%p %s %s %s%s%s breakType=%d xmost=%d\n", - line, line->mNext, + NS_STATIC_CAST(void*, line.get()), + NS_STATIC_CAST(void*, line.next().get()), line->IsBlock() ? "block" : "inline", "wrapping", line->HasBreak() ? "has-break " : "", @@ -1752,15 +1786,15 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) line->mBounds.XMost()); } #endif - - line = line->mNext; } } else { // Mark everything dirty - while (nsnull != line) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { line->MarkDirty(); - line = line->mNext; } } return NS_OK; @@ -1768,22 +1802,23 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) //---------------------------------------- -nsLineBox* +PRBool nsBlockFrame::FindLineFor(nsIFrame* aFrame, - nsLineBox** aPrevLineResult, - PRBool* aIsFloaterResult) + PRBool* aIsFloaterResult, + line_iterator* aFoundLine) { - // This assertion actually fires on lots of pages - // (e.g., bugzilla, bugzilla query page), so limit it - // to a few people until we fix the problem causing it. + // This assertion actually fires on lots of pages (e.g., bugzilla, + // bugzilla query page), so limit it to a few people until we fix the + // problem causing it. It's related to the similarly |#ifdef|ed + // assertion in |PrepareChildIncrementalReflow|. #if defined(DEBUG_dbaron) || defined(DEBUG_waterson) NS_PRECONDITION(aFrame, "why pass a null frame?"); #endif - nsLineBox* prevLine = nsnull; - nsLineBox* line = mLines; PRBool isFloater = PR_FALSE; - while (nsnull != line) { + line_iterator line = begin_lines(), + line_end = end_lines(); + for ( ; line != line_end; ++line) { if (line->Contains(aFrame)) { break; } @@ -1798,14 +1833,12 @@ nsBlockFrame::FindLineFor(nsIFrame* aFrame, fc = fc->Next(); } } - prevLine = line; - line = line->mNext; } done: *aIsFloaterResult = isFloater; - *aPrevLineResult = prevLine; - return line; + *aFoundLine = line; + return (line != line_end); } // SEC: added GetCurrentLine() for bug 45152 @@ -1819,118 +1852,117 @@ nsBlockFrame::GetCurrentLine(nsBlockReflowState *aState, nsLineBox ** aOutCurren return NS_OK; } +/** + * Remember regions of reflow damage from floaters that changed size (so + * far, only vertically, which is a bug) in the space manager so that we + * can mark any impacted lines dirty in |PropagateFloaterDamage|. + */ void -nsBlockFrame::RecoverStateFrom(nsBlockReflowState& aState, - nsLineBox* aLine, - nsRect* aDamageRect) +nsBlockFrame::RememberFloaterDamage(nsBlockReflowState& aState, + nsLineBox* aLine, + const nsRect& aOldCombinedArea) { - PRBool applyTopMargin = PR_FALSE; - if (aLine->IsBlock()) { - nsIFrame* framePrevInFlow; - aLine->mFirstChild->GetPrevInFlow(&framePrevInFlow); - if (nsnull == framePrevInFlow) { - applyTopMargin = ShouldApplyTopMargin(aState, aLine); - } - } + nsRect lineCombinedArea; + aLine->GetCombinedArea(&lineCombinedArea); + if (lineCombinedArea != aLine->mBounds && + lineCombinedArea != aOldCombinedArea) { + // The line's combined-area changed. Therefore we need to damage + // the lines below that were previously (or are now) impacted by + // the change. It's possible that a floater shrunk or grew so + // use the larger of the impacted area. - aState.RecoverStateFrom(aLine, applyTopMargin, aDamageRect); + // XXXldb If just the width of the floater changed, then this code + // won't be triggered and the code below (in |PropagateFloaterDamage|) + // won't kick in for "non-Block lines". See + // "XXX: Maybe the floater itself changed size?" below. + // + // This is a major flaw in this code. + // + nscoord newYMost = lineCombinedArea.YMost(); + nscoord oldYMost = aOldCombinedArea.YMost(); + nscoord impactYB = newYMost < oldYMost ? oldYMost : newYMost; + nscoord impactYA = lineCombinedArea.y; + nsISpaceManager *spaceManager = aState.mReflowState.mSpaceManager; + spaceManager->IncludeInDamage(impactYA, impactYB); + } } /** - * Propagate reflow "damage" from the just reflowed line (aLine) to - * any subsequent lines that were affected. The only thing that causes - * damage is a change to the impact that floaters make. + * Propagate reflow "damage" from from earlier lines to the current + * line. The reflow damage comes from the following sources: + * 1. The regions of floater damage remembered in + * |RememberFloaterDamage|. + * 2. The combination of nonzero |aDeltaY| and any impact by a floater, + * either the previous reflow or now. * - * XXX_perf: This is O(N^2) because we loop over all following lines - * for each line that is damaged. See bug 61962. + * When entering this function, |aLine| is still at its old position and + * |aDeltaY| indicates how much it will later be slid (assuming it + * doesn't get marked dirty and reflowed entirely). */ void -nsBlockFrame::PropagateReflowDamage(nsBlockReflowState& aState, - nsLineBox* aLine, - const nsRect& aOldCombinedArea, - nscoord aDeltaY) +nsBlockFrame::PropagateFloaterDamage(nsBlockReflowState& aState, + nsLineBox* aLine, + nscoord aDeltaY) { - // See if the line has a relevant combined area, and if it does if - // the combined area has changed. - nsRect lineCombinedArea; - aLine->GetCombinedArea(&lineCombinedArea); - if (lineCombinedArea != aLine->mBounds) { - if (lineCombinedArea != aOldCombinedArea) { - // The line's combined-area changed. Therefore we need to damage - // the lines below that were previously (or are now) impacted by - // the change. It's possible that a floater shrunk or grew so - // use the larger of the impacted area. - nscoord newYMost = lineCombinedArea.YMost(); - nscoord oldYMost = aOldCombinedArea.YMost(); - nscoord impactYB = newYMost < oldYMost ? oldYMost : newYMost; - nscoord impactYA = lineCombinedArea.y; + NS_PRECONDITION(!aLine->IsDirty(), "should never be called on dirty lines"); - // Loop over each subsequent line and mark them dirty if they - // intersect the impacted area. Note: we cannot stop after the - // first non-intersecting line because lines might be - // overlapping because of negative margins. - nsLineBox* next = aLine->mNext; - while (nsnull != next) { - nscoord lineYA = next->mBounds.y + aDeltaY; - nscoord lineYB = lineYA + next->mBounds.height; - if ((lineYB >= impactYA) && (lineYA < impactYB)) { - next->MarkDirty(); - } - next = next->mNext; - } - } - else { - // The line's combined area didn't change from last - // time. Therefore just sliding subsequent lines will work. + // Check the damage region recorded in the float damage. + nsISpaceManager *spaceManager = aState.mReflowState.mSpaceManager; + if (spaceManager->HasFloatDamage()) { + nscoord lineYA = aLine->mBounds.y + aDeltaY; + nscoord lineYB = lineYA + aLine->mBounds.height; + if (spaceManager->IntersectsDamage(lineYA, lineYB)) { + aLine->MarkDirty(); return; } } if (aDeltaY) { - nsLineBox* next = aLine->mNext; - while (nsnull != next) { - if (!next->IsDirty()) { - // Cases we need to find: - // - // 1. the line was impacted by a floater and now isn't - // 2. the line wasn't impacted by a floater and now is - // - //XXXPerf: An optimization: if the line was and is completely - //impacted by a floater and the floater hasn't changed size, - //then we don't need to mark the line dirty. - aState.GetAvailableSpace(next->mBounds.y + aDeltaY); - PRBool wasImpactedByFloater = next->IsImpactedByFloater(); - PRBool isImpactedByFloater = aState.IsImpactedByFloater() ? PR_TRUE : PR_FALSE; + // Cases we need to find: + // + // 1. the line was impacted by a floater and now isn't + // 2. the line wasn't impacted by a floater and now is + // 3. the line is impacted by a floater both before and after and + // the floater has changed position relative to the line (or it's + // a different floater). (XXXPerf we don't currently + // check whether the floater changed size. We currently just + // mark blocks dirty and ignore any possibility of damage to + // inlines by it being a different floater with a different + // size.) + // + // XXXPerf: An optimization: if the line was and is completely + // impacted by a floater and the floater hasn't changed size, + // then we don't need to mark the line dirty. + aState.GetAvailableSpace(aLine->mBounds.y + aDeltaY); + PRBool wasImpactedByFloater = aLine->IsImpactedByFloater(); + PRBool isImpactedByFloater = aState.IsImpactedByFloater(); #ifdef REALLY_NOISY_REFLOW - printf("nsBlockFrame::PropagateReflowDamage %p was = %d, is=%d\n", - this, wasImpactedByFloater, isImpactedByFloater); + printf("nsBlockFrame::PropagateFloaterDamage %p was = %d, is=%d\n", + this, wasImpactedByFloater, isImpactedByFloater); #endif - if (wasImpactedByFloater != isImpactedByFloater) { - next->MarkDirty(); - } - else if (isImpactedByFloater) { - //XXX: Maybe the floater itself changed size? - if (next->IsBlock()) { - //XXXPerf - // Case: - // It's possible that more/less of the line is impacted by - // the floater than last time. So reflow. - next->MarkDirty(); - } - } - } - next = next->mNext; + // Mark the line dirty if: + // 1. It used to be impacted by a floater and now isn't, or vice + // versa. + // 2. It is impacted by a floater and it is a block, which means + // that more or less of the line could be impacted than was in + // the past. (XXXPerf This could be optimized further, since + // we're marking the whole line dirty.) + if ((wasImpactedByFloater != isImpactedByFloater) || + (isImpactedByFloater && aLine->IsBlock())) { + aLine->MarkDirty(); } } } +// NOTE: The first parameter *must* be passed by value. static PRBool -WrappedLinesAreDirty(nsLineBox* aLine) +WrappedLinesAreDirty(nsLineList::iterator aLine, + const nsLineList::iterator aLineEnd) { if (aLine->IsInline()) { while (aLine->IsLineWrapped()) { - aLine = aLine->mNext; - if (!aLine) { + ++aLine; + if (aLine == aLineEnd) { break; } @@ -1972,6 +2004,8 @@ PRBool nsBlockFrame::IsIncrementalDamageConstrained(const nsBlockReflowState& aS return PR_FALSE; // default case, damage is not constrained (or unknown) } +static void PlaceFrameView(nsIPresContext* aPresContext, nsIFrame* aFrame); + /** * Reflow the dirty lines */ @@ -1980,6 +2014,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) { nsresult rv = NS_OK; PRBool keepGoing = PR_TRUE; + PRBool repositionViews = PR_FALSE; // should we really need this? #ifdef DEBUG if (gNoisyReflow) { @@ -2008,24 +2043,30 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) aState.mReflowState.reason == eReflowReason_Dirty; + // the amount by which we will slide the current line if it is not + // dirty nscoord deltaY = 0; - // Reflow the lines that are already ours - aState.mPrevLine = nsnull; - nsLineBox* line = mLines; + // whether we did NOT reflow the previous line and thus we need to + // recompute the carried out margin before the line if we want to + // reflow it or if its previous margin is dirty + PRBool needToRecoverMargin = PR_FALSE; - while (line) { + // Reflow the lines that are already ours + line_iterator line = begin_lines(), line_end = end_lines(); + for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) { #ifdef DEBUG if (gNoisyReflow) { nsRect lca; line->GetCombinedArea(&lca); IndentBy(stdout, gNoiseIndent); printf("line=%p mY=%d dirty=%s oldBounds={%d,%d,%d,%d} oldCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d\n", - line, aState.mY, line->IsDirty() ? "yes" : "no", + NS_STATIC_CAST(void*, line.get()), aState.mY, + line->IsDirty() ? "yes" : "no", line->mBounds.x, line->mBounds.y, line->mBounds.width, line->mBounds.height, lca.x, lca.y, lca.width, lca.height, - deltaY, aState.mPrevBottomMargin); + deltaY, aState.mPrevBottomMargin.get()); gNoiseIndent++; } #endif @@ -2033,11 +2074,55 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // If we're supposed to update our maximum width, then we'll also need to // reflow this line if it's line wrapped and any of the continuing lines // are dirty - if (line->IsDirty() || (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && ::WrappedLinesAreDirty(line))) { + if (!line->IsDirty() && + (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && + ::WrappedLinesAreDirty(line, line_end))) { + line->MarkDirty(); + } + + // Make sure |aState.mPrevBottomMargin| is at the correct position + // before calling PropagateFloaterDamage. + if (needToRecoverMargin) { + needToRecoverMargin = PR_FALSE; + // We need to reconstruct the bottom margin only if we didn't + // reflow the previous line and we do need to reflow (or repair + // the top position of) the next line. + if (line->IsDirty() || line->IsPreviousMarginDirty()) + aState.ReconstructMarginAbove(line); + } + + if (line->IsPreviousMarginDirty() && !line->IsDirty()) { + // If the previous margin is dirty and we're not going to reflow + // the line we need to pull out the correct top margin and set + // |deltaY| correctly. + // If there's float damage we might end up doing this work twice, + // but whatever... + if (line->IsBlock()) { + // XXXPerf We could actually make this faster by stealing code + // from the top of nsBlockFrame::ReflowBlockFrame, but it's an + // edge case that will generally happen at most once in a given + // reflow (insertion of a new line before a block) + line->MarkDirty(); + } else { + deltaY = aState.mY + aState.mPrevBottomMargin.get() - line->mBounds.y; + } + } + line->ClearPreviousMarginDirty(); + + // See if there's any reflow damage that requires that we mark the + // line dirty. + if (!line->IsDirty()) { + PropagateFloaterDamage(aState, line, deltaY); + } + + // Now repair the line and update |aState.mY| by calling + // |ReflowLine| or |SlideLine|. + if (line->IsDirty()) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. - nscoord oldHeight = line->mBounds.height; + nscoord oldY = line->mBounds.y; + nscoord oldYMost = line->mBounds.YMost(); nsRect oldCombinedArea; line->GetCombinedArea(&oldCombinedArea); @@ -2053,35 +2138,40 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) } if (!keepGoing) { if (0 == line->GetChildCount()) { - DeleteLine(aState, line); + DeleteLine(aState, line, line_end); } break; } - nscoord newHeight = line->mBounds.height; - deltaY += newHeight - oldHeight; + if (oldY == 0 && deltaY != line->mBounds.y) { + // This means the current line was just reflowed for the first + // time. Thus we must mark the the previous margin of the next + // line dirty. + // XXXldb Move this into where we insert the line! (or will + // that mess up deltaY manipulation?) + if (line.next() != end_lines()) { + line.next()->MarkPreviousMarginDirty(); + // since it's marked dirty, nobody will care about |deltaY| + } + } else { + deltaY = line->mBounds.YMost() - oldYMost; + } - // If the next line is clean then check and see if reflowing the - // current line "damaged" the next line. Damage occurs when the - // current line contains floaters that intrude upon the - // subsequent lines. - nsLineBox* next = line->mNext; - if (next && !next->IsDirty()) { - PropagateReflowDamage(aState, line, oldCombinedArea, deltaY); - } - } - else { - // XXX what if the slid line doesn't fit because we are in a - // vertically constrained situation? - // Recover state as if we reflowed this line - nsRect damageRect; - RecoverStateFrom(aState, line, incrementalReflow ? &damageRect : 0); - if (incrementalReflow && !damageRect.IsEmpty()) { -#ifdef NOISY_BLOCK_INVALIDATE - printf("%p invalidate 5 (%d, %d, %d, %d)\n", - this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); -#endif - Invalidate(aState.mPresContext, damageRect); - } + // Remember any things that could potentially be changes in the + // positions of floaters in this line so that we later damage the + // lines adjacent to those changes. + RememberFloaterDamage(aState, line, oldCombinedArea); + } else { + if (deltaY != 0) + SlideLine(aState, line, deltaY); + else + repositionViews = PR_TRUE; + + // XXX EVIL O(N^2) EVIL + aState.RecoverStateFrom(line, deltaY); + + // Keep mY up to date in case we're propagating reflow damage. + aState.mY = line->mBounds.YMost(); + needToRecoverMargin = PR_TRUE; } #ifdef DEBUG @@ -2091,45 +2181,50 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) line->GetCombinedArea(&lca); IndentBy(stdout, gNoiseIndent); printf("line=%p mY=%d newBounds={%d,%d,%d,%d} newCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d\n", - line, aState.mY, + NS_STATIC_CAST(void*, line.get()), aState.mY, line->mBounds.x, line->mBounds.y, line->mBounds.width, line->mBounds.height, lca.x, lca.y, lca.width, lca.height, - deltaY, aState.mPrevBottomMargin); + deltaY, aState.mPrevBottomMargin.get()); } #endif - - // If this is an inline frame then its time to stop - aState.mPrevLine = line; - line = line->mNext; - aState.AdvanceToNextLine(); } + if (needToRecoverMargin) { + // Is this expensive? + aState.ReconstructMarginAbove(line); + } + + // Should we really have to do this? + if (repositionViews) + ::PlaceFrameView(aState.mPresContext, this); + // Pull data from a next-in-flow if there's still room for more // content here. while (keepGoing && (nsnull != aState.mNextInFlow)) { // Grab first line from our next-in-flow - line = aState.mNextInFlow->mLines; - if (nsnull == line) { + nsBlockFrame* nextInFlow = aState.mNextInFlow; + line_iterator nifLine = nextInFlow->begin_lines(); + if (nifLine == nextInFlow->end_lines()) { aState.mNextInFlow = (nsBlockFrame*) aState.mNextInFlow->mNextInFlow; continue; } // XXX See if the line is not dirty; if it's not maybe we can // avoid the pullup if it can't fit? - aState.mNextInFlow->mLines = line->mNext; - line->mNext = nsnull; - if (0 == line->GetChildCount()) { + nsLineBox *toMove = nifLine; + nextInFlow->mLines.erase(nifLine); + if (0 == toMove->GetChildCount()) { // The line is empty. Try the next one. - NS_ASSERTION(nsnull == line->mFirstChild, "bad empty line"); - aState.FreeLineBox(line); + NS_ASSERTION(nsnull == toMove->mFirstChild, "bad empty line"); + aState.FreeLineBox(toMove); continue; } // XXX move to a subroutine: run-in, overflow, pullframe and this do this // Make the children in the line ours. - nsIFrame* frame = line->mFirstChild; + nsIFrame* frame = toMove->mFirstChild; nsIFrame* lastFrame = nsnull; - PRInt32 n = line->GetChildCount(); + PRInt32 n = toMove->GetChildCount(); while (--n >= 0) { frame->SetParent(this); // When pushing and pulling frames we need to check for whether any @@ -2141,15 +2236,9 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) lastFrame->SetNextSibling(nsnull); // Add line to our line list - if (nsnull == aState.mPrevLine) { - NS_ASSERTION(nsnull == mLines, "bad aState.mPrevLine"); - mLines = line; - } - else { - NS_ASSERTION(nsnull == aState.mPrevLine->mNext, "bad aState.mPrevLine"); - aState.mPrevLine->mNext = line; - aState.mPrevChild->SetNextSibling(line->mFirstChild); - } + if (aState.mPrevChild) + aState.mPrevChild->SetNextSibling(toMove->mFirstChild); + line = mLines.before_insert(end_lines(), toMove); // If line contains floaters, remove them from aState.mNextInFlow's // floater list. They will be pushed onto this blockframe's floater @@ -2175,27 +2264,27 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // (we have to loop here because reflowing the line may case a new // line to be created; see SplitLine's callers for examples of // when this happens). - while (nsnull != line) { - rv = ReflowLine(aState, line, &keepGoing, incrementalReflow /* force invalidate */); + while (line != end_lines()) { + rv = ReflowLine(aState, line, &keepGoing, + incrementalReflow /* force invalidate */); if (NS_FAILED(rv)) { return rv; } if (!keepGoing) { if (0 == line->GetChildCount()) { - DeleteLine(aState, line); + DeleteLine(aState, line, line_end); } break; } // If this is an inline frame then its time to stop - aState.mPrevLine = line; - line = line->mNext; + ++line; aState.AdvanceToNextLine(); } } // Handle an odd-ball case: a list-item with no lines - if (mBullet && HaveOutsideBullet() && !mLines) { + if (mBullet && HaveOutsideBullet() && mLines.empty()) { nsHTMLReflowMetrics metrics(nsnull); ReflowBullet(aState, metrics); @@ -2219,19 +2308,21 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) void nsBlockFrame::DeleteLine(nsBlockReflowState& aState, - nsLineBox* aLine) + nsLineList::iterator aLine, + nsLineList::iterator aLineEnd) { NS_PRECONDITION(0 == aLine->GetChildCount(), "can't delete !empty line"); if (0 == aLine->GetChildCount()) { - if (nsnull == aState.mPrevLine) { - NS_ASSERTION(aLine == mLines, "huh"); - mLines = nsnull; - } - else { - NS_ASSERTION(aState.mPrevLine->mNext == aLine, "bad prev-line"); - aState.mPrevLine->mNext = aLine->mNext; - } - aState.FreeLineBox(aLine); + NS_ASSERTION(aState.mCurrentLine == aLine, + "using function more generally than designed, " + "but perhaps OK now"); + nsLineBox *line = aLine; + aLine = mLines.erase(aLine); + aState.FreeLineBox(line); + // Mark the previous margin of the next line dirty since we need to + // recompute its top position. + if (aLine != aLineEnd) + aLine->MarkPreviousMarginDirty(); } } @@ -2242,7 +2333,7 @@ nsBlockFrame::DeleteLine(nsBlockReflowState& aState, */ nsresult nsBlockFrame::ReflowLine(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRBool aDamageDirtyArea) { @@ -2333,10 +2424,11 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // XXX This approach doesn't work when floaters are involved in which case // we'll either need to recover the floater state that applies to the // unconstrained reflow or keep it around in a separate space manager... - PRBool isBeginningLine = !aState.mPrevLine || !aState.mPrevLine->IsLineWrapped(); + PRBool isBeginningLine = aState.mCurrentLine == begin_lines() || + !aState.mCurrentLine.prev()->IsLineWrapped(); if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) { nscoord oldY = aState.mY; - nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; + nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin); PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); // First reflow the line with an unconstrained width. When doing this @@ -2429,7 +2521,7 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, */ nsresult nsBlockFrame::PullFrame(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool aDamageDeletedLines, nsIFrame*& aFrameResult) { @@ -2438,8 +2530,8 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, aFrameResult = nsnull; // First check our remaining lines - while (nsnull != aLine->mNext) { - rv = PullFrameFrom(aState, aLine, &aLine->mNext, PR_FALSE, + while (end_lines() != aLine.next()) { + rv = PullFrameFrom(aState, aLine, mLines, aLine.next(), PR_FALSE, aDamageDeletedLines, aFrameResult, stopPulling); if (NS_FAILED(rv) || stopPulling) { return rv; @@ -2449,13 +2541,13 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, // Pull frames from the next-in-flow(s) until we can't nsBlockFrame* nextInFlow = aState.mNextInFlow; while (nsnull != nextInFlow) { - nsLineBox* line = nextInFlow->mLines; - if (nsnull == line) { + if (nextInFlow->mLines.empty()) { nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow; aState.mNextInFlow = nextInFlow; continue; } - rv = PullFrameFrom(aState, aLine, &nextInFlow->mLines, PR_TRUE, + rv = PullFrameFrom(aState, aLine, nextInFlow->mLines, + nextInFlow->mLines.begin(), PR_TRUE, aDamageDeletedLines, aFrameResult, stopPulling); if (NS_FAILED(rv) || stopPulling) { return rv; @@ -2465,11 +2557,10 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, } /** - * Try to pull a frame out of a line pointed at by aFromList. If a - * frame is pulled then aPulled will be set to PR_TRUE. In addition, - * if aUpdateGeometricParent is set then the pulled frames geometric - * parent will be updated (e.g. when pulling from a next-in-flows line - * list). + * Try to pull a frame out of a line pointed at by aFromLine. If a frame + * is pulled then aPulled will be set to PR_TRUE. In addition, if + * aUpdateGeometricParent is set then the pulled frames geometric parent + * will be updated (e.g. when pulling from a next-in-flows line list). * * Note: pulling a frame from a line that is a place-holder frame * doesn't automatically remove the corresponding floater from the @@ -2481,13 +2572,14 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, nsresult nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState, nsLineBox* aLine, - nsLineBox** aFromList, + nsLineList& aFromContainer, + nsLineList::iterator aFromLine, PRBool aUpdateGeometricParent, PRBool aDamageDeletedLines, nsIFrame*& aFrameResult, PRBool& aStopPulling) { - nsLineBox* fromLine = *aFromList; + nsLineBox* fromLine = aFromLine; NS_ABORT_IF_FALSE(fromLine, "bad line to pull from"); NS_ABORT_IF_FALSE(fromLine->GetChildCount(), "empty line"); NS_ABORT_IF_FALSE(aLine->GetChildCount(), "empty line"); @@ -2517,7 +2609,9 @@ nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState, if (aDamageDeletedLines) { Invalidate(aState.mPresContext, fromLine->mBounds); } - *aFromList = fromLine->mNext; + if (aFromLine.next() != end_lines()) + aFromLine.next()->MarkPreviousMarginDirty(); + aFromContainer.erase(aFromLine); aState.FreeLineBox(fromLine); } @@ -2563,15 +2657,19 @@ PlaceFrameView(nsIPresContext* aPresContext, nsContainerFrame::PositionChildViews(aPresContext, aFrame); } -// XXXldb If I understand correctly what's going on here, this should never -// be called with nonzero |aDY| on a line for which |IsBlock| is false. -// XXX_perf Shouldn't we optimize for |aDY == 0|? void nsBlockFrame::SlideLine(nsBlockReflowState& aState, nsLineBox* aLine, nscoord aDY) { + NS_PRECONDITION(aDY != 0, "why slide a line nowhere?"); + + PRBool doInvalidate = !aLine->mBounds.IsEmpty(); + if (doInvalidate) + Invalidate(aState.mPresContext, aLine->mBounds); // Adjust line state aLine->SlideBy(aDY); + if (doInvalidate) + Invalidate(aState.mPresContext, aLine->mBounds); // Adjust the frames in the line nsIFrame* kid = aLine->mFirstChild; @@ -2634,8 +2732,9 @@ nsresult nsBlockFrame::UpdateSpaceManager(nsIPresContext* aPresContext, nsISpaceManager* aSpaceManager) { - nsLineBox* line = mLines; - while (nsnull != line) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { // Place the floaters in the spacemanager if (line->HasFloaters()) { nsFloaterCache* fc = line->GetFirstFloater(); @@ -2663,7 +2762,7 @@ nsBlockFrame::UpdateSpaceManager(nsIPresContext* aPresContext, // the reflow began). nsBlockFrame* bf; nsresult rv = line->mFirstChild->QueryInterface(kBlockFrameCID, - (void**) &bf); + NS_REINTERPRET_CAST(void**, &bf)); if (NS_SUCCEEDED(rv)) { nsPoint origin; bf->GetOrigin(origin); @@ -2676,8 +2775,6 @@ nsBlockFrame::UpdateSpaceManager(nsIPresContext* aPresContext, aSpaceManager->Translate(-origin.x, -origin.y); } } - - line = line->mNext; } return NS_OK; @@ -2725,7 +2822,7 @@ nsBlockFrame::AttributeChanged(nsIPresContext* aPresContext, // make the assumption that all related list items share a // common block parent. // XXXldb I think that's a bad assumption. - while (nextAncestor != nsnull) { + while (nextAncestor) { if (NS_OK == nextAncestor->QueryInterface(kBlockFrameCID, (void**)&blockParent)) { break; @@ -2758,6 +2855,77 @@ nsBlockFrame::AttributeChanged(nsIPresContext* aPresContext, return rv; } +inline PRBool +IsBorderZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) +{ + return ((aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0)); +} + +inline PRBool +IsPaddingZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) +{ + return (aUnit == eStyleUnit_Null || + (aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) || + (aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0)); +} + +inline PRBool +IsMarginZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) +{ + return (aUnit == eStyleUnit_Null || + aUnit == eStyleUnit_Auto || + (aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) || + (aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0)); +} + +NS_IMETHODIMP +nsBlockFrame::IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool *aResult) +{ + // XXXldb In hindsight, I'm not sure why I made this check the margin, + // but it seems to work right and I'm a little hesitant to change it. + const nsStyleMargin* margin = NS_STATIC_CAST(const nsStyleMargin*, + mStyleContext->GetStyleData(eStyleStruct_Margin)); + const nsStyleBorder* border = NS_STATIC_CAST(const nsStyleBorder*, + mStyleContext->GetStyleData(eStyleStruct_Border)); + const nsStylePadding* padding = NS_STATIC_CAST(const nsStylePadding*, + mStyleContext->GetStyleData(eStyleStruct_Padding)); + nsStyleCoord coord; + if ((border->IsBorderSideVisible(NS_SIDE_TOP) && + !IsBorderZero(border->mBorder.GetTopUnit(), + border->mBorder.GetTop(coord))) || + (border->IsBorderSideVisible(NS_SIDE_BOTTOM) && + !IsBorderZero(border->mBorder.GetTopUnit(), + border->mBorder.GetTop(coord))) || + !IsPaddingZero(padding->mPadding.GetTopUnit(), + padding->mPadding.GetTop(coord)) || + !IsPaddingZero(padding->mPadding.GetBottomUnit(), + padding->mPadding.GetBottom(coord)) || + !IsMarginZero(margin->mMargin.GetTopUnit(), + margin->mMargin.GetTop(coord)) || + !IsMarginZero(margin->mMargin.GetBottomUnit(), + margin->mMargin.GetBottom(coord))) { + *aResult = PR_FALSE; + return NS_OK; + } + + + const nsStyleText* styleText = NS_STATIC_CAST(const nsStyleText*, + mStyleContext->GetStyleData(eStyleStruct_Text)); + PRBool isPre = + ((NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) || + (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace)); + *aResult = PR_TRUE; + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { + line->IsEmpty(aIsQuirkMode, isPre, aResult); + if (! *aResult) + break; + } + return NS_OK; +} + PRBool nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, nsLineBox* aLine) @@ -2776,8 +2944,9 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, } // Determine if this line is "essentially" the first line - nsLineBox* line = mLines; - while (line != aLine) { + for (line_iterator line = begin_lines(); + line != aLine; + ++line) { if (line->IsBlock()) { // A line which preceeds aLine contains a block; therefore the // top margin applies. @@ -2788,7 +2957,6 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, } // No need to apply the top margin if the line has floaters. We // should collapse anyway (bug 44419) - line = line->mNext; } // The line being reflowed is "essentially" the first line in the @@ -2800,17 +2968,18 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, nsIFrame* nsBlockFrame::GetTopBlockChild() { - nsIFrame* firstChild = mLines ? mLines->mFirstChild : nsnull; + nsIFrame* firstChild = mLines.empty() ? nsnull : mLines.front()->mFirstChild; if (firstChild) { - if (mLines->IsBlock()) { + if (mLines.front()->IsBlock()) { // Winner return firstChild; } // If the first line is not a block line then the second line must // be a block line otherwise the top child can't be a block. - nsLineBox* next = mLines->mNext; - if ((nsnull == next) || !next->IsBlock()) { + line_iterator next = begin_lines(); + ++next; + if ((next == end_lines()) || !next->IsBlock()) { // There is no line after the first line or its not a block so // don't bother trying to skip over the first line. return nsnull; @@ -2830,7 +2999,7 @@ nsBlockFrame::GetTopBlockChild() // See if each frame is a text frame that contains nothing but // whitespace. - PRInt32 n = mLines->GetChildCount(); + PRInt32 n = mLines.front()->GetChildCount(); while (--n >= 0) { nsIContent* content; nsresult rv = firstChild->GetContent(&content); @@ -2868,7 +3037,7 @@ nsBlockFrame::GetTopBlockChild() nsresult nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing) { NS_PRECONDITION(*aKeepReflowGoing, "bad caller"); @@ -2931,14 +3100,13 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState, frame, availSpace); - // Now compute the collapsed margin-top value - topMargin = - nsBlockReflowContext::ComputeCollapsedTopMargin(aState.mPresContext, - reflowState); - - // And collapse it with the previous bottom margin to get the final value - topMargin = - nsBlockReflowContext::MaxMargin(topMargin, aState.mPrevBottomMargin); + // Now compute the collapsed margin-top value into aState.mPrevBottomMargin + nsCollapsingMargin oldPrevBottomMargin = aState.mPrevBottomMargin; + nsBlockReflowContext::ComputeCollapsedTopMargin(aState.mPresContext, + reflowState, + aState.mPrevBottomMargin); + topMargin = aState.mPrevBottomMargin.get(); + aState.mPrevBottomMargin = oldPrevBottomMargin; // perhaps not needed // Temporarily advance the running Y value so that the // GetAvailableSpace method will return the right available @@ -2996,7 +3164,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) { // None of the child block fits. - PushLines(aState); + PushLines(aState, aLine.prev()); *aKeepReflowGoing = PR_FALSE; aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; } @@ -3005,11 +3173,12 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Try to place the child block PRBool isAdjacentWithTop = aState.IsAdjacentWithTop(); - nscoord collapsedBottomMargin; + nsCollapsingMargin collapsedBottomMargin; nsRect combinedArea(0,0,0,0); *aKeepReflowGoing = brc.PlaceBlock(isAdjacentWithTop, computedOffsets, - &collapsedBottomMargin, + collapsedBottomMargin, aLine->mBounds, combinedArea); + aLine->SetCarriedOutBottomMargin(collapsedBottomMargin); if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line as dirty so once we known the final shrink wrap width @@ -3033,7 +3202,6 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Advance to new Y position nscoord newY = aLine->mBounds.YMost(); aState.mY = newY; - aLine->SetCarriedOutBottomMargin(brc.GetCarriedOutBottomMargin()); // Continue the block frame now if it didn't completely fit in // the available space. @@ -3052,8 +3220,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, if (nsnull == line) { return NS_ERROR_OUT_OF_MEMORY; } - line->mNext = aLine->mNext; - aLine->mNext = line; + mLines.after_insert(aLine, line); // Do not count the continuation child on the line it used // to be on @@ -3062,8 +3229,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Advance to next line since some of the block fit. That way // only the following lines will be pushed. - aState.mPrevLine = aLine; - PushLines(aState); + PushLines(aState, aLine); aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; *aKeepReflowGoing = PR_FALSE; @@ -3078,7 +3244,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, printf(" prevBottomMargin=%d, setting to zero\n", aState.mPrevBottomMargin); #endif - aState.mPrevBottomMargin = 0; + aState.mPrevBottomMargin.Zero(); } else { #ifdef NOISY_VERTICAL_MARGINS @@ -3086,7 +3252,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, printf(": reflow complete for "); nsFrame::ListTag(stdout, frame); printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n", - aState.mPrevBottomMargin, collapsedBottomMargin); + aState.mPrevBottomMargin, collapsedBottomMargin.get()); #endif aState.mPrevBottomMargin = collapsedBottomMargin; } @@ -3095,7 +3261,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, printf(": frame="); nsFrame::ListTag(stdout, frame); printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n", - aLine->GetCarriedOutBottomMargin(), collapsedBottomMargin, + brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(), aState.mPrevBottomMargin); #endif @@ -3146,8 +3312,9 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // contains a block (example:
  • \n

    ... ). This is where // the second case can happen. if (mBullet && HaveOutsideBullet() && - ((aLine == mLines) || - ((0 == mLines->mBounds.height) && (aLine == mLines->mNext)))) { + ((aLine == mLines.front()) || + ((0 == mLines.front()->mBounds.height) && + (aLine == begin_lines().next())))) { // Reflow the bullet nsHTMLReflowMetrics metrics(nsnull); ReflowBullet(aState, metrics); @@ -3172,7 +3339,9 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Tall bullets won't look particularly nice here... nsRect bbox; mBullet->GetRect(bbox); - nscoord bulletTopMargin = applyTopMargin ? collapsedBottomMargin : 0; + nscoord bulletTopMargin = applyTopMargin + ? collapsedBottomMargin.get() + : 0; bbox.y = aState.BorderPadding().top + ascent - metrics.ascent + bulletTopMargin; mBullet->SetRect(aState.mPresContext, bbox); @@ -3180,7 +3349,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } else { // None of the block fits. Determine the correct reflow status. - if (aLine == mLines) { + if (aLine == mLines.front()) { // If it's our very first line then we need to be pushed to // our parents next-in-flow. Therefore, return break-before // status for our reflow status. @@ -3189,7 +3358,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, else { // Push the line that didn't fit and any lines that follow it // to our next-in-flow. - PushLines(aState); + PushLines(aState, aLine.prev()); aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; } } @@ -3206,7 +3375,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsresult nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRBool aDamageDirtyArea, PRBool aUpdateMaximumWidth) @@ -3249,7 +3418,7 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, nsresult nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRUint8* aLineReflowStatus, PRBool aUpdateMaximumWidth, @@ -3273,7 +3442,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsresult nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRUint8* aLineReflowStatus, PRBool aUpdateMaximumWidth, @@ -3294,7 +3463,7 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsresult nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRUint8* aLineReflowStatus, PRBool aUpdateMaximumWidth, @@ -3307,7 +3476,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, // Setup initial coordinate system for reflowing the inline frames // into. Apply a previous block frame's bottom margin first. - aState.mY += aState.mPrevBottomMargin; + aState.mY += aState.mPrevBottomMargin.get(); aState.GetAvailableSpace(); PRBool impactedByFloaters = aState.IsImpactedByFloater() ? PR_TRUE : PR_FALSE; aLine->SetLineIsImpactedByFloater(impactedByFloaters); @@ -3369,15 +3538,16 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, // It is possible that one or more of next lines are empty // (because of DeleteChildsNextInFlow). If so, delete them now // in case we are finished. - nsLineBox* nextLine = aLine->mNext; - while ((nsnull != nextLine) && (0 == nextLine->GetChildCount())) { + ++aLine; + while ((aLine != end_lines()) && (0 == aLine->GetChildCount())) { // XXX Is this still necessary now that DeleteChildsNextInFlow // uses DoRemoveFrame? - aLine->mNext = nextLine->mNext; - NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line"); - aState.FreeLineBox(nextLine); - nextLine = aLine->mNext; + nsLineBox *toremove = aLine; + aLine = mLines.erase(aLine); + NS_ASSERTION(nsnull == toremove->mFirstChild, "bad empty line"); + aState.FreeLineBox(toremove); } + --aLine; break; } frame->GetNextSibling(&frame); @@ -3452,7 +3622,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nsresult nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, nsIFrame* aFrame, PRUint8* aLineReflowStatus) { @@ -3579,7 +3749,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) { // Mark next line dirty in case SplitLine didn't end up // pushing any frames. - nsLineBox* next = aLine->mNext; + nsLineBox* next = aLine.next(); if ((nsnull != next) && !next->IsBlock()) { next->MarkDirty(); } @@ -3623,7 +3793,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Mark next line dirty in case SplitLine didn't end up // pushing any frames. - nsLineBox* next = aLine->mNext; + nsLineBox* next = aLine.next(); if ((nsnull != next) && !next->IsBlock()) { next->MarkDirty(); } @@ -3664,7 +3834,7 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState, nsresult nsBlockFrame::SplitLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, nsIFrame* aFrame) { NS_ABORT_IF_FALSE(aLine->IsInline(), "illegal SplitLine on block line"); @@ -3675,7 +3845,8 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, #ifdef DEBUG if (gNoisyReflow) { nsFrame::IndentBy(stdout, gNoiseIndent); - printf("split line: from line=%p pushCount=%d aFrame=", aLine, pushCount); + printf("split line: from line=%p pushCount=%d aFrame=", + NS_STATIC_CAST(void*, aLine.get()), pushCount); if (aFrame) { nsFrame::ListTag(stdout, aFrame); } @@ -3698,8 +3869,7 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, if (!newLine) { return NS_ERROR_OUT_OF_MEMORY; } - newLine->mNext = aLine->mNext; - aLine->mNext = newLine; + mLines.after_insert(aLine, newLine); aLine->SetChildCount(aLine->GetChildCount() - pushCount); #ifdef DEBUG if (gReallyNoisyReflow) { @@ -3718,32 +3888,31 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, } PRBool -nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine) +nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, + line_iterator aLine) { - nsLineBox* next = aLine->mNext; - while (nsnull != next) { + while (++aLine != end_lines()) { // There is another line - if (0 != next->GetChildCount()) { + if (0 != aLine->GetChildCount()) { // If the next line is a block line then we must not justify // this line because it means that this line is the last in a // group of inline lines. - return !next->IsBlock(); + return !aLine->IsBlock(); } - // The next line is empty, try the next one - next = next->mNext; } // XXX Not sure about this part // Try our next-in-flows lines to answer the question nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; while (nsnull != nextInFlow) { - nsLineBox* line = nextInFlow->mLines; - while (nsnull != line) { - if (0 != line->GetChildCount()) { + for (line_iterator line = nextInFlow->begin_lines(), + line_end = nextInFlow->end_lines(); + line != line_end; + ++line) + { + if (0 != line->GetChildCount()) return !line->IsBlock(); - } - line = line->mNext; } nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow; } @@ -3755,15 +3924,14 @@ nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine) nsresult nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; // Trim extra white-space from the line before placing the frames - PRBool trimmed = aLineLayout.TrimTrailingWhiteSpace(); - aLine->SetTrimmed(trimmed); + aLineLayout.TrimTrailingWhiteSpace(); // Vertically align the frames on this line. // @@ -3781,8 +3949,8 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // case is happening then the worst that will happen is that the // bullet frame will be reflowed twice. PRBool addedBullet = PR_FALSE; - if (mBullet && HaveOutsideBullet() && (aLine == mLines) && - (!aLineLayout.IsZeroHeight() || !aLine->mNext)) { + if (mBullet && HaveOutsideBullet() && (aLine == mLines.front()) && + (!aLineLayout.IsZeroHeight() || (aLine == mLines.back()))) { nsHTMLReflowMetrics metrics(nsnull); ReflowBullet(aState, metrics); aLineLayout.AddBulletFrame(mBullet, metrics); @@ -3792,7 +3960,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nscoord lineAscent; aLineLayout.VerticalAlignLine(aLine, maxElementSize, lineAscent); // Our ascent is the ascent of our first line - if (aLine == mLines) { + if (aLine == mLines.front()) { mAscent = lineAscent; } @@ -3807,19 +3975,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } } #ifdef DEBUG - { - static nscoord lastHeight = 0; - if (CRAZY_HEIGHT(aLine->mBounds.y)) { - lastHeight = aLine->mBounds.y; - if (abs(aLine->mBounds.y - lastHeight) > CRAZY_H/10) { - nsFrame::ListTag(stdout); - printf(": line=%p y=%d line.bounds.height=%d\n", - aLine, aLine->mBounds.y, aLine->mBounds.height); - } - } - else { - lastHeight = 0; - } + { + static nscoord lastHeight = 0; + if (CRAZY_HEIGHT(aLine->mBounds.y)) { + lastHeight = aLine->mBounds.y; + if (abs(aLine->mBounds.y - lastHeight) > CRAZY_H/10) { + nsFrame::ListTag(stdout); + printf(": line=%p y=%d line.bounds.height=%d\n", + NS_STATIC_CAST(void*, aLine.get()), + aLine->mBounds.y, aLine->mBounds.height); + } + } + else { + lastHeight = 0; + } } #endif @@ -3870,8 +4039,8 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aState.mPresContext->GetBidiUtils(&bidiUtils); if (bidiUtils && bidiUtils->IsSuccessful() ) { - nsIFrame* nextInFlow = (aLine->mNext) - ? aLine->mNext->mFirstChild : nsnull; + nsIFrame* nextInFlow = (aLine.next() != end_lines()) + ? aLine.next()->mFirstChild : nsnull; bidiUtils->ReorderFrames(aState.mPresContext, aState.mReflowState.rendContext, @@ -3900,28 +4069,29 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, if (aLine->mBounds.height > 0) { // This line has some height. Therefore the application of the // previous-bottom-margin should stick. - aState.mPrevBottomMargin = 0; + aState.mPrevBottomMargin.Zero(); newY = aLine->mBounds.YMost(); } else { // Don't let the previous-bottom-margin value affect the newY // coordinate (it was applied in ReflowInlineFrames speculatively) // since the line is empty. - nscoord dy = -aState.mPrevBottomMargin; + nscoord dy = -aState.mPrevBottomMargin.get(); newY = aState.mY + dy; aLine->SlideBy(dy); // keep our ascent in sync - if (mLines == aLine) { + if (mLines.front() == aLine) { mAscent += dy; } } // See if the line fit. If it doesn't we need to push it. Our first // line will always fit. - if ((mLines != aLine) && (newY > aState.mBottomEdge)) { + if ((mLines.front() != aLine) && (newY > aState.mBottomEdge)) { // Push this line and all of it's children and anything else that // follows to our next-in-flow - PushLines(aState); + NS_ASSERTION((aState.mCurrentLine == aLine), "oops"); + PushLines(aState, aLine.prev()); // Stop reflow and whack the reflow status if reflow hasn't // already been stopped. @@ -4137,7 +4307,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, #ifdef DEBUG if (CRAZY_WIDTH(xmost)) { ListTag(stdout); - printf(": line=%p xmost=%d\n", aLine, xmost); + printf(": line=%p xmost=%d\n", NS_STATIC_CAST(void*, aLine), xmost); } #endif if (xmost > aState.mKidXMost) { @@ -4149,36 +4319,48 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, } void -nsBlockFrame::PushLines(nsBlockReflowState& aState) +nsBlockFrame::PushLines(nsBlockReflowState& aState, + nsLineList::iterator aLineBefore) { - NS_ASSERTION(nsnull != aState.mPrevLine, "bad push"); + nsLineList::iterator overBegin(aLineBefore.next()); + NS_ASSERTION(overBegin != begin_lines(), "bad push"); - nsLineBox* lastLine = aState.mPrevLine; - nsLineBox* nextLine = lastLine->mNext; - - if (nextLine) { - lastLine->mNext = nsnull; - SetOverflowLines(aState.mPresContext, nextLine); + if (overBegin != end_lines()) { + // XXXldb use presshell arena! + nsLineList* overflowLines = new nsLineList(); + overflowLines->splice(overflowLines->end(), mLines, overBegin, + end_lines()); + NS_ASSERTION(!overflowLines->empty(), "should not be empty"); + // this takes ownership but it won't delete it immediately so we + // can keep using it. + SetOverflowLines(aState.mPresContext, overflowLines); // Mark all the overflow lines dirty so that they get reflowed when // they are pulled up by our next-in-flow. // XXXldb Can this get called O(N) times making the whole thing O(N^2)? - while (nsnull != nextLine) { - nextLine->MarkDirty(); - nextLine = nextLine->mNext; + for (line_iterator line = overflowLines->begin(), + line_end = overflowLines->end(); + line != line_end; + ++line) + { + line->MarkDirty(); } } // Break frame sibling list - nsIFrame* lastFrame = lastLine->LastChild(); - lastFrame->SetNextSibling(nsnull); + aLineBefore->LastChild()->SetNextSibling(nsnull); #ifdef DEBUG VerifyOverflowSituation(aState.mPresContext); #endif } + +// The overflowLines property is stored as a pointer to a line list, +// which must be deleted. However, the following functions all maintain +// the invariant that the property is never set if the list is empty. + PRBool nsBlockFrame::DrainOverflowLines(nsIPresContext* aPresContext) { @@ -4186,18 +4368,20 @@ nsBlockFrame::DrainOverflowLines(nsIPresContext* aPresContext) VerifyOverflowSituation(aPresContext); #endif PRBool drained = PR_FALSE; - nsLineBox* overflowLines; + nsLineList* overflowLines; // First grab the prev-in-flows overflow lines nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow; if (nsnull != prevBlock) { overflowLines = prevBlock->GetOverflowLines(aPresContext, PR_TRUE); if (nsnull != overflowLines) { + NS_ASSERTION(! overflowLines->empty(), + "overflow lines should never be set and empty"); drained = PR_TRUE; // Make all the frames on the overflow line list mine nsIFrame* lastFrame = nsnull; - nsIFrame* frame = overflowLines->mFirstChild; + nsIFrame* frame = overflowLines->front()->mFirstChild; while (nsnull != frame) { frame->SetParent(this); @@ -4234,42 +4418,40 @@ nsBlockFrame::DrainOverflowLines(nsIPresContext* aPresContext) } // Join the line lists - if (nsnull == mLines) { - mLines = overflowLines; - } - else { + NS_ASSERTION(lastFrame, "overflow list was created with no frames"); + if (! mLines.empty()) { // Join the sibling lists together - lastFrame->SetNextSibling(mLines->mFirstChild); - - // Place overflow lines at the front of our line list - nsLineBox* lastLine = nsLineBox::LastLine(overflowLines); - lastLine->mNext = mLines; - mLines = overflowLines; + lastFrame->SetNextSibling(mLines.front()->mFirstChild); } + // Place overflow lines at the front of our line list + mLines.splice(mLines.begin(), *overflowLines); + NS_ASSERTION(overflowLines->empty(), "splice should empty list"); + delete overflowLines; } } // Now grab our own overflow lines overflowLines = GetOverflowLines(aPresContext, PR_TRUE); if (overflowLines) { + NS_ASSERTION(! overflowLines->empty(), + "overflow lines should never be set and empty"); // This can happen when we reflow and not everything fits and then // we are told to reflow again before a next-in-flow is created // and reflows. - nsLineBox* lastLine = nsLineBox::LastLine(mLines); - if (nsnull == lastLine) { // if we had no lines before the drain operation - mLines = overflowLines; // set our mLines to the overflow - } - else { // otherwise, append the overflow to the mLines list - lastLine->mNext = overflowLines; - nsIFrame* lastFrame = lastLine->LastChild(); - lastFrame->SetNextSibling(overflowLines->mFirstChild); + + if (! mLines.empty()) { + mLines.back()->LastChild()->SetNextSibling( + overflowLines->front()->mFirstChild ); } + // append the overflow to mLines + mLines.splice(mLines.end(), *overflowLines); drained = PR_TRUE; + delete overflowLines; } return drained; } -nsLineBox* +nsLineList* nsBlockFrame::GetOverflowLines(nsIPresContext* aPresContext, PRBool aRemoveProperty) const { @@ -4282,14 +4464,18 @@ nsBlockFrame::GetOverflowLines(nsIPresContext* aPresContext, if (frameManager) { PRUint32 options = 0; - void* value; + nsLineList* value; if (aRemoveProperty) { options |= NS_IFRAME_MGR_REMOVE_PROP; } - frameManager->GetFrameProperty((nsIFrame*)this, nsLayoutAtoms::overflowLinesProperty, - options, &value); - return (nsLineBox*)value; + frameManager->GetFrameProperty(NS_CONST_CAST(nsBlockFrame*, this), + nsLayoutAtoms::overflowLinesProperty, + options, + NS_REINTERPRET_CAST(void**, &value)); + NS_ASSERTION(!value || !value->empty(), + "value should never be stored as empty"); + return value; } } @@ -4304,26 +4490,34 @@ DestroyOverflowLines(nsIPresContext* aPresContext, void* aPropertyValue) { if (aPropertyValue) { - nsLineBox* lines = (nsLineBox*) aPropertyValue; - nsLineBox::DeleteLineList(aPresContext, lines); + nsLineList* lines = NS_STATIC_CAST(nsLineList*, aPropertyValue); + nsLineBox::DeleteLineList(aPresContext, *lines); + delete lines; } } +// This takes ownership of aOverflowLines. +// XXX We should allocate overflowLines from presShell arena! nsresult nsBlockFrame::SetOverflowLines(nsIPresContext* aPresContext, - nsLineBox* aOverflowFrames) + nsLineList* aOverflowLines) { nsCOMPtr presShell; nsresult rv = NS_ERROR_FAILURE; + NS_ASSERTION(aOverflowLines, "null lines"); + NS_ASSERTION(! aOverflowLines->empty(), "empty lines"); + aPresContext->GetShell(getter_AddRefs(presShell)); if (presShell) { nsCOMPtr frameManager; presShell->GetFrameManager(getter_AddRefs(frameManager)); if (frameManager) { - rv = frameManager->SetFrameProperty(this, nsLayoutAtoms::overflowLinesProperty, - aOverflowFrames, DestroyOverflowLines); + rv = frameManager->SetFrameProperty(this, + nsLayoutAtoms::overflowLinesProperty, + aOverflowLines, + DestroyOverflowLines); // Verify that we didn't overwrite an existing overflow list NS_ASSERTION(rv != NS_IFRAME_MGR_PROP_OVERWRITTEN, "existing overflow list"); @@ -4338,9 +4532,8 @@ nsBlockFrame::SetOverflowLines(nsIPresContext* aPresContext, nsIFrame* nsBlockFrame::LastChild() { - if (mLines) { - nsLineBox* line = nsLineBox::LastLine(mLines); - return line->LastChild(); + if (! mLines.empty()) { + return mLines.back()->LastChild(); } return nsnull; } @@ -4370,7 +4563,7 @@ nsBlockFrame::AppendFrames(nsIPresContext* aPresContext, // Find the proper last-child for where the append should go nsIFrame* lastKid = nsnull; - nsLineBox* lastLine = nsLineBox::LastLine(mLines); + nsLineBox* lastLine = mLines.empty() ? nsnull : mLines.back(); if (lastLine) { lastKid = lastLine->LastChild(); } @@ -4452,17 +4645,22 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext, aPresContext->GetShell(getter_AddRefs(presShell)); // Attempt to find the line that contains the previous sibling - nsLineBox* prevSibLine = nsnull; + nsLineList::iterator prevSibLine = end_lines(); PRInt32 prevSiblingIndex = -1; if (aPrevSibling) { + // XXX_perf This is technically O(N^2) in some cases, but by using + // RFind instead of Find, we make it O(N) in the most common case, + // which is appending cotent. + // Find the line that contains the previous sibling - prevSibLine = nsLineBox::FindLineContaining(mLines, aPrevSibling, - &prevSiblingIndex); - NS_ASSERTION(nsnull != prevSibLine, "prev sibling not in line list"); - if (nsnull == prevSibLine) { - // Note: defensive code! FindLineContaining must not return - // null in this case, so if it does... + if (! nsLineBox::RFindLineContaining(aPrevSibling, + begin_lines(), prevSibLine, + &prevSiblingIndex)) { + // Note: defensive code! RFindLineContaining must not return + // false in this case, so if it does... + NS_NOTREACHED("prev sibling not in line list"); aPrevSibling = nsnull; + prevSibLine = end_lines(); } } @@ -4481,8 +4679,7 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext, if (!line) { return NS_ERROR_OUT_OF_MEMORY; } - line->mNext = prevSibLine->mNext; - prevSibLine->mNext = line; + mLines.after_insert(prevSibLine, line); prevSibLine->SetChildCount(prevSibLine->GetChildCount() - rem); prevSibLine->MarkDirty(); } @@ -4490,8 +4687,8 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext, // Now (partially) join the sibling lists together aPrevSibling->SetNextSibling(aFrameList); } - else if (mLines) { - prevSiblingNextFrame = mLines->mFirstChild; + else if (! mLines.empty()) { + prevSiblingNextFrame = mLines.front()->mFirstChild; } // Walk through the new frames being added and update the line data @@ -4502,24 +4699,23 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext, // If the frame is a block frame, or if there is no previous line // or if the previous line is a block line then make a new line. - if (isBlock || !prevSibLine || prevSibLine->IsBlock()) { + if (isBlock || prevSibLine == end_lines() || prevSibLine->IsBlock()) { // Create a new line for the frame and add its line to the line // list. nsLineBox* line = NS_NewLineBox(presShell, newFrame, 1, isBlock); if (!line) { return NS_ERROR_OUT_OF_MEMORY; } - if (prevSibLine) { + if (prevSibLine != end_lines()) { // Append new line after prevSibLine - line->mNext = prevSibLine->mNext; - prevSibLine->mNext = line; + mLines.after_insert(prevSibLine, line); + ++prevSibLine; } else { // New line is going before the other lines - line->mNext = mLines; - mLines = line; + mLines.push_front(line); + prevSibLine = begin_lines(); } - prevSibLine = line; } else { prevSibLine->SetChildCount(prevSibLine->GetChildCount() + 1); @@ -4579,20 +4775,18 @@ nsBlockFrame::RemoveFrame(nsIPresContext* aPresContext, mFloaters.RemoveFrame(aOldFrame); // Find which line contains the floater - nsLineBox* line = mLines; - while (nsnull != line) { + line_iterator line = begin_lines(), line_end = end_lines(); + for ( ; line != line_end; ++line) { if (line->IsInline() && line->RemoveFloater(aOldFrame)) { aOldFrame->Destroy(aPresContext); - goto found_it; + break; } - line = line->mNext; } - found_it: // Mark every line at and below the line where the floater was dirty - while (nsnull != line) { + // XXXldb This could be done more efficiently. + for ( ; line != line_end; ++line) { line->MarkDirty(); - line = line->mNext; } } #ifdef IBMBIDI @@ -4627,11 +4821,11 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, // Find the line and the previous sibling that contains // deletedFrame; we also find the pointer to the line. nsBlockFrame* flow = this; - nsLineBox** linep = &flow->mLines; - nsLineBox* line = flow->mLines; - nsLineBox* prevLine = nsnull; + nsLineList& lines = flow->mLines; + nsLineList::iterator line = lines.begin(), + line_end = lines.end(); nsIFrame* prevSibling = nsnull; - while (nsnull != line) { + for ( ; line != line_end; ++line) { nsIFrame* frame = line->mFirstChild; PRInt32 n = line->GetChildCount(); while (--n >= 0) { @@ -4641,13 +4835,10 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, prevSibling = frame; frame->GetNextSibling(&frame); } - linep = &line->mNext; - prevLine = line; - line = line->mNext; } found_frame:; #ifdef NS_DEBUG - NS_ASSERTION(nsnull != line, "can't find deleted frame in lines"); + NS_ASSERTION(line != line_end, "can't find deleted frame in lines"); if (nsnull != prevSibling) { nsIFrame* tmp; prevSibling->GetNextSibling(&tmp); @@ -4657,7 +4848,7 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, // Remove frame and all of its continuations while (nsnull != aDeletedFrame) { - while ((nsnull != line) && (nsnull != aDeletedFrame)) { + while ((line != line_end) && (nsnull != aDeletedFrame)) { #ifdef NS_DEBUG nsIFrame* parent; aDeletedFrame->GetParent(&parent); @@ -4680,16 +4871,19 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, if (line->mFirstChild == aDeletedFrame) { line->mFirstChild = nextFrame; } - if (prevLine && !prevLine->IsBlock()) { + + --line; + if (line != line_end && !line->IsBlock()) { // Since we just removed a frame that follows some inline // frames, we need to reflow the previous line. - prevLine->MarkDirty(); + line->MarkDirty(); } + ++line; // Take aDeletedFrame out of the sibling list. Note that // prevSibling will only be nsnull when we are deleting the very // first frame. - if (nsnull != prevSibling) { + if (prevSibling) { prevSibling->SetNextSibling(nextFrame); } @@ -4703,8 +4897,7 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, nsSplittableFrame::RemoveFromFlow(aDeletedFrame); } #ifdef NOISY_REMOVE_FRAME - printf("DoRemoveFrame: prevLine=%p line=%p frame=", - prevLine, line); + printf("DoRemoveFrame: line=%p frame=", line); nsFrame::ListTag(stdout, aDeletedFrame); printf(" prevSibling=%p nextInFlow=%p\n", prevSibling, nextInFlow); #endif @@ -4712,24 +4905,29 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, aDeletedFrame = nextInFlow; // If line is empty, remove it now - nsLineBox* next = line->mNext; PRInt32 lineChildCount = line->GetChildCount(); if (0 == --lineChildCount) { - *linep = next; - line->mNext = nsnull; + nsLineBox *cur = line; + line = lines.erase(line); // Invalidate the space taken up by the line. // XXX We need to do this if we're removing a frame as a result of // a call to RemoveFrame(), but we may not need to do this in all // cases... nsRect lineCombinedArea; - line->GetCombinedArea(&lineCombinedArea); + cur->GetCombinedArea(&lineCombinedArea); #ifdef NOISY_BLOCK_INVALIDATE printf("%p invalidate 10 (%d, %d, %d, %d)\n", this, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height); #endif Invalidate(aPresContext, lineCombinedArea); - line->Destroy(presShell); - line = next; + cur->Destroy(presShell); + + // If we're removing a line, ReflowDirtyLines isn't going to + // know that it needs to slide lines unless something is marked + // dirty. So mark the previous margin of the next line dirty if + // there is one. + if (line != line_end) + line->MarkPreviousMarginDirty(); } else { // Make the line that just lost a frame dirty @@ -4739,9 +4937,7 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, // If we just removed the last frame on the line then we need // to advance to the next line. if (isLastFrameOnLine) { - prevLine = line; - linep = &line->mNext; - line = next; + ++line; } } @@ -4768,9 +4964,9 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, NS_ASSERTION(nsnull != flow, "whoops, continuation without a parent"); // add defensive pointer check for bug 56894 if(flow) { - prevLine = nsnull; - line = flow->mLines; - linep = &flow->mLines; + lines = flow->mLines; + line = lines.begin(); + line_end = lines.end(); prevSibling = nsnull; } } @@ -4799,7 +4995,7 @@ nsBlockFrame::DeleteChildsNextInFlow(nsIPresContext* aPresContext, nsBlockFrame* parent; nextInFlow->GetParent((nsIFrame**)&parent); NS_PRECONDITION(nsnull != parent, "next-in-flow with no parent"); - NS_PRECONDITION(nsnull != parent->mLines, "next-in-flow with weird parent"); + NS_PRECONDITION(! parent->mLines.empty(), "next-in-flow with weird parent"); // NS_PRECONDITION(nsnull == parent->mOverflowLines, "parent with overflow"); parent->DoRemoveFrame(aPresContext, nextInFlow); #ifdef IBMBIDI @@ -4861,7 +5057,9 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, PRBool isAdjacentWithTop = aState.IsAdjacentWithTop(); nsReflowStatus frameReflowStatus; - nsresult rv = brc.ReflowBlock(floater, availSpace, PR_TRUE, 0, isAdjacentWithTop, + nsCollapsingMargin margin; + nsresult rv = brc.ReflowBlock(floater, availSpace, PR_TRUE, margin, + isAdjacentWithTop, aComputedOffsetsResult, frameReflowStatus); if (NS_SUCCEEDED(rv) && isAutoWidth) { nscoord maxElementWidth = brc.GetMaxElementSize().width; @@ -4870,7 +5068,9 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, // width. Reflow it again, this time pinning the width to the // maxElementSize. availSpace.width = maxElementWidth; - rv = brc.ReflowBlock(floater, availSpace, PR_TRUE, 0, isAdjacentWithTop, + nsCollapsingMargin marginMES; + rv = brc.ReflowBlock(floater, availSpace, PR_TRUE, marginMES, + isAdjacentWithTop, aComputedOffsetsResult, frameReflowStatus); } } @@ -4893,9 +5093,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, const nsMargin& m = brc.GetMargin(); aMarginResult.top = brc.GetTopMargin(); aMarginResult.right = m.right; - aMarginResult.bottom = - nsBlockReflowContext::MaxMargin(brc.GetCarriedOutBottomMargin(), - m.bottom); + brc.GetCarriedOutBottomMargin().Include(m.bottom); + aMarginResult.bottom = brc.GetCarriedOutBottomMargin().get(); aMarginResult.left = m.left; const nsHTMLReflowMetrics& metrics = brc.GetMetrics(); @@ -4903,6 +5102,10 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, // Set the rect, make sure the view is properly sized and positioned, // and tell the frame we're done reflowing it + // XXXldb This seems like the wrong place to be doing this -- shouldn't + // we be doing this in nsBlockReflowState::FlowAndPlaceFloater after + // we've positioned the floater, and shouldn't we be doing the equivalent + // of |::PlaceFrameView| here? floater->SizeTo(aState.mPresContext, metrics.width, metrics.height); nsIView* view; floater->GetView(aState.mPresContext, &view); @@ -4944,16 +5147,18 @@ nsBlockFrame::GetSkipSides() const } #ifdef DEBUG -static void ComputeCombinedArea(nsLineBox* aLine, +static void ComputeCombinedArea(nsLineList& aLines, nscoord aWidth, nscoord aHeight, nsRect& aResult) { nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight; - while (nsnull != aLine) { + for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end(); + line != line_end; + ++line) { // Compute min and max x/y values for the reflowed frame's // combined areas nsRect lineCombinedArea; - aLine->GetCombinedArea(&lineCombinedArea); + line->GetCombinedArea(&lineCombinedArea); nscoord x = lineCombinedArea.x; nscoord y = lineCombinedArea.y; nscoord xmost = x + lineCombinedArea.width; @@ -4970,7 +5175,6 @@ static void ComputeCombinedArea(nsLineBox* aLine, if (ymost > yb) { yb = ymost; } - aLine = aLine->mNext; } aResult.x = xa; @@ -5154,7 +5358,9 @@ nsBlockFrame::PaintFloaters(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect) { - for (nsLineBox* line = mLines; nsnull != line; line = line->mNext) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { if (!line->HasFloaters()) { continue; } @@ -5194,7 +5400,9 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext, } #endif - for (nsLineBox* line = mLines; nsnull != line; line = line->mNext) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { // If the line's combined area (which includes child frames that // stick outside of the line's bounding box or our bounding box) // intersects the dirty rect then paint the line. @@ -5206,7 +5414,8 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext, line->GetCombinedArea(&lineCombinedArea); nsFrame::IndentBy(stdout, depth+1); printf("draw line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n", - line, line->mBounds.x, line->mBounds.y, + NS_STATIC_CAST(void*, line.get()), + line->mBounds.x, line->mBounds.y, line->mBounds.width, line->mBounds.height, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height); @@ -5231,7 +5440,8 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext, line->GetCombinedArea(&lineCombinedArea); nsFrame::IndentBy(stdout, depth+1); printf("skip line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n", - line, line->mBounds.x, line->mBounds.y, + NS_STATIC_CAST(void*, line.get()), + line->mBounds.x, line->mBounds.y, line->mBounds.width, line->mBounds.height, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height); @@ -5252,7 +5462,7 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext, if (gLamePaintMetrics) { PRTime end = PR_Now(); - PRInt32 numLines = nsLineBox::ListLength(mLines); + PRInt32 numLines = mLines.size(); if (!numLines) numLines = 1; PRTime lines, deltaPerLine, delta; LL_I2L(lines, numLines); @@ -5386,15 +5596,13 @@ nsBlockFrame::HandleEvent(nsIPresContext* aPresContext, nsIFrame *resultFrame = nsnull;//this will be passed the handle event when we //can tell who to pass it to - nsCOMPtr it; nsIFrame *mainframe = this; - nsCOMPtr tracker; - aPresContext->GetShell(getter_AddRefs(shell)); + aPresContext->GetShell(getter_AddRefs(shell)); if (!shell) return NS_OK; - result = shell->QueryInterface(NS_GET_IID(nsIFocusTracker),getter_AddRefs(tracker)); + nsCOMPtr tracker( do_QueryInterface(shell, &result) ); - result = mainframe->QueryInterface(NS_GET_IID(nsILineIterator),getter_AddRefs(it)); + nsCOMPtr it( do_QueryInterface(mainframe, &result) ); nsIView* parentWithView; nsPoint origin; nsPeekOffsetStruct pos; @@ -5426,7 +5634,7 @@ nsBlockFrame::HandleEvent(nsIPresContext* aPresContext, if (NS_SUCCEEDED(result) && pos.mResultFrame){ if (result == NS_OK) - result = pos.mResultFrame->QueryInterface(NS_GET_IID(nsILineIterator),getter_AddRefs(it));//if this fails thats ok + it = do_QueryInterface(pos.mResultFrame, &result);//if this fails thats ok resultFrame = pos.mResultFrame; mainframe = resultFrame; } @@ -5590,18 +5798,19 @@ nsBlockFrame::ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild) // Mark the line containing the child frame dirty. PRBool isFloater; - nsLineBox* prevLine; - nsLineBox* line = FindLineFor(aChild, &prevLine, &isFloater); + line_iterator fline; + FindLineFor(aChild, &isFloater, &fline); if (!isFloater) { - if (line) - MarkLineDirty(line, prevLine); + if (fline != end_lines()) + MarkLineDirty(fline); } else { - line = mLines; - while (nsnull != line) { + // XXXldb Could we do this more efficiently? + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { line->MarkDirty(); - line = line->mNext; } } } @@ -5658,27 +5867,28 @@ nsBlockFrame::ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild) #ifdef NS_DEBUG static PRBool -InLineList(nsLineBox* aLines, nsIFrame* aFrame) +InLineList(nsLineList& aLines, nsIFrame* aFrame) { - while (nsnull != aLines) { - nsIFrame* frame = aLines->mFirstChild; - PRInt32 n = aLines->GetChildCount(); + for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end(); + line != line_end; + ++line) { + nsIFrame* frame = line->mFirstChild; + PRInt32 n = line->GetChildCount(); while (--n >= 0) { if (frame == aFrame) { return PR_TRUE; } frame->GetNextSibling(&frame); } - aLines = aLines->mNext; } return PR_FALSE; } static PRBool -InSiblingList(nsLineBox* aLine, nsIFrame* aFrame) +InSiblingList(nsLineList& aLines, nsIFrame* aFrame) { - if (nsnull != aLine) { - nsIFrame* frame = aLine->mFirstChild; + if (! aLines.empty()) { + nsIFrame* frame = aLines.front()->mFirstChild; while (nsnull != frame) { if (frame == aFrame) { return PR_TRUE; @@ -5700,8 +5910,8 @@ nsBlockFrame::IsChild(nsIPresContext* aPresContext, nsIFrame* aFrame) if (InLineList(mLines, aFrame) && InSiblingList(mLines, aFrame)) { return PR_TRUE; } - nsLineBox* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); - if (InLineList(overflowLines, aFrame) && InSiblingList(overflowLines, aFrame)) { + nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); + if (InLineList(*overflowLines, aFrame) && InSiblingList(*overflowLines, aFrame)) { return PR_TRUE; } return PR_FALSE; @@ -5724,12 +5934,12 @@ nsBlockFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const PRUint32 sum = sizeof(*this); // Add in size of each line object - nsLineBox* line = mLines; - while (line) { + for (const_line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { PRUint32 lineBoxSize; nsIAtom* atom = line->SizeOf(aHandler, &lineBoxSize); aHandler->AddSize(atom, lineBoxSize); - line = line->mNext; } *aResult = sum; @@ -5900,8 +6110,10 @@ nsBlockFrame::RenumberListsInBlock(nsIPresContext* aPresContext, while (nsnull != aBlockFrame) { // Examine each line in the block - nsLineBox* line = aBlockFrame->mLines; - while (line) { + for (line_iterator line = aBlockFrame->begin_lines(), + line_end = aBlockFrame->end_lines(); + line != line_end; + ++line) { nsIFrame* kid = line->mFirstChild; PRInt32 n = line->GetChildCount(); while (--n >= 0) { @@ -5912,7 +6124,6 @@ nsBlockFrame::RenumberListsInBlock(nsIPresContext* aPresContext, } kid->GetNextSibling(&kid); } - line = line->mNext; } // Advance to the next continuation @@ -6038,8 +6249,9 @@ nsBlockFrame::BuildFloaterList() { nsIFrame* head = nsnull; nsIFrame* current = nsnull; - nsLineBox* line = mLines; - while (nsnull != line) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { if (line->HasFloaters()) { nsFloaterCache* fc = line->GetFirstFloater(); while (fc) { @@ -6054,7 +6266,6 @@ nsBlockFrame::BuildFloaterList() fc = fc->Next(); } } - line = line->mNext; } // Terminate end of floater list just in case a floater was removed @@ -6073,8 +6284,7 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK) if (!gVerifyLines) { return; } - nsLineBox* line = mLines; - if (!line) { + if (mLines.empty()) { return; } @@ -6082,7 +6292,10 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK) // set properly. PRInt32 count = 0; PRBool seenBlock = PR_FALSE; - while (nsnull != line) { + line_iterator line, line_end; + for (line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { if (aFinalCheckOK) { NS_ABORT_IF_FALSE(line->GetChildCount(), "empty line"); if (line->IsBlock()) { @@ -6093,12 +6306,11 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK) } } count += line->GetChildCount(); - line = line->mNext; } // Then count the frames PRInt32 frameCount = 0; - nsIFrame* frame = mLines->mFirstChild; + nsIFrame* frame = mLines.front()->mFirstChild; while (nsnull != frame) { frameCount++; frame->GetNextSibling(&frame); @@ -6106,17 +6318,16 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK) NS_ASSERTION(count == frameCount, "bad line list"); // Next: test that each line has right number of frames on it - line = mLines; - nsLineBox* prevLine = nsnull; - while (nsnull != line) { + for (line = begin_lines(), line_end = end_lines(); + line != line_end; + ) { count = line->GetChildCount(); frame = line->mFirstChild; while (--count >= 0) { frame->GetNextSibling(&frame); } - prevLine = line; - line = line->mNext; - if ((nsnull != line) && (0 != line->GetChildCount())) { + ++line; + if ((line != line_end) && (0 != line->GetChildCount())) { NS_ASSERTION(frame == line->mFirstChild, "bad line list"); } } @@ -6130,10 +6341,10 @@ nsBlockFrame::VerifyOverflowSituation(nsIPresContext* aPresContext) { nsBlockFrame* flow = (nsBlockFrame*) GetFirstInFlow(); while (nsnull != flow) { - nsLineBox* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); + nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); if (nsnull != overflowLines) { - NS_ASSERTION(nsnull != overflowLines->mFirstChild, - "bad overflow list"); + NS_ASSERTION(! overflowLines->empty(), "should not be empty if present"); + NS_ASSERTION(overflowLines->front()->mFirstChild, "bad overflow list"); } flow = (nsBlockFrame*) flow->mNextInFlow; } diff --git a/mozilla/layout/generic/nsBlockFrame.h b/mozilla/layout/generic/nsBlockFrame.h index 7c501433fc7..efc8f52558b 100644 --- a/mozilla/layout/generic/nsBlockFrame.h +++ b/mozilla/layout/generic/nsBlockFrame.h @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -40,12 +41,14 @@ #include "nsHTMLContainerFrame.h" #include "nsHTMLParts.h" #include "nsAbsoluteContainingBlock.h" +#include "nsLineBox.h" class nsBlockReflowState; class nsBulletFrame; class nsLineBox; class nsFirstLineFrame; class nsILineIterator; +class nsIntervalSet; /** * Child list name indices * @see #GetAdditionalChildListName() @@ -71,6 +74,22 @@ extern const nsIID kBlockFrameCID; */ class nsBlockFrame : public nsBlockFrameSuper { +public: + typedef nsLineList::iterator line_iterator; + typedef nsLineList::const_iterator const_line_iterator; + typedef nsLineList::reverse_iterator reverse_line_iterator; + typedef nsLineList::const_reverse_iterator const_reverse_line_iterator; + +protected: + line_iterator begin_lines() { return mLines.begin(); } + line_iterator end_lines() { return mLines.end(); } + const_line_iterator begin_lines() const { return mLines.begin(); } + const_line_iterator end_lines() const { return mLines.end(); } + reverse_line_iterator rbegin_lines() { return mLines.rbegin(); } + reverse_line_iterator rend_lines() { return mLines.rend(); } + const_reverse_line_iterator rbegin_lines() const { return mLines.rbegin(); } + const_reverse_line_iterator rend_lines() const { return mLines.rend(); } + public: friend nsresult NS_NewBlockFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aFlags); @@ -130,6 +149,8 @@ public: PRBool aCheckVis, PRBool* aIsVisible); + NS_IMETHOD IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool* aResult); + // nsIHTMLReflow NS_IMETHOD Reflow(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, @@ -173,8 +194,10 @@ public: nsresult UpdateSpaceManager(nsIPresContext* aPresContext, nsISpaceManager* aSpaceManager); - nsLineBox* FindLineFor(nsIFrame* aFrame, nsLineBox** aPrevLineResult, - PRBool* aIsFloaterResult); + // returns true on success and false if aFoundLine is set to end_lines() + PRBool FindLineFor(nsIFrame* aFrame, + PRBool* aIsFloaterResult, + line_iterator* aFoundLine); static nsresult GetCurrentLine(nsBlockReflowState *aState, nsLineBox **aOutCurrentLine); @@ -278,11 +301,6 @@ protected: /** reflow all lines that have been marked dirty */ nsresult ReflowDirtyLines(nsBlockReflowState& aState); - /** set aState to what it would be if we had done a full reflow to this point. */ - void RecoverStateFrom(nsBlockReflowState& aState, - nsLineBox* aLine, - nsRect* aDamageRect); - //---------------------------------------- // Methods for line reflow /** @@ -295,18 +313,21 @@ protected: * this indicates that frames may have changed size, for example */ nsresult ReflowLine(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRBool aDamageDirtyArea = PR_FALSE); nsresult PlaceLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRBool aUpdateMaximumWidth); - nsresult MarkLineDirty (nsLineBox* aLine, - nsLineBox* aPrevLine); + /** + * Mark |aLine| dirty, and, if necessary because of possible + * pull-up, mark the previous line dirty as well. + */ + nsresult MarkLineDirty(line_iterator aLine); // XXX blech void PostPlaceLine(nsBlockReflowState& aState, @@ -318,9 +339,12 @@ protected: nsSize* aMaxElementSize); // XXX where to go - PRBool ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine); + PRBool ShouldJustifyLine(nsBlockReflowState& aState, + line_iterator aLine); - void DeleteLine(nsBlockReflowState& aState, nsLineBox* aLine); + void DeleteLine(nsBlockReflowState& aState, + nsLineList::iterator aLine, + nsLineList::iterator aLineEnd); //---------------------------------------- // Methods for individual frame reflow @@ -329,32 +353,32 @@ protected: nsLineBox* aLine); nsresult ReflowBlockFrame(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepGoing); nsresult ReflowInlineFrames(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepLineGoing, PRBool aDamageDirtyArea, PRBool aUpdateMaximumWidth = PR_FALSE); nsresult DoReflowInlineFrames(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRUint8* aLineReflowStatus, PRBool aUpdateMaximumWidth, PRBool aDamageDirtyArea); nsresult DoReflowInlineFramesAuto(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRUint8* aLineReflowStatus, PRBool aUpdateMaximumWidth, PRBool aDamageDirtyArea); nsresult DoReflowInlineFramesMalloc(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRUint8* aLineReflowStatus, PRBool aUpdateMaximumWidth, @@ -362,7 +386,7 @@ protected: nsresult ReflowInlineFrame(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, nsIFrame* aFrame, PRUint8* aLineReflowStatus); @@ -382,23 +406,25 @@ protected: nsresult SplitLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, nsIFrame* aFrame); nsresult PullFrame(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool aDamageDeletedLine, nsIFrame*& aFrameResult); nsresult PullFrameFrom(nsBlockReflowState& aState, nsLineBox* aToLine, - nsLineBox** aFromList, + nsLineList& aFromContainer, + nsLineList::iterator aFromLine, PRBool aUpdateGeometricParent, PRBool aDamageDeletedLines, nsIFrame*& aFrameResult, PRBool& aStopPulling); - void PushLines(nsBlockReflowState& aState); + void PushLines(nsBlockReflowState& aState, + nsLineList::iterator aLineBefore); //---------------------------------------- //XXX @@ -412,10 +438,13 @@ protected: nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect); - void PropagateReflowDamage(nsBlockReflowState& aState, + void RememberFloaterDamage(nsBlockReflowState& aState, nsLineBox* aLine, - const nsRect& aOldCombinedArea, - nscoord aDeltaY); + const nsRect& aOldCombinedArea); + + void PropagateFloaterDamage(nsBlockReflowState& aState, + nsLineBox* aLine, + nscoord aDeltaY); void BuildFloaterList(); @@ -440,11 +469,11 @@ protected: //---------------------------------------- - nsLineBox* GetOverflowLines(nsIPresContext* aPresContext, - PRBool aRemoveProperty) const; + nsLineList* GetOverflowLines(nsIPresContext* aPresContext, + PRBool aRemoveProperty) const; nsresult SetOverflowLines(nsIPresContext* aPresContext, - nsLineBox* aOverflowFrames); + nsLineList* aOverflowLines); nsIFrame* LastChild(); @@ -458,7 +487,7 @@ protected: // Ascent of our first line to support 'vertical-align: baseline' in table-cells nscoord mAscent; - nsLineBox* mLines; + nsLineList mLines; // List of all floaters in this block nsFrameList mFloaters; diff --git a/mozilla/layout/generic/nsBlockReflowContext.cpp b/mozilla/layout/generic/nsBlockReflowContext.cpp index 9b2eec23b32..c03a10c1f2f 100644 --- a/mozilla/layout/generic/nsBlockReflowContext.cpp +++ b/mozilla/layout/generic/nsBlockReflowContext.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -80,25 +81,31 @@ nsBlockReflowContext::nsBlockReflowContext(nsIPresContext* aPresContext, mMetrics.mFlags |= NS_REFLOW_CALC_MAX_WIDTH; } -nscoord +void nsBlockReflowContext::ComputeCollapsedTopMargin(nsIPresContext* aPresContext, - nsHTMLReflowState& aRS) + nsHTMLReflowState& aRS, + /* inout */ nsCollapsingMargin& aMargin) { // Get aFrame's top margin - nscoord topMargin = aRS.mComputedMargin.top; + aMargin.Include(aRS.mComputedMargin.top); + +#ifdef NOISY_VERTICAL_MARGINS + nsFrame::ListTag(stdout, aRS.frame); + printf(": %d => %d\n", aRS.mComputedMargin.top, aMargin.get()); +#endif // Calculate aFrame's generational top-margin from its child // blocks. Note that if aFrame has a non-zero top-border or // top-padding then this step is skipped because it will be a margin // root. It is also skipped if the frame is a margin root for other // reasons. - nscoord generationalTopMargin = 0; if (0 == aRS.mComputedBorderPadding.top) { nsFrameState state; aRS.frame->GetFrameState(&state); if (!(state & NS_BLOCK_MARGIN_ROOT)) { nsBlockFrame* bf; - if (NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, (void**)&bf))) { + if (NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, + NS_REINTERPRET_CAST(void**, &bf)))) { // Ask the block frame for the top block child that we should // try to collapse the top margin with. @@ -109,7 +116,7 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(nsIPresContext* aPresContext, nsIFrame* childFrame = bf->GetTopBlockChild(); if (nsnull != childFrame) { - // Here is where we recurse. Now that we have determined that a + // Here is where we recur. Now that we have determined that a // generational collapse is required we need to compute the // child blocks margin and so in so that we can look into // it. For its margins to be computed we need to have a reflow @@ -117,24 +124,16 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(nsIPresContext* aPresContext, nsSize availSpace(aRS.mComputedWidth, aRS.mComputedHeight); nsHTMLReflowState reflowState(aPresContext, aRS, childFrame, availSpace); - generationalTopMargin = - ComputeCollapsedTopMargin(aPresContext, reflowState); + ComputeCollapsedTopMargin(aPresContext, reflowState, aMargin); } } } } - // Now compute the collapsed top-margin value. At this point we have - // the child frames effective top margin value. - nscoord collapsedTopMargin = MaxMargin(topMargin, generationalTopMargin); - #ifdef NOISY_VERTICAL_MARGINS nsFrame::ListTag(stdout, aRS.frame); - printf(": topMargin=%d generationalTopMargin=%d => %d\n", - topMargin, generationalTopMargin, collapsedTopMargin); + printf(": => %d\n", aMargin.get()); #endif - - return collapsedTopMargin; } struct nsBlockHorizontalAlign { @@ -245,7 +244,7 @@ nsresult nsBlockReflowContext::ReflowBlock(nsIFrame* aFrame, const nsRect& aSpace, PRBool aApplyTopMargin, - nscoord aPrevBottomMargin, + nsCollapsingMargin& aPrevBottomMargin, PRBool aIsAdjacentWithTop, nsMargin& aComputedOffsets, nsReflowStatus& aFrameReflowStatus) @@ -422,7 +421,7 @@ nsBlockReflowContext::DoReflowBlock(nsHTMLReflowState &aReflowState, nsIFrame* aFrame, const nsRect& aSpace, PRBool aApplyTopMargin, - nscoord aPrevBottomMargin, + nsCollapsingMargin& aPrevBottomMargin, PRBool aIsAdjacentWithTop, nsMargin& aComputedOffsets, nsReflowStatus& aFrameReflowStatus) @@ -438,32 +437,25 @@ nsBlockReflowContext::DoReflowBlock(nsHTMLReflowState &aReflowState, mIsTable = NS_STYLE_DISPLAY_TABLE == aReflowState.mStyleDisplay->mDisplay; mComputedWidth = aReflowState.mComputedWidth; - nscoord topMargin = 0; if (aApplyTopMargin) { // Compute the childs collapsed top margin (its margin collpased // with its first childs top-margin -- recursively). - topMargin = ComputeCollapsedTopMargin(mPresContext, aReflowState); + ComputeCollapsedTopMargin(mPresContext, aReflowState, aPrevBottomMargin); #ifdef NOISY_VERTICAL_MARGINS nsFrame::ListTag(stdout, mOuterReflowState.frame); printf(": reflowing "); nsFrame::ListTag(stdout, aFrame); - printf(" prevBottomMargin=%d, collapsedTopMargin=%d => %d\n", - aPrevBottomMargin, topMargin, - MaxMargin(topMargin, aPrevBottomMargin)); + printf(" margin => %d\n", aPrevBottomMargin.get()); #endif - // Collapse that value with the previous bottom margin to perform - // the sibling to sibling collaspe. - topMargin = MaxMargin(topMargin, aPrevBottomMargin); - // Adjust the available height if its constrained so that the // child frame doesn't think it can reflow into its margin area. - if (aApplyTopMargin && (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) { - aReflowState.availableHeight -= topMargin; + if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) { + aReflowState.availableHeight -= aPrevBottomMargin.get(); } } - mTopMargin = topMargin; + mTopMargin = aPrevBottomMargin.get(); // Compute x/y coordinate where reflow will begin. Use the rules // from 10.3.3 to determine what to apply. At this point in the @@ -473,7 +465,7 @@ nsBlockReflowContext::DoReflowBlock(nsHTMLReflowState &aReflowState, mStyleMargin = aReflowState.mStyleMargin; mStylePadding = aReflowState.mStylePadding; nscoord x; - nscoord y = aSpace.y + topMargin; + nscoord y = aSpace.y + mTopMargin; // If it's a right floated element, then calculate the x-offset // differently @@ -698,14 +690,13 @@ nsBlockReflowContext::DoReflowBlock(nsHTMLReflowState &aReflowState, PRBool nsBlockReflowContext::PlaceBlock(PRBool aForceFit, const nsMargin& aComputedOffsets, - nscoord* aBottomMarginResult, + nsCollapsingMargin& aBottomMarginResult, nsRect& aInFlowBounds, nsRect& aCombinedRect) { // Compute collapsed bottom margin value - nscoord collapsedBottomMargin = MaxMargin(mMetrics.mCarriedOutBottomMargin, - mMargin.bottom); - *aBottomMarginResult = collapsedBottomMargin; + aBottomMarginResult = mMetrics.mCarriedOutBottomMargin; + aBottomMarginResult.Include(mMargin.bottom); // See if the block will fit in the available space PRBool fits = PR_TRUE; @@ -723,8 +714,7 @@ nsBlockReflowContext::PlaceBlock(PRBool aForceFit, { // Collapse the bottom margin with the top margin that was already // applied. - nscoord newBottomMargin = MaxMargin(collapsedBottomMargin, mTopMargin); - *aBottomMarginResult = newBottomMargin; + aBottomMarginResult.Include(mTopMargin); #ifdef NOISY_VERTICAL_MARGINS printf(" "); nsFrame::ListTag(stdout, mOuterReflowState.frame); diff --git a/mozilla/layout/generic/nsBlockReflowContext.h b/mozilla/layout/generic/nsBlockReflowContext.h index 5f12c739557..30422ec4b03 100644 --- a/mozilla/layout/generic/nsBlockReflowContext.h +++ b/mozilla/layout/generic/nsBlockReflowContext.h @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -70,20 +71,20 @@ public: nsresult ReflowBlock(nsIFrame* aFrame, const nsRect& aSpace, PRBool aApplyTopMargin, - nscoord aPrevBottomMargin, + nsCollapsingMargin& aPrevBottomMargin, PRBool aIsAdjacentWithTop, nsMargin& aComputedOffsets, nsReflowStatus& aReflowStatus); PRBool PlaceBlock(PRBool aForceFit, const nsMargin& aComputedOffsets, - nscoord* aBottomMarginResult, + nsCollapsingMargin& aBottomMarginResult /* out */, nsRect& aInFlowBounds, nsRect& aCombinedRect); void AlignBlockHorizontally(nscoord aWidth, nsBlockHorizontalAlign&); - nscoord GetCarriedOutBottomMargin() const { + nsCollapsingMargin& GetCarriedOutBottomMargin() { return mMetrics.mCarriedOutBottomMargin; } @@ -111,25 +112,9 @@ public: return mBlockShouldInvalidateItself; } - // Compute the largest of two adjacent vertical margins, as per the - // CSS2 spec section 8.3.1 - static nscoord MaxMargin(nscoord a, nscoord b) { - if (a < 0) { - if (b < 0) { - if (a < b) return a; - return b; - } - return b + a; - } - else if (b < 0) { - return a + b; - } - if (a > b) return a; - return b; - } - - static nscoord ComputeCollapsedTopMargin(nsIPresContext* aPresContext, - nsHTMLReflowState& aRS); + static void ComputeCollapsedTopMargin(nsIPresContext* aPresContext, + nsHTMLReflowState& aRS, + /* inout */ nsCollapsingMargin& aMargin); protected: nsStyleUnit GetRealMarginLeftUnit(); @@ -140,7 +125,7 @@ protected: nsIFrame* aFrame, const nsRect& aSpace, PRBool aApplyTopMargin, - nscoord aPrevBottomMargin, + nsCollapsingMargin& aPrevBottomMargin, PRBool aIsAdjacentWithTop, nsMargin& aComputedOffsets, nsReflowStatus& aReflowStatus); diff --git a/mozilla/layout/generic/nsBlockReflowState.cpp b/mozilla/layout/generic/nsBlockReflowState.cpp index 01b19bec913..a41e46a14fb 100644 --- a/mozilla/layout/generic/nsBlockReflowState.cpp +++ b/mozilla/layout/generic/nsBlockReflowState.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -60,7 +61,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mReflowState(aReflowState), mLastFloaterY(0), mNextRCFrame(nsnull), - mPrevBottomMargin(0), + mPrevBottomMargin(), mLineNumber(0), mFlags(0) { @@ -93,7 +94,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mReflowStatus = NS_FRAME_COMPLETE; mPresContext = aPresContext; - mBlock->GetNextInFlow((nsIFrame**)&mNextInFlow); + mBlock->GetNextInFlow(NS_REINTERPRET_CAST(nsIFrame**, &mNextInFlow)); mKidXMost = 0; // Compute content area width (the content area is inside the border @@ -143,8 +144,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mBand.Init(mSpaceManager, mContentArea); mPrevChild = nsnull; - mCurrentLine = nsnull; - mPrevLine = nsnull; + mCurrentLine = aFrame->end_lines(); const nsStyleText* styleText; mBlock->GetStyleData(eStyleStruct_Text, @@ -366,7 +366,7 @@ nsBlockReflowState::ClearPastFloaters(PRUint8 aBreakType) case NS_STYLE_CLEAR_RIGHT: case NS_STYLE_CLEAR_LEFT_AND_RIGHT: // Apply the previous margin before clearing - saveY = mY + mPrevBottomMargin; + saveY = mY + mPrevBottomMargin.get(); ClearFloaters(saveY, aBreakType); #ifdef NOISY_FLOATER_CLEARING nsFrame::ListTag(stdout, mBlock); @@ -398,7 +398,9 @@ nsBlockReflowState::ClearPastFloaters(PRUint8 aBreakType) // XXXldb This doesn't handle collapsing with negative margins // correctly, although it's arguable what "correct" is. - mPrevBottomMargin = deltaY; + // XXX Are all the other margins included by this point? + mPrevBottomMargin.Zero(); + mPrevBottomMargin.Include(deltaY); mY = saveY; // Force margin to be applied in this circumstance @@ -408,73 +410,77 @@ nsBlockReflowState::ClearPastFloaters(PRUint8 aBreakType) // Put mY back to its original value since no clearing // happened. That way the previous blocks bottom margin will // be applied properly. - mY = saveY - mPrevBottomMargin; + mY = saveY - mPrevBottomMargin.get(); } break; } return applyTopMargin; } -// Recover the collapsed vertical margin values for aLine. Note that -// the values are not collapsed with aState.mPrevBottomMargin, nor are -// they collapsed with each other when the line height is zero. +/* + * Reconstruct the vertical margin before the line |aLine| in order to + * do an incremental reflow that begins with |aLine| without reflowing + * the line before it. |aLine| may point to the fencepost at the end of + * the line list, and it is used this way since we (for now, anyway) + * always need to recover margins at the end of a block. + * + * The reconstruction involves walking backward through the line list to + * find any collapsed margins preceding the line that would have been in + * the reflow state's |mPrevBottomMargin| when we reflowed that line in + * a full reflow (under the rule in CSS2 that all adjacent vertical + * margins of blocks collapse). + */ void -nsBlockReflowState::RecoverVerticalMargins(nsLineBox* aLine, - PRBool aApplyTopMargin, - nscoord* aTopMarginResult, - nscoord* aBottomMarginResult) +nsBlockReflowState::ReconstructMarginAbove(nsLineList::iterator aLine) { - if (aLine->IsBlock()) { - // Update band data - GetAvailableSpace(); + mPrevBottomMargin.Zero(); + nsBlockFrame *block = mBlock; - // Setup reflow state to compute the block childs top and bottom - // margins - nsIFrame* frame = aLine->mFirstChild; - nsRect availSpaceRect; - const nsStyleDisplay* display; - frame->GetStyleData(eStyleStruct_Display, - (const nsStyleStruct*&) display); - nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; - frame->IsSplittable(splitType); - ComputeBlockAvailSpace(frame, splitType, display, availSpaceRect); - nsSize availSpace(availSpaceRect.width, availSpaceRect.height); - nsHTMLReflowState reflowState(mPresContext, mReflowState, - frame, availSpace); + const nsStyleText* styleText = NS_STATIC_CAST(const nsStyleText*, + block->mStyleContext->GetStyleData(eStyleStruct_Text)); + PRBool isPre = + ((NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) || + (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace)); - // Compute collapsed top margin - nscoord topMargin = 0; - if (aApplyTopMargin) { - topMargin = - nsBlockReflowContext::ComputeCollapsedTopMargin(mPresContext, - reflowState); + nsCompatibility mode; + mPresContext->GetCompatibilityMode(&mode); + PRBool isQuirkMode = mode == eCompatibility_NavQuirks; + + nsLineList::iterator firstLine = block->begin_lines(); + for (;;) { + --aLine; + if (aLine->IsBlock()) { + mPrevBottomMargin = aLine->GetCarriedOutBottomMargin(); + break; + } + PRBool isEmpty; + aLine->IsEmpty(isQuirkMode, isPre, &isEmpty); + if (! isEmpty) { + break; + } + if (aLine == firstLine) { + // If the top margin was carried out (and thus already applied), + // set it to zero. Either way, we're done. + if ((0 == mReflowState.mComputedBorderPadding.top) && + !(block->mState & NS_BLOCK_MARGIN_ROOT)) { + mPrevBottomMargin.Zero(); + } + break; } - - // Compute collapsed bottom margin - nscoord bottomMargin = reflowState.mComputedMargin.bottom; - bottomMargin = - nsBlockReflowContext::MaxMargin(bottomMargin, - aLine->GetCarriedOutBottomMargin()); - *aTopMarginResult = topMargin; - *aBottomMarginResult = bottomMargin; - } - else { - // XXX_ib, see bug 44188 - *aTopMarginResult = 0; - *aBottomMarginResult = 0; } } void -nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, - PRBool aApplyTopMargin, - nsRect* aDamageRect) +nsBlockReflowState::RecoverStateFrom(nsLineList::iterator aLine, + nscoord aDeltaY) { // Make the line being recovered the current line mCurrentLine = aLine; // Update aState.mPrevChild as if we had reflowed all of the frames // in this line. + // XXXldb This is expensive in some cases, since it requires walking + // |GetNextSibling|. mPrevChild = aLine->LastChild(); // Recover mKidXMost and mMaxElementSize @@ -506,83 +512,6 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, UpdateMaximumWidth(aLine->mMaximumWidth); } - // The line may have clear before semantics. - if (aLine->IsBlock() && aLine->HasBreak()) { - // Clear past floaters before the block if the clear style is not none - aApplyTopMargin = ClearPastFloaters(aLine->GetBreakType()); -#ifdef NOISY_VERTICAL_MARGINS - nsFrame::ListTag(stdout, mBlock); - printf(": RecoverStateFrom: y=%d child ", mY); - nsFrame::ListTag(stdout, aLine->mFirstChild); - printf(" has clear of %d => %s, mPrevBottomMargin=%d\n", aLine->mBreakType, - aApplyTopMargin ? "applyTopMargin" : "nope", mPrevBottomMargin); -#endif - } - - // Recover mPrevBottomMargin and calculate the line's new Y - // coordinate (newLineY) - nscoord newLineY = mY; - nsRect lineCombinedArea; - aLine->GetCombinedArea(&lineCombinedArea); - if (aLine->IsBlock()) { - if ((0 == aLine->mBounds.height) && (0 == lineCombinedArea.height)) { - // The line's top and bottom margin values need to be collapsed - // with the mPrevBottomMargin to determine a new - // mPrevBottomMargin value. - nscoord topMargin, bottomMargin; - RecoverVerticalMargins(aLine, aApplyTopMargin, - &topMargin, &bottomMargin); - nscoord m = nsBlockReflowContext::MaxMargin(bottomMargin, - mPrevBottomMargin); - m = nsBlockReflowContext::MaxMargin(m, topMargin); - mPrevBottomMargin = m; - } - else { - // Recover the top and bottom margins for this line - nscoord topMargin, bottomMargin; - RecoverVerticalMargins(aLine, aApplyTopMargin, - &topMargin, &bottomMargin); - - // Compute the collapsed top margin value - nscoord collapsedTopMargin = - nsBlockReflowContext::MaxMargin(topMargin, mPrevBottomMargin); - - // The lineY is just below the collapsed top margin value. The - // mPrevBottomMargin gets set to the bottom margin value for the - // line. - newLineY += collapsedTopMargin; - mPrevBottomMargin = bottomMargin; - } - } - else if (0 == aLine->GetHeight()) { - // For empty inline lines we leave the previous bottom margin - // alone so that it's collpased with the next line. - } - else { - // For non-empty inline lines the previous margin is applied - // before the line. Therefore apply it now and zero it out. - newLineY += mPrevBottomMargin; - mPrevBottomMargin = 0; - } - - // Save away the old combined area for later - nsRect oldCombinedArea = lineCombinedArea; - - // Slide the frames in the line by the computed delta. This also - // updates the lines Y coordinate and the combined area's Y - // coordinate. - nscoord finalDeltaY = newLineY - aLine->mBounds.y; - mBlock->SlideLine(*this, aLine, finalDeltaY); - // aLine has been slided, but... - // XXX it is not necessary to worry about the ascent of mBlock here, right? - // Indeed, depending on the status of the first line of mBlock, we can either have: - // case first line of mBlock is dirty : it will be reflowed by mBlock and so - // mBlock->mAscent will be recomputed by the block frame, and we will - // never enter into this RecoverStateFrom(aLine) function. - // case first line of mBlock is clean : it is untouched by the incremental reflow. - // In other words, aLine is never equals to mBlock->mLines in this function. - // so mBlock->mAscent will remain unchanged. - // Place floaters for this line into the space manager if (aLine->HasFloaters()) { // Undo border/padding translation since the nsFloaterCache's @@ -596,11 +525,11 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, nsRect r; nsFloaterCache* fc = aLine->GetFirstFloater(); while (fc) { - fc->mRegion.y += finalDeltaY; - fc->mCombinedArea.y += finalDeltaY; + fc->mRegion.y += aDeltaY; + fc->mCombinedArea.y += aDeltaY; nsIFrame* floater = fc->mPlaceholder->GetOutOfFlowFrame(); floater->GetRect(r); - floater->MoveTo(mPresContext, r.x, r.y + finalDeltaY); + floater->MoveTo(mPresContext, r.x, r.y + aDeltaY); #ifdef DEBUG if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisySpaceManager) { nscoord tx, ty; @@ -609,8 +538,8 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, printf("RecoverState: txy=%d,%d (%d,%d) ", tx, ty, mSpaceManagerX, mSpaceManagerY); nsFrame::ListTag(stdout, floater); - printf(" r.y=%d finalDeltaY=%d (sum=%d) region={%d,%d,%d,%d}\n", - r.y, finalDeltaY, r.y + finalDeltaY, + printf(" r.y=%d aDeltaY=%d (sum=%d) region={%d,%d,%d,%d}\n", + r.y, aDeltaY, r.y + aDeltaY, fc->mRegion.x, fc->mRegion.y, fc->mRegion.width, fc->mRegion.height); } @@ -627,32 +556,6 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, // And then put the translation back again mSpaceManager->Translate(bp.left, bp.top); } - - // Recover mY - mY = aLine->mBounds.YMost(); - - // Compute the damage area - if (aDamageRect) { - if (0 == finalDeltaY) { - aDamageRect->Empty(); - } else { - aLine->GetCombinedArea(&lineCombinedArea); - aDamageRect->UnionRect(oldCombinedArea, lineCombinedArea); - } - } - -// XXX Does this do anything? It doesn't seem to work.... (bug 29413) - // It's possible that the line has clear after semantics - if (!aLine->IsBlock() && aLine->HasBreak()) { - PRUint8 breakType = aLine->GetBreakType(); - switch (breakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(mY, breakType); - break; - } - } } PRBool @@ -662,7 +565,7 @@ nsBlockReflowState::IsImpactedByFloater() const printf("nsBlockReflowState::IsImpactedByFloater %p returned %d\n", this, mBand.GetFloaterCount()); #endif - return mBand.GetFloaterCount(); + return mBand.GetFloaterCount() > 0; } @@ -694,7 +597,7 @@ nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout, nsPlaceholderFrame* aPlaceholder, PRBool aInitialReflow) { - NS_PRECONDITION(nsnull != mCurrentLine, "null ptr"); + NS_PRECONDITION(mBlock->end_lines() != mCurrentLine, "null ptr"); // Allocate a nsFloaterCache for the floater nsFloaterCache* fc = mFloaterCacheFreeList.Alloc(); diff --git a/mozilla/layout/generic/nsBlockReflowState.h b/mozilla/layout/generic/nsBlockReflowState.h index cd3a544203e..718867ac895 100644 --- a/mozilla/layout/generic/nsBlockReflowState.h +++ b/mozilla/layout/generic/nsBlockReflowState.h @@ -56,12 +56,6 @@ public: ~nsBlockReflowState(); - /** - * Update our state when aLine is skipped over during incremental - * reflow. - */ - void RecoverStateFrom(nsLineBox* aLine, PRBool aPrevLineWasClean); - /** * Get the available reflow space for the current y coordinate. The * available space is relative to our coordinate system (0,0) is our @@ -107,19 +101,16 @@ public: void UpdateMaximumWidth(nscoord aMaximumWidth); - void RecoverVerticalMargins(nsLineBox* aLine, - PRBool aApplyTopMargin, - nscoord* aTopMarginResult, - nscoord* aBottomMarginResult); + // Reconstruct the previous bottom margin that goes above |aLine|. + void ReconstructMarginAbove(nsLineList::iterator aLine); void ComputeBlockAvailSpace(nsIFrame* aFrame, nsSplittableType aSplitType, const nsStyleDisplay* aDisplay, nsRect& aResult); - void RecoverStateFrom(nsLineBox* aLine, - PRBool aApplyTopMargin, - nsRect* aDamageRect); + void RecoverStateFrom(nsLineList::iterator aLine, + nscoord aDeltaY); void AdvanceToNextLine() { mLineNumber++; @@ -175,10 +166,8 @@ public: // dirty and is passed over during incremental reflow. // The current line being reflowed - nsLineBox* mCurrentLine; - - // The previous line just reflowed - nsLineBox* mPrevLine; + // If it is mBlock->end_lines(), then it is invalid. + nsLineList::iterator mCurrentLine; // The current Y coordinate in the block nscoord mY; @@ -213,7 +202,7 @@ public: nsIFrame* mNextRCFrame; // The previous child frames collapsed bottom margin value. - nscoord mPrevBottomMargin; + nsCollapsingMargin mPrevBottomMargin; // The current next-in-flow for the block. When lines are pulled // from a next-in-flow, this is used to know which next-in-flow to diff --git a/mozilla/layout/generic/nsContainerFrame.cpp b/mozilla/layout/generic/nsContainerFrame.cpp index dc3f064fed8..611acd94504 100644 --- a/mozilla/layout/generic/nsContainerFrame.cpp +++ b/mozilla/layout/generic/nsContainerFrame.cpp @@ -1069,28 +1069,28 @@ nsContainerFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) IndentBy(out, aIndent); ListTag(out); #ifdef DEBUG_waterson - fprintf(out, " [parent=%p]", mParent); + fprintf(out, " [parent=%p]", NS_STATIC_CAST(void*, mParent)); #endif nsIView* view; GetView(aPresContext, &view); if (nsnull != view) { - fprintf(out, " [view=%p]", view); + fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, view)); } if (nsnull != mNextSibling) { - fprintf(out, " next=%p", mNextSibling); + fprintf(out, " next=%p", NS_STATIC_CAST(void*, mNextSibling)); } if (nsnull != mPrevInFlow) { - fprintf(out, " prev-in-flow=%p", mPrevInFlow); + fprintf(out, " prev-in-flow=%p", NS_STATIC_CAST(void*, mPrevInFlow)); } if (nsnull != mNextInFlow) { - fprintf(out, " next-in-flow=%p", mNextInFlow); + fprintf(out, " next-in-flow=%p", NS_STATIC_CAST(void*, mNextInFlow)); } fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height); if (0 != mState) { fprintf(out, " [state=%08x]", mState); } - fprintf(out, " [content=%p]", mContent); - fprintf(out, " [sc=%p]", mStyleContext); + fprintf(out, " [content=%p]", NS_STATIC_CAST(void*, mContent)); + fprintf(out, " [sc=%p]", NS_STATIC_CAST(void*, mStyleContext)); // Output the children nsIAtom* listName = nsnull; diff --git a/mozilla/layout/generic/nsFrame.cpp b/mozilla/layout/generic/nsFrame.cpp index 3677c501e41..d91d401b537 100644 --- a/mozilla/layout/generic/nsFrame.cpp +++ b/mozilla/layout/generic/nsFrame.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -292,24 +293,22 @@ nsFrame::~nsFrame() nsresult nsFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) { - if (NULL == aInstancePtr) { - return NS_ERROR_NULL_POINTER; - } - static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); - static NS_DEFINE_IID(kClassIID, NS_GET_IID(nsIFrame)); + NS_PRECONDITION(aInstancePtr, "null out param"); #ifdef DEBUG if (aIID.Equals(NS_GET_IID(nsIFrameDebug))) { - *aInstancePtr = (void*)(nsIFrameDebug*)this; + *aInstancePtr = NS_STATIC_CAST(void*,NS_STATIC_CAST(nsIFrameDebug*,this)); return NS_OK; } #endif - if (aIID.Equals(kClassIID) || aIID.Equals(kISupportsIID)) { - *aInstancePtr = (void*)this; + if (aIID.Equals(NS_GET_IID(nsIFrame)) || + aIID.Equals(NS_GET_IID(nsISupports))) { + *aInstancePtr = NS_STATIC_CAST(void*,NS_STATIC_CAST(nsIFrame*,this)); return NS_OK; } + *aInstancePtr = nsnull; return NS_NOINTERFACE; } @@ -2143,7 +2142,7 @@ nsFrame::IsFrameTreeTooDeep(const nsHTMLReflowState& aReflowState, aMetrics.height = 0; aMetrics.ascent = 0; aMetrics.descent = 0; - aMetrics.mCarriedOutBottomMargin = 0; + aMetrics.mCarriedOutBottomMargin.Zero(); aMetrics.mOverflowArea.x = 0; aMetrics.mOverflowArea.y = 0; aMetrics.mOverflowArea.width = 0; @@ -2241,18 +2240,18 @@ nsFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) const IndentBy(out, aIndent); ListTag(out); #ifdef DEBUG_waterson - fprintf(out, " [parent=%p]", mParent); + fprintf(out, " [parent=%p]", NS_STATIC_CAST(void*, mParent)); #endif nsIView* view; GetView(aPresContext, &view); if (view) { - fprintf(out, " [view=%p]", view); + fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, view)); } fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height); if (0 != mState) { fprintf(out, " [state=%08x]", mState); } - fprintf(out, " [content=%p]", mContent); + fprintf(out, " [content=%p]", NS_STATIC_CAST(void*, mContent)); fputs("\n", out); return NS_OK; } @@ -2415,6 +2414,13 @@ nsFrame::IsVisibleForPainting(nsIPresContext * aPresContext, return rv; } +NS_IMETHODIMP +nsFrame::IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool *aResult) +{ + *aResult = PR_FALSE; + return NS_OK; +} + NS_IMETHODIMP nsFrame::GetSelectionController(nsIPresContext *aPresContext, nsISelectionController **aSelCon) { diff --git a/mozilla/layout/generic/nsFrame.h b/mozilla/layout/generic/nsFrame.h index 9bce3279770..03d44f20698 100644 --- a/mozilla/layout/generic/nsFrame.h +++ b/mozilla/layout/generic/nsFrame.h @@ -291,6 +291,10 @@ public: PRBool aCheckVis, PRBool* aIsVisible); + NS_IMETHOD IsEmpty(PRBool aIsQuirkMode, + PRBool aIsPre, + PRBool* aResult); + // nsIHTMLReflow NS_IMETHOD WillReflow(nsIPresContext* aPresContext); NS_IMETHOD Reflow(nsIPresContext* aPresContext, @@ -410,7 +414,7 @@ public: frameDebug->GetFrameName(tmp); } fputs(NS_LossyConvertUCS2toASCII(tmp).get(), out); - fprintf(out, "@%p", aFrame); + fprintf(out, "@%p", NS_STATIC_CAST(void*, aFrame)); } static void IndentBy(FILE* out, PRInt32 aIndent) { diff --git a/mozilla/layout/generic/nsGfxScrollFrame.cpp b/mozilla/layout/generic/nsGfxScrollFrame.cpp index 065afbde420..1ecf213e53f 100644 --- a/mozilla/layout/generic/nsGfxScrollFrame.cpp +++ b/mozilla/layout/generic/nsGfxScrollFrame.cpp @@ -70,9 +70,9 @@ #include "nsIGfxTextControlFrame.h" #include "nsIDOMHTMLTextAreaElement.h" -static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID); -static NS_DEFINE_IID(kScrollingViewCID, NS_SCROLLING_VIEW_CID); -static NS_DEFINE_IID(kViewCID, NS_VIEW_CID); +static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID); +static NS_DEFINE_CID(kScrollingViewCID, NS_SCROLLING_VIEW_CID); +static NS_DEFINE_CID(kViewCID, NS_VIEW_CID); diff --git a/mozilla/layout/generic/nsHTMLContainerFrame.cpp b/mozilla/layout/generic/nsHTMLContainerFrame.cpp index fb3ecbb15e7..00c88f78b90 100644 --- a/mozilla/layout/generic/nsHTMLContainerFrame.cpp +++ b/mozilla/layout/generic/nsHTMLContainerFrame.cpp @@ -64,7 +64,7 @@ #include "nsIStyleSet.h" #include "nsCOMPtr.h" -static NS_DEFINE_IID(kCChildCID, NS_CHILD_CID); +static NS_DEFINE_CID(kCChildCID, NS_CHILD_CID); NS_IMETHODIMP nsHTMLContainerFrame::Paint(nsIPresContext* aPresContext, @@ -551,7 +551,7 @@ nsHTMLContainerFrame::CreateViewForFrame(nsIPresContext* aPresContext, NS_ASSERTION(parentView, "no parent with view"); // Create a view - static NS_DEFINE_IID(kViewCID, NS_VIEW_CID); + static NS_DEFINE_CID(kViewCID, NS_VIEW_CID); nsresult result = nsComponentManager::CreateInstance(kViewCID, nsnull, diff --git a/mozilla/layout/generic/nsHTMLReflowMetrics.h b/mozilla/layout/generic/nsHTMLReflowMetrics.h index 036e4d3eeba..026b59242a0 100644 --- a/mozilla/layout/generic/nsHTMLReflowMetrics.h +++ b/mozilla/layout/generic/nsHTMLReflowMetrics.h @@ -54,6 +54,65 @@ struct nsSize; #define NS_REFLOW_CALC_BOUNDING_METRICS 0x0002 #endif +/** + * An nsCollapsingMargin represents a vertical collapsing margin between + * blocks as described in section 8.3.1 of CSS2, + * . + * + * All adjacent vertical margins collapse, and the resulting margin is + * the sum of the largest positive margin included and the smallest (most + * negative) negative margin included. + */ +struct nsCollapsingMargin { + private: + nscoord mMostPos; // the largest positive margin included + nscoord mMostNeg; // the smallest negative margin included + + public: + nsCollapsingMargin() + : mMostPos(0), + mMostNeg(0) + { + } + + nsCollapsingMargin(const nsCollapsingMargin& aOther) + : mMostPos(aOther.mMostPos), + mMostNeg(aOther.mMostNeg) + { + } + + nsCollapsingMargin& operator=(const nsCollapsingMargin& aOther) + { + mMostPos = aOther.mMostPos; + mMostNeg = aOther.mMostNeg; + return *this; + } + + void Include(nscoord aCoord) + { + if (aCoord > mMostPos) + mMostPos = aCoord; + else if (aCoord < mMostNeg) + mMostNeg = aCoord; + } + + void Zero() + { + mMostPos = 0; + mMostNeg = 0; + } + + PRBool IsZero() const + { + return (mMostPos == 0) && (mMostNeg == 0); + } + + nscoord get() const + { + return mMostPos + mMostNeg; + } +}; + /** * Reflow metrics used to return the frame's desired size and alignment * information. @@ -83,7 +142,7 @@ struct nsHTMLReflowMetrics { // Carried out bottom margin values. This is the collapsed // (generational) bottom margin value. - nscoord mCarriedOutBottomMargin; + nsCollapsingMargin mCarriedOutBottomMargin; // For frames that have content that overflow their content area // (NS_FRAME_OUTSIDE_CHILDREN) this rectangle represents the total area @@ -104,7 +163,6 @@ struct nsHTMLReflowMetrics { maxElementSize = aMaxElementSize; mMaximumWidth = 0; mFlags = aFlags; - mCarriedOutBottomMargin = 0; mOverflowArea.x = 0; mOverflowArea.y = 0; mOverflowArea.width = 0; diff --git a/mozilla/layout/generic/nsHTMLReflowState.cpp b/mozilla/layout/generic/nsHTMLReflowState.cpp index b496cdc68d9..356eb54d8e4 100644 --- a/mozilla/layout/generic/nsHTMLReflowState.cpp +++ b/mozilla/layout/generic/nsHTMLReflowState.cpp @@ -321,6 +321,7 @@ nsHTMLReflowState::DetermineFrameType(nsIFrame* aFrame, else if (NS_STYLE_FLOAT_NONE != aDisplay->mFloats) { frameType = NS_CSS_FRAME_TYPE_FLOATING; } + // XXXldb UMR in this case (else, else) we don't initialize frameType } else { switch (aDisplay->mDisplay) { @@ -826,16 +827,14 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsIPresContext* aPresContext, // the line containing the placeholder frame if (aBlockFrame) { nsIFrame* blockChild; - nsLineBox* lineBox; - nsLineBox* prevLineBox; + nsBlockFrame::line_iterator lineBox; PRBool isFloater; + nsBlockFrame* blockFrame = NS_STATIC_CAST(nsBlockFrame*, aBlockFrame); // We need the immediate child of the block frame, and that may not be // the placeholder frame blockChild = FindImmediateChildOf(aBlockFrame, aPlaceholderFrame); - lineBox = ((nsBlockFrame*)aBlockFrame)->FindLineFor(blockChild, &prevLineBox, - &isFloater); - if (lineBox) { + if (blockFrame->FindLineFor(blockChild, &isFloater, &lineBox)) { // The top of the hypothetical box is just below the line containing // the placeholder aHypotheticalBox.mTop = lineBox->mBounds.YMost(); diff --git a/mozilla/layout/generic/nsIFrame.h b/mozilla/layout/generic/nsIFrame.h index ce08c45d8ea..c042b2279e4 100644 --- a/mozilla/layout/generic/nsIFrame.h +++ b/mozilla/layout/generic/nsIFrame.h @@ -1116,6 +1116,21 @@ public: nsIRenderingContext& aRenderingContext, PRBool aCheckVis, PRBool* aIsVisible) = 0; + + /** + * Determine whether the frame is logically empty, i.e., whether the + * layout would be the same whether or not the frame is present. + * Placeholder frames should return true. Block frames should be + * considered empty whenever margins collapse through them, even + * though those margins are relevant. + * + * aIsPre should be ignored by frames to which the 'white-space' + * property applies. + */ + NS_IMETHOD IsEmpty(PRBool aIsQuirkMode, + PRBool aIsPre, + PRBool* aResult) = 0; + #ifdef IBMBIDI /** * retrieve and set Bidi property of this frame diff --git a/mozilla/layout/generic/nsILineIterator.h b/mozilla/layout/generic/nsILineIterator.h index 0818190080a..4ec1c9fb607 100644 --- a/mozilla/layout/generic/nsILineIterator.h +++ b/mozilla/layout/generic/nsILineIterator.h @@ -64,12 +64,6 @@ // clear, it means that the line contains inline elements. #define NS_LINE_FLAG_IS_BLOCK 0x1 -// This bit, when set, indicates that the line has had a trailing -// white-space compressed. The bit is only set on lines that contain -// inline elements, and is only set when actual white-space is -// compressed. -#define NS_LINE_FLAG_IS_TRIMMED 0x2 - // This bit is set when the line ends in some sort of break. #define NS_LINE_FLAG_ENDS_IN_BREAK 0x4 diff --git a/mozilla/layout/generic/nsInlineFrame.cpp b/mozilla/layout/generic/nsInlineFrame.cpp index 18c2474145b..6157fda6f88 100644 --- a/mozilla/layout/generic/nsInlineFrame.cpp +++ b/mozilla/layout/generic/nsInlineFrame.cpp @@ -111,6 +111,84 @@ nsInlineFrame::GetFrameType(nsIAtom** aType) const return NS_OK; } +inline PRBool +IsBorderZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) +{ + return ((aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0)); +} + +inline PRBool +IsPaddingZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) +{ + return (aUnit == eStyleUnit_Null || + (aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) || + (aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0)); +} + +inline PRBool +IsMarginZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) +{ + return (aUnit == eStyleUnit_Null || + aUnit == eStyleUnit_Auto || + (aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) || + (aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0)); +} + +NS_IMETHODIMP +nsInlineFrame::IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool* aResult) +{ + if (!aIsQuirkMode) { + *aResult = PR_FALSE; + return NS_OK; + } + const nsStyleMargin* margin = NS_STATIC_CAST(const nsStyleMargin*, + mStyleContext->GetStyleData(eStyleStruct_Margin)); + const nsStyleBorder* border = NS_STATIC_CAST(const nsStyleBorder*, + mStyleContext->GetStyleData(eStyleStruct_Border)); + const nsStylePadding* padding = NS_STATIC_CAST(const nsStylePadding*, + mStyleContext->GetStyleData(eStyleStruct_Padding)); + nsStyleCoord coord; + if ((border->IsBorderSideVisible(NS_SIDE_TOP) && + !IsBorderZero(border->mBorder.GetTopUnit(), + border->mBorder.GetTop(coord))) || + (border->IsBorderSideVisible(NS_SIDE_RIGHT) && + !IsBorderZero(border->mBorder.GetTopUnit(), + border->mBorder.GetTop(coord))) || + (border->IsBorderSideVisible(NS_SIDE_BOTTOM) && + !IsBorderZero(border->mBorder.GetTopUnit(), + border->mBorder.GetTop(coord))) || + (border->IsBorderSideVisible(NS_SIDE_LEFT) && + !IsBorderZero(border->mBorder.GetTopUnit(), + border->mBorder.GetTop(coord))) || + !IsPaddingZero(padding->mPadding.GetTopUnit(), + padding->mPadding.GetTop(coord)) || + !IsPaddingZero(padding->mPadding.GetRightUnit(), + padding->mPadding.GetRight(coord)) || + !IsPaddingZero(padding->mPadding.GetBottomUnit(), + padding->mPadding.GetBottom(coord)) || + !IsPaddingZero(padding->mPadding.GetLeftUnit(), + padding->mPadding.GetLeft(coord)) || + !IsMarginZero(margin->mMargin.GetTopUnit(), + margin->mMargin.GetTop(coord)) || + !IsMarginZero(margin->mMargin.GetRightUnit(), + margin->mMargin.GetRight(coord)) || + !IsMarginZero(margin->mMargin.GetBottomUnit(), + margin->mMargin.GetBottom(coord)) || + !IsMarginZero(margin->mMargin.GetLeftUnit(), + margin->mMargin.GetLeft(coord))) { + *aResult = PR_FALSE; + return NS_OK; + } + + *aResult = PR_TRUE; + for (nsIFrame *kid = mFrames.FirstChild(); kid; kid->GetNextSibling(&kid)) { + kid->IsEmpty(aIsQuirkMode, aIsPre, aResult); + if (! *aResult) + break; + } + return NS_OK; +} + NS_IMETHODIMP nsInlineFrame::AppendFrames(nsIPresContext* aPresContext, nsIPresShell& aPresShell, diff --git a/mozilla/layout/generic/nsInlineFrame.h b/mozilla/layout/generic/nsInlineFrame.h index 94db405a703..74033deaef6 100644 --- a/mozilla/layout/generic/nsInlineFrame.h +++ b/mozilla/layout/generic/nsInlineFrame.h @@ -90,6 +90,8 @@ public: #endif NS_IMETHOD GetFrameType(nsIAtom** aType) const; + NS_IMETHOD IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool* aResult); + // nsIHTMLReflow overrides NS_IMETHOD Reflow(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, diff --git a/mozilla/layout/generic/nsIntervalSet.cpp b/mozilla/layout/generic/nsIntervalSet.cpp new file mode 100644 index 00000000000..32cffc0d0b7 --- /dev/null +++ b/mozilla/layout/generic/nsIntervalSet.cpp @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +// vim:cindent:ts=8:et:sw=4: +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is nsIntervalSet. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by the Initial Developer are Copyright + * (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * L. David Baron (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIntervalSet.h" +#include + +nsIntervalSet::nsIntervalSet(IntervalSetAlloc aAlloc, IntervalSetFree aFree, + void* aAllocatorClosure) + : mList(nsnull), + mAlloc(aAlloc), + mFree(aFree), + mAllocatorClosure(aAllocatorClosure) +{ + NS_ASSERTION(mAlloc && mFree, "null callback params"); +} + +nsIntervalSet::~nsIntervalSet() +{ + Interval *current = mList; + while (current) { + Interval *trash = current; + current = current->mNext; + FreeInterval(trash); + } +} + +void nsIntervalSet::FreeInterval(nsIntervalSet::Interval *aInterval) +{ + NS_ASSERTION(aInterval, "null interval"); + + aInterval->Interval::~Interval(); + (*mFree)(sizeof(Interval), aInterval, mAllocatorClosure); +} + +void nsIntervalSet::IncludeInterval(coord_type aBegin, coord_type aEnd) +{ + Interval *newInterval = NS_STATIC_CAST(Interval*, + (*mAlloc)(sizeof(Interval), mAllocatorClosure)); + if (!newInterval) { + NS_NOTREACHED("allocation failure"); + return; + } + new(newInterval) Interval(aBegin, aEnd); + + Interval **current = &mList; + while (*current && (*current)->mEnd < aBegin) + current = &(*current)->mNext; + + newInterval->mNext = *current; + *current = newInterval; + + Interval *subsumed = newInterval->mNext; + while (subsumed && subsumed->mBegin <= aEnd) { + newInterval->mEnd = PR_MAX(newInterval->mEnd, subsumed->mEnd); + newInterval->mNext = subsumed->mNext; + FreeInterval(subsumed); + subsumed = newInterval->mNext; + } +} + +PRBool nsIntervalSet::HasPoint(coord_type aPoint) const +{ + Interval *current = mList; + while (current && current->mBegin <= aPoint) { + if (current->mEnd >= aPoint) + return PR_TRUE; + current = current->mNext; + } + return PR_FALSE; +} + +PRBool nsIntervalSet::Intersects(coord_type aBegin, coord_type aEnd) const +{ + Interval *current = mList; + while (current && current->mBegin <= aEnd) { + if (current->mEnd >= aBegin) + return PR_TRUE; + current = current->mNext; + } + return PR_FALSE; +} + +PRBool nsIntervalSet::Contains(coord_type aBegin, coord_type aEnd) const +{ + Interval *current = mList; + while (current && current->mBegin <= aBegin) { + if (current->mEnd >= aEnd) + return PR_TRUE; + current = current->mNext; + } + return PR_FALSE; +} diff --git a/mozilla/layout/generic/nsIntervalSet.h b/mozilla/layout/generic/nsIntervalSet.h new file mode 100644 index 00000000000..e0f8a7bfbc0 --- /dev/null +++ b/mozilla/layout/generic/nsIntervalSet.h @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +// vim:cindent:ts=8:et:sw=4: +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is nsIntervalSet. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by the Initial Developer are Copyright + * (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * L. David Baron (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsIntervalSet_h___ +#define nsIntervalSet_h___ + +#include "prtypes.h" +#include "nsCoord.h" + +typedef void * +(* PR_CALLBACK IntervalSetAlloc)(size_t aBytes, void *aClosure); + +typedef void +(* PR_CALLBACK IntervalSetFree) (size_t aBytes, void *aPtr, void *aClosure); + +/* + * A list-based class (hopefully tree-based when I get around to it) + * for representing a set of ranges on a number-line. + */ +class nsIntervalSet { + +public: + + typedef nscoord coord_type; + + nsIntervalSet(IntervalSetAlloc aAlloc, IntervalSetFree aFree, + void* aAllocatorClosure); + ~nsIntervalSet(); + + /* + * Include the interval [aBegin, aEnd] in the set. + * + * Removal of intervals added is not supported because that would + * require keeping track of the individual intervals that were + * added (nsIntervalMap should do that). It would be simple to + * implement ExcludeInterval if anyone wants it, though. + */ + void IncludeInterval(coord_type aBegin, coord_type aEnd); + + /* + * Is the point aPoint contained within the set of intervals? + */ + PRBool HasPoint(coord_type aPoint) const; + + /* + * Are _some_ points in [aBegin, aEnd] contained within the set + * of intervals? + */ + PRBool Intersects(coord_type aBegin, coord_type aEnd) const; + + /* + * Are _all_ points in [aBegin, aEnd] contained within the set + * of intervals? + */ + PRBool Contains(coord_type aBegin, coord_type aEnd) const; + + PRBool IsEmpty() const + { + return !mList; + } + +private: + + class Interval { + + public: + Interval(coord_type aBegin, coord_type aEnd) + : mBegin(aBegin), + mEnd(aEnd), + mPrev(nsnull), + mNext(nsnull) + { + } + + coord_type mBegin; + coord_type mEnd; + Interval *mPrev; + Interval *mNext; + }; + + void FreeInterval(Interval *aInterval); + + Interval *mList; + IntervalSetAlloc mAlloc; + IntervalSetFree mFree; + void *mAllocatorClosure; + +}; + +#endif // !defined(nsIntervalSet_h___) diff --git a/mozilla/layout/generic/nsLineBox.cpp b/mozilla/layout/generic/nsLineBox.cpp index c7c5779444b..091cdbb31a9 100644 --- a/mozilla/layout/generic/nsLineBox.cpp +++ b/mozilla/layout/generic/nsLineBox.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -20,6 +21,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * L. David Baron * Pierre Phaneuf * * Alternatively, the contents of this file may be used under the terms of @@ -40,10 +42,12 @@ #include "nsIStyleContext.h" #include "nsLineLayout.h" #include "prprf.h" +#include "nsBlockFrame.h" +#include "nsITextContent.h" +#include "nsLayoutAtoms.h" #ifdef DEBUG #include "nsISizeOfHandler.h" -#include "nsLayoutAtoms.h" #endif #ifdef DEBUG @@ -55,11 +59,10 @@ MOZ_DECL_CTOR_COUNTER(nsLineBox) nsLineBox::nsLineBox(nsIFrame* aFrame, PRInt32 aCount, PRBool aIsBlock) : mFirstChild(aFrame), - mNext(nsnull), mBounds(0, 0, 0, 0), mMaxElementWidth(0), - mData(nsnull), - mMaximumWidth(-1) + mMaximumWidth(-1), + mData(nsnull) { MOZ_COUNT_CTOR(nsLineBox); #ifdef DEBUG @@ -139,7 +142,7 @@ ListFloaters(FILE* out, PRInt32 aIndent, const nsFloaterCacheList& aFloaters) nsFrame::IndentBy(out, aIndent); nsPlaceholderFrame* ph = fc->mPlaceholder; if (nsnull != ph) { - fprintf(out, "placeholder@%p ", ph); + fprintf(out, "placeholder@%p ", NS_STATIC_CAST(void*, ph)); nsIFrame* frame = ph->GetOutOfFlowFrame(); if (nsnull != frame) { nsIFrameDebug* frameDebug; @@ -163,19 +166,19 @@ ListFloaters(FILE* out, PRInt32 aIndent, const nsFloaterCacheList& aFloaters) } #endif +#ifdef DEBUG char* nsLineBox::StateToString(char* aBuf, PRInt32 aBufSize) const { PR_snprintf(aBuf, aBufSize, "%s,%s,%s,%s[0x%x]", IsBlock() ? "block" : "inline", IsDirty() ? "dirty" : "clean", + IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean", IsImpactedByFloater() ? "IMPACTED" : "NOT Impacted", - IsTrimmed() ? "trimmed" : "", mAllFlags); return aBuf; } -#ifdef DEBUG void nsLineBox::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) const { @@ -184,9 +187,10 @@ nsLineBox::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) const for (i = aIndent; --i >= 0; ) fputs(" ", out); char cbuf[100]; fprintf(out, "line %p: count=%d state=%s ", - this, GetChildCount(), StateToString(cbuf, sizeof(cbuf))); - if (0 != GetCarriedOutBottomMargin()) { - fprintf(out, "bm=%d ", GetCarriedOutBottomMargin()); + NS_STATIC_CAST(const void*, this), GetChildCount(), + StateToString(cbuf, sizeof(cbuf))); + if (IsBlock() && !GetCarriedOutBottomMargin().IsZero()) { + fprintf(out, "bm=%d ", GetCarriedOutBottomMargin().get()); } if (0 != mMaxElementWidth) { fprintf(out, "mew=%d ", mMaxElementWidth); @@ -253,67 +257,103 @@ nsLineBox::IndexOf(nsIFrame* aFrame) const return -1; } -void -nsLineBox::DeleteLineList(nsIPresContext* aPresContext, nsLineBox* aLine) +nsresult +nsLineBox::IsEmpty(PRBool aIsQuirkMode, PRBool aParentIsPre, + PRBool *aResult) const { - if (nsnull != aLine) { + if (IsBlock()) + return mFirstChild->IsEmpty(aIsQuirkMode, aParentIsPre, aResult); + + *aResult = PR_TRUE; + PRInt32 n; + nsIFrame *kid; + for (n = GetChildCount(), kid = mFirstChild; + n > 0; + --n, kid->GetNextSibling(&kid)) + { + kid->IsEmpty(aIsQuirkMode, aParentIsPre, aResult); + if (! *aResult) + break; + } + return NS_OK; +} + +void +nsLineBox::DeleteLineList(nsIPresContext* aPresContext, nsLineList& aLines) +{ + if (! aLines.empty()) { // Delete our child frames before doing anything else. In particular // we do all of this before our base class releases it's hold on the // view. - for (nsIFrame* child = aLine->mFirstChild; child; ) { + for (nsIFrame* child = aLines.front()->mFirstChild; child; ) { nsIFrame* nextChild; child->GetNextSibling(&nextChild); child->Destroy(aPresContext); child = nextChild; } - while (nsnull != aLine) { - nsLineBox* next = aLine->mNext; - delete aLine; - aLine = next; + while (! aLines.empty()) { + nsLineBox* line = aLines.front(); + aLines.pop_front(); + delete line; } } } nsLineBox* -nsLineBox::LastLine(nsLineBox* aLine) -{ - if (nsnull != aLine) { - while (nsnull != aLine->mNext) { - aLine = aLine->mNext; - } - } - return aLine; -} - -nsLineBox* -nsLineBox::FindLineContaining(nsLineBox* aLine, nsIFrame* aFrame, +nsLineBox::FindLineContaining(nsLineList& aLines, nsIFrame* aFrame, PRInt32* aFrameIndexInLine) { - NS_PRECONDITION(aFrameIndexInLine && aLine && aFrame, "null ptr"); - while (nsnull != aLine) { - PRInt32 ix = aLine->IndexOf(aFrame); + NS_PRECONDITION(aFrameIndexInLine && !aLines.empty() && aFrame, "null ptr"); + for (nsLineList::iterator line = aLines.begin(), + line_end = aLines.end(); + line != line_end; + ++line) + { + PRInt32 ix = line->IndexOf(aFrame); if (ix >= 0) { *aFrameIndexInLine = ix; - return aLine; + return line; } - aLine = aLine->mNext; } *aFrameIndexInLine = -1; return nsnull; } -nscoord +PRBool +nsLineBox::RFindLineContaining(nsIFrame* aFrame, + const nsLineList::iterator& aBegin, + nsLineList::iterator& aEnd, + PRInt32* aFrameIndexInLine) +{ + NS_PRECONDITION(aFrame, "null ptr"); + while (aBegin != aEnd) { + --aEnd; + PRInt32 ix = aEnd->IndexOf(aFrame); + if (ix >= 0) { + *aFrameIndexInLine = ix; + return PR_TRUE; + } + } + *aFrameIndexInLine = -1; + return PR_FALSE; +} + +nsCollapsingMargin nsLineBox::GetCarriedOutBottomMargin() const { - return (IsBlock() && mBlockData) ? mBlockData->mCarriedOutBottomMargin : 0; + NS_ASSERTION(IsBlock(), + "GetCarriedOutBottomMargin called on non-block line."); + return (IsBlock() && mBlockData) + ? mBlockData->mCarriedOutBottomMargin + : nsCollapsingMargin(); } void -nsLineBox::SetCarriedOutBottomMargin(nscoord aValue) +nsLineBox::SetCarriedOutBottomMargin(nsCollapsingMargin aValue) { if (IsBlock()) { - if (aValue) { + if (! aValue.IsZero()) { if (!mBlockData) { mBlockData = new ExtraBlockData(mBounds); } @@ -338,7 +378,7 @@ nsLineBox::MaybeFreeData() mInlineData = nsnull; } } - else if (0 == mBlockData->mCarriedOutBottomMargin) { + else if (mBlockData->mCarriedOutBottomMargin.IsZero()) { delete mBlockData; mBlockData = nsnull; } @@ -462,17 +502,6 @@ nsLineBox::GetCombinedArea(nsRect* aResult) } #ifdef DEBUG -PRInt32 -nsLineBox::ListLength(nsLineBox* aLine) -{ - PRInt32 count = 0; - while (aLine) { - count++; - aLine = aLine->mNext; - } - return count; -} - nsIAtom* nsLineBox::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const { @@ -533,17 +562,12 @@ nsLineIterator::~nsLineIterator() NS_IMPL_ISUPPORTS2(nsLineIterator, nsILineIterator, nsILineIteratorNavigator) nsresult -nsLineIterator::Init(nsLineBox* aLines, PRBool aRightToLeft) +nsLineIterator::Init(nsLineList& aLines, PRBool aRightToLeft) { mRightToLeft = aRightToLeft; // Count the lines - PRInt32 numLines = 0; - nsLineBox* line = aLines; - while (line) { - numLines++; - line = line->mNext; - } + PRInt32 numLines = aLines.size(); if (0 == numLines) { // Use gDummyLines so that we don't need null pointer checks in // the accessor methods @@ -560,10 +584,11 @@ nsLineIterator::Init(nsLineBox* aLines, PRBool aRightToLeft) return NS_ERROR_OUT_OF_MEMORY; } nsLineBox** lp = mLines; - line = aLines; - while (line) { + for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end() ; + line != line_end; + ++line) + { *lp++ = line; - line = line->mNext; } mNumLines = numLines; return NS_OK; @@ -618,8 +643,6 @@ nsLineIterator::GetLine(PRInt32 aLineNumber, flags |= NS_LINE_FLAG_IS_BLOCK; } else { - if (line->IsTrimmed()) - flags |= NS_LINE_FLAG_IS_TRIMMED; if (line->HasBreak()) flags |= NS_LINE_FLAG_ENDS_IN_BREAK; } @@ -634,13 +657,12 @@ nsLineIterator::FindLineContaining(nsIFrame* aFrame, { nsLineBox* line = mLines[0]; PRInt32 lineNumber = 0; - while (line) { + while (lineNumber != mNumLines) { if (line->Contains(aFrame)) { *aLineNumberResult = lineNumber; return NS_OK; } - line = line->mNext; - lineNumber++; + line = mLines[++lineNumber]; } *aLineNumberResult = -1; return NS_OK; @@ -656,13 +678,12 @@ nsLineIterator::FindLineAt(nscoord aY, return NS_OK; } PRInt32 lineNumber = 0; - while (line) { + while (lineNumber != mNumLines) { if ((aY >= line->mBounds.y) && (aY < line->mBounds.YMost())) { *aLineNumberResult = lineNumber; return NS_OK; } - line = line->mNext; - lineNumber++; + line = mLines[++lineNumber]; } *aLineNumberResult = mNumLines; return NS_OK; @@ -686,7 +707,7 @@ nsLineIterator::CheckLineOrder(PRInt32 aLine, PRInt32 lineFrameCount; PRUint32 lineFlags; - nsresult result; + nsresult result = NS_OK; // an RTL paragraph is always considered as reordered // in an LTR paragraph, find out by examining the coordinates of each frame in the line diff --git a/mozilla/layout/generic/nsLineBox.h b/mozilla/layout/generic/nsLineBox.h index 4f1526d4bac..3c823ab8612 100644 --- a/mozilla/layout/generic/nsLineBox.h +++ b/mozilla/layout/generic/nsLineBox.h @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -20,6 +21,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * L. David Baron * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -161,12 +163,45 @@ need to rearrange the mBits bitfield; nsLineBox* NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame, PRInt32 aCount, PRBool aIsBlock); +class nsLineList; + +// don't use the following names outside of this file. Instead, use +// nsLineList::iterator, etc. These are just here to allow them to +// be specified as parameters to methods of nsLineBox. +class nsLineList_iterator; +class nsLineList_const_iterator; +class nsLineList_reverse_iterator; +class nsLineList_const_reverse_iterator; + +/** + * Users must have the class that is to be part of the list inherit + * from nsLineLink. If they want to be efficient, it should be the + * first base class. (This was originally nsCLink in a templatized + * nsCList, but it's still useful separately.) + */ + +class nsLineLink { + + public: + friend class nsLineList; + friend class nsLineList_iterator; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_iterator; + friend class nsLineList_const_reverse_iterator; + + private: + nsLineLink *_mNext; // or head + nsLineLink *_mPrev; // or tail + +}; + + /** * The nsLineBox class represents a horizontal line of frames. It contains * enough state to support incremental reflow of the frames, event handling * for the frames, and rendering of the frames. */ -class nsLineBox { +class nsLineBox : public nsLineLink { private: nsLineBox(nsIFrame* aFrame, PRInt32 aCount, PRBool aIsBlock); ~nsLineBox(); @@ -202,6 +237,17 @@ public: return mFlags.mDirty; } + // mPreviousMarginDirty bit + void MarkPreviousMarginDirty() { + mFlags.mPreviousMarginDirty = 1; + } + void ClearPreviousMarginDirty() { + mFlags.mPreviousMarginDirty = 0; + } + PRBool IsPreviousMarginDirty() const { + return mFlags.mPreviousMarginDirty; + } + // mImpactedByFloater bit void SetLineIsImpactedByFloater(PRBool aValue) { NS_ASSERTION((PR_FALSE==aValue || PR_TRUE==aValue), "somebody is playing fast and loose with bools and bits!"); @@ -211,15 +257,6 @@ public: return mFlags.mImpactedByFloater; } - // mTrimmed bit - void SetTrimmed(PRBool aOn) { - NS_ASSERTION((PR_FALSE==aOn || PR_TRUE==aOn), "somebody is playing fast and loose with bools and bits!"); - mFlags.mTrimmed = aOn; - } - PRBool IsTrimmed() const { - return mFlags.mTrimmed; - } - // mHasPercentageChild bit void SetHasPercentageChild(PRBool aOn) { NS_ASSERTION((PR_FALSE==aOn || PR_TRUE==aOn), "somebody is playing fast and loose with bools and bits!"); @@ -285,8 +322,8 @@ public: } // mCarriedOutBottomMargin value - nscoord GetCarriedOutBottomMargin() const; - void SetCarriedOutBottomMargin(nscoord aValue); + nsCollapsingMargin GetCarriedOutBottomMargin() const; + void SetCarriedOutBottomMargin(nsCollapsingMargin aValue); // mFloaters PRBool HasFloaters() const { @@ -320,14 +357,24 @@ public: return mBounds.height; } - static void DeleteLineList(nsIPresContext* aPresContext, nsLineBox* aLine); + static void DeleteLineList(nsIPresContext* aPresContext, nsLineList& aLines); - static nsLineBox* LastLine(nsLineBox* aLine); - - static nsLineBox* FindLineContaining(nsLineBox* aLine, nsIFrame* aFrame, + // search from beginning to end + // XXX Should switch to API below + static nsLineBox* FindLineContaining(nsLineList& aLines, nsIFrame* aFrame, PRInt32* aFrameIndexInLine); + // search from end to beginning of [aBegin, aEnd) + // Returns PR_TRUE if it found the line and PR_FALSE if not. + // Moves aEnd as it searches so that aEnd points to the resulting line. + static PRBool RFindLineContaining(nsIFrame* aFrame, + const nsLineList_iterator& aBegin, + nsLineList_iterator& aEnd, + PRInt32* aFrameIndexInLine); + #ifdef DEBUG + char* StateToString(char* aBuf, PRInt32 aBufSize) const; + void List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) const; #endif @@ -335,22 +382,22 @@ public: PRBool IsLastChild(nsIFrame* aFrame) const; - char* StateToString(char* aBuf, PRInt32 aBufSize) const; - PRInt32 IndexOf(nsIFrame* aFrame) const; PRBool Contains(nsIFrame* aFrame) const { return IndexOf(aFrame) >= 0; } + // whether the line box is "logically" empty (just like nsIFrame::IsEmpty) + nsresult IsEmpty(PRBool aIsQuirkMode, PRBool aParentIsPre, + PRBool *aResult) const; + #ifdef DEBUG nsIAtom* SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const; static PRInt32 GetCtorCount(); - static PRInt32 ListLength(nsLineBox* aLine); #endif nsIFrame* mFirstChild; - nsLineBox* mNext; nsRect mBounds; nscoord mMaxElementWidth; // width part of max-element-size @@ -358,13 +405,13 @@ public: struct FlagBits { PRUint32 mDirty : 1; + PRUint32 mPreviousMarginDirty : 1; PRUint32 mBlock : 1; PRUint32 mImpactedByFloater : 1; - PRUint32 mTrimmed : 1; PRUint32 mHasPercentageChild : 1; PRUint32 mLineWrapped: 1; PRUint32 mForceInvalidate: 1; // default 0 = means this line handles it's own invalidation. 1 = always invalidate this entire line - PRUint32 mResizeReflowOptimizationDisabled: 1; // default 0 = means that the opt potentially applies to this line. 1 = never skip reflowing this line for a resize reflow + PRUint32 mResizeReflowOptimizationDisabled: 1; // default 0 = means that the opt potentially applies to this line. 1 = never skip reflowing this line for a resize reflow PRUint32 mBreakType : 4; PRUint32 mChildCount : 20; @@ -377,10 +424,12 @@ public: }; struct ExtraBlockData : public ExtraData { - ExtraBlockData(const nsRect& aBounds) : ExtraData(aBounds) { - mCarriedOutBottomMargin = 0; + ExtraBlockData(const nsRect& aBounds) + : ExtraData(aBounds), + mCarriedOutBottomMargin() + { } - nscoord mCarriedOutBottomMargin; + nsCollapsingMargin mCarriedOutBottomMargin; }; struct ExtraInlineData : public ExtraData { @@ -405,6 +454,978 @@ protected: void MaybeFreeData(); }; +#ifdef DEBUG +#define NS_LINELIST_DEBUG_PASS_END +#else +#undef NS_LINELIST_DEBUG_PASS_END +#endif + +/** + * A linked list type where the items in the list must inherit from + * a link type to fuse allocations. + * + * API heavily based on the |list| class in the C++ standard. + */ + +class nsLineList_iterator { + public: + friend class nsLineList; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_iterator; + friend class nsLineList_const_reverse_iterator; + + typedef nsLineList_iterator iterator_self_type; + typedef nsLineList_reverse_iterator iterator_reverse_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef PRUint32 size_type; + typedef PRInt32 difference_type; + + typedef nsLineLink link_type; + + // Auto generated default constructor OK. + // Auto generated copy-constructor OK. + + inline iterator_self_type& + operator=(const iterator_self_type& aOther); + inline iterator_self_type& + operator=(const iterator_reverse_type& aOther); + + iterator_self_type& operator++() + { + mCurrent = mCurrent->_mNext; + return *this; + } + + iterator_self_type operator++(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mNext; + return rv; + } + + iterator_self_type& operator--() + { + mCurrent = mCurrent->_mPrev; + return *this; + } + + iterator_self_type operator--(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mPrev; + return rv; + } + + reference operator*() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return *NS_STATIC_CAST(pointer, mCurrent); + } + + pointer operator->() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(pointer, mCurrent); + } + + pointer get() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(pointer, mCurrent); + } + + operator pointer() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(pointer, mCurrent); + } + + const_reference operator*() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return *NS_STATIC_CAST(const_pointer, mCurrent); + } + + const_pointer operator->() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } + +#ifndef __MWERKS__ + operator const_pointer() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } +#endif /* !__MWERKS__ */ + + iterator_self_type next() + { + iterator_self_type copy(*this); + return ++copy; + } + + const iterator_self_type next() const + { + iterator_self_type copy(*this); + return ++copy; + } + + iterator_self_type prev() + { + iterator_self_type copy(*this); + return --copy; + } + + const iterator_self_type prev() const + { + iterator_self_type copy(*this); + return --copy; + } + + PRBool operator==(const iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(const iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + + private: + link_type *mCurrent; +#ifdef NS_LINELIST_DEBUG_PASS_END + link_type *mListLink; // the list's link, i.e., the end +#endif +}; + +class nsLineList_reverse_iterator { + + public: + + friend class nsLineList; + friend class nsLineList_iterator; + friend class nsLineList_const_iterator; + friend class nsLineList_const_reverse_iterator; + + typedef nsLineList_reverse_iterator iterator_self_type; + typedef nsLineList_iterator iterator_reverse_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef PRUint32 size_type; + typedef PRInt32 difference_type; + + typedef nsLineLink link_type; + + // Auto generated default constructor OK. + // Auto generated copy-constructor OK. + + inline iterator_self_type& + operator=(const iterator_reverse_type& aOther); + inline iterator_self_type& + operator=(const iterator_self_type& aOther); + + iterator_self_type& operator++() + { + mCurrent = mCurrent->_mPrev; + return *this; + } + + iterator_self_type operator++(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mPrev; + return rv; + } + + iterator_self_type& operator--() + { + mCurrent = mCurrent->_mNext; + return *this; + } + + iterator_self_type operator--(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mNext; + return rv; + } + + reference operator*() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return *NS_STATIC_CAST(pointer, mCurrent); + } + + pointer operator->() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(pointer, mCurrent); + } + + pointer get() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(pointer, mCurrent); + } + + operator pointer() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(pointer, mCurrent); + } + + const_reference operator*() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return *NS_STATIC_CAST(const_pointer, mCurrent); + } + + const_pointer operator->() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } + +#ifndef __MWERKS__ + operator const_pointer() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } +#endif /* !__MWERKS__ */ + + PRBool operator==(const iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(const iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + + private: + link_type *mCurrent; +#ifdef NS_LINELIST_DEBUG_PASS_END + link_type *mListLink; // the list's link, i.e., the end +#endif +}; + +class nsLineList_const_iterator { + public: + + friend class nsLineList; + friend class nsLineList_iterator; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_reverse_iterator; + + typedef nsLineList_const_iterator iterator_self_type; + typedef nsLineList_const_reverse_iterator iterator_reverse_type; + typedef nsLineList_iterator iterator_nonconst_type; + typedef nsLineList_reverse_iterator iterator_nonconst_reverse_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef PRUint32 size_type; + typedef PRInt32 difference_type; + + typedef nsLineLink link_type; + + // Auto generated default constructor OK. + // Auto generated copy-constructor OK. + + inline iterator_self_type& + operator=(const iterator_nonconst_type& aOther); + inline iterator_self_type& + operator=(const iterator_nonconst_reverse_type& aOther); + inline iterator_self_type& + operator=(const iterator_self_type& aOther); + inline iterator_self_type& + operator=(const iterator_reverse_type& aOther); + + iterator_self_type& operator++() + { + mCurrent = mCurrent->_mNext; + return *this; + } + + iterator_self_type operator++(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mNext; + return rv; + } + + iterator_self_type& operator--() + { + mCurrent = mCurrent->_mPrev; + return *this; + } + + iterator_self_type operator--(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mPrev; + return rv; + } + + const_reference operator*() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return *NS_STATIC_CAST(const_pointer, mCurrent); + } + + const_pointer operator->() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } + + const_pointer get() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } + +#ifndef __MWERKS__ + operator const_pointer() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } +#endif /* !__MWERKS__ */ + + const iterator_self_type next() const + { + iterator_self_type copy(*this); + return ++copy; + } + + const iterator_self_type prev() const + { + iterator_self_type copy(*this); + return --copy; + } + + PRBool operator==(const iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(const iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + + private: + const link_type *mCurrent; +#ifdef NS_LINELIST_DEBUG_PASS_END + const link_type *mListLink; // the list's link, i.e., the end +#endif +}; + +class nsLineList_const_reverse_iterator { + public: + + friend class nsLineList; + friend class nsLineList_iterator; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_iterator; + + typedef nsLineList_const_reverse_iterator iterator_self_type; + typedef nsLineList_const_iterator iterator_reverse_type; + typedef nsLineList_iterator iterator_nonconst_reverse_type; + typedef nsLineList_reverse_iterator iterator_nonconst_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef PRUint32 size_type; + typedef PRInt32 difference_type; + + typedef nsLineLink link_type; + + // Auto generated default constructor OK. + // Auto generated copy-constructor OK. + + inline iterator_self_type& + operator=(const iterator_nonconst_type& aOther); + inline iterator_self_type& + operator=(const iterator_nonconst_reverse_type& aOther); + inline iterator_self_type& + operator=(const iterator_self_type& aOther); + inline iterator_self_type& + operator=(const iterator_reverse_type& aOther); + + iterator_self_type& operator++() + { + mCurrent = mCurrent->_mPrev; + return *this; + } + + iterator_self_type operator++(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mPrev; + return rv; + } + + iterator_self_type& operator--() + { + mCurrent = mCurrent->_mNext; + return *this; + } + + iterator_self_type operator--(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mNext; + return rv; + } + + const_reference operator*() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return *NS_STATIC_CAST(const_pointer, mCurrent); + } + + const_pointer operator->() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } + + const_pointer get() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } + +#ifndef __MWERKS__ + operator const_pointer() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } +#endif /* !__MWERKS__ */ + + PRBool operator==(const iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(const iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + +//private: + const link_type *mCurrent; +#ifdef NS_LINELIST_DEBUG_PASS_END + const link_type *mListLink; // the list's link, i.e., the end +#endif +}; + +class nsLineList { + + public: + + friend class nsLineList_iterator; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_iterator; + friend class nsLineList_const_reverse_iterator; + + typedef PRUint32 size_type; + typedef PRInt32 difference_type; + + typedef nsLineLink link_type; + + private: + link_type mLink; + + public: + typedef nsLineList self_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef nsLineList_iterator iterator; + typedef nsLineList_reverse_iterator reverse_iterator; + typedef nsLineList_const_iterator const_iterator; + typedef nsLineList_const_reverse_iterator const_reverse_iterator; + + nsLineList() + { + clear(); + } + + const_iterator begin() const + { + const_iterator rv; + rv.mCurrent = mLink._mNext; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + iterator begin() + { + iterator rv; + rv.mCurrent = mLink._mNext; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + const_iterator end() const + { + const_iterator rv; + rv.mCurrent = &mLink; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + iterator end() + { + iterator rv; + rv.mCurrent = &mLink; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + const_reverse_iterator rbegin() const + { + const_reverse_iterator rv; + rv.mCurrent = mLink._mPrev; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + reverse_iterator rbegin() + { + reverse_iterator rv; + rv.mCurrent = mLink._mPrev; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + const_reverse_iterator rend() const + { + const_reverse_iterator rv; + rv.mCurrent = &mLink; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + reverse_iterator rend() + { + reverse_iterator rv; + rv.mCurrent = &mLink; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + PRBool empty() const + { + return mLink._mNext == &mLink; + } + + // NOTE: O(N). + size_type size() const + { + size_type count = 0; + for (const link_type *cur = mLink._mNext; + cur != &mLink; + cur = cur->_mNext) + { + ++count; + } + return count; + } + + pointer front() + { + NS_ASSERTION(!empty(), "no element to return"); + return NS_STATIC_CAST(pointer, mLink._mNext); + } + + const_pointer front() const + { + NS_ASSERTION(!empty(), "no element to return"); + return NS_STATIC_CAST(const_pointer, mLink._mNext); + } + + pointer back() + { + NS_ASSERTION(!empty(), "no element to return"); + return NS_STATIC_CAST(pointer, mLink._mPrev); + } + + const_pointer back() const + { + NS_ASSERTION(!empty(), "no element to return"); + return NS_STATIC_CAST(const_pointer, mLink._mPrev); + } + + void push_front(pointer aNew) + { + aNew->_mNext = mLink._mNext; + mLink._mNext->_mPrev = aNew; + aNew->_mPrev = &mLink; + mLink._mNext = aNew; + } + + void pop_front() + // NOTE: leaves dangling next/prev pointers + { + NS_ASSERTION(!empty(), "no element to pop"); + link_type *newFirst = mLink._mNext->_mNext; + newFirst->_mPrev = &mLink; + // mLink._mNext->_mNext = nsnull; + // mLink._mNext->_mPrev = nsnull; + mLink._mNext = newFirst; + } + + void push_back(pointer aNew) + { + aNew->_mPrev = mLink._mPrev; + mLink._mPrev->_mNext = aNew; + aNew->_mNext = &mLink; + mLink._mPrev = aNew; + } + + void pop_back() + // NOTE: leaves dangling next/prev pointers + { + NS_ASSERTION(!empty(), "no element to pop"); + link_type *newLast = mLink._mPrev->_mPrev; + newLast->_mNext = &mLink; + // mLink._mPrev->_mPrev = nsnull; + // mLink._mPrev->_mNext = nsnull; + mLink._mPrev = newLast; + } + + // inserts x before position + iterator before_insert(iterator position, pointer x) + { + // use |mCurrent| to prevent DEBUG_PASS_END assertions + x->_mPrev = position.mCurrent->_mPrev; + x->_mNext = position.mCurrent; + position.mCurrent->_mPrev->_mNext = x; + position.mCurrent->_mPrev = x; + return --position; + } + + // inserts x after position + iterator after_insert(iterator position, pointer x) + { + // use |mCurrent| to prevent DEBUG_PASS_END assertions + x->_mNext = position.mCurrent->_mNext; + x->_mPrev = position.mCurrent; + position.mCurrent->_mNext->_mPrev = x; + position.mCurrent->_mNext = x; + return ++position; + } + + // returns iterator pointing to after the element + iterator erase(iterator position) + // NOTE: leaves dangling next/prev pointers + { + position->_mPrev->_mNext = position->_mNext; + position->_mNext->_mPrev = position->_mPrev; + return ++position; + } + + void swap(self_type& y) + { + link_type tmp(y.mLink); + y.mLink = mLink; + mLink = tmp; + } + + void clear() + // NOTE: leaves dangling next/prev pointers + { + mLink._mNext = &mLink; + mLink._mPrev = &mLink; + } + + // inserts the conts of x before position and makes x empty + void splice(iterator position, self_type& x) + { + // use |mCurrent| to prevent DEBUG_PASS_END assertions + position.mCurrent->_mPrev->_mNext = x.mLink._mNext; + x.mLink._mNext->_mPrev = position.mCurrent->_mPrev; + x.mLink._mPrev->_mNext = position.mCurrent; + position.mCurrent->_mPrev = x.mLink._mPrev; + x.clear(); + } + + // Inserts element *i from list x before position and removes + // it from x. + void splice(iterator position, self_type& x, iterator i) + { + NS_ASSERTION(!x.empty(), "Can't insert from empty list."); + NS_ASSERTION(position != i && position != i->_mNext, + "We don't check for this case."); + + // remove from |x| + i->_mPrev->_mNext = i->_mNext; + i->_mNext->_mPrev = i->_mPrev; + + // use |mCurrent| to prevent DEBUG_PASS_END assertions + // link into |this|, before-side + i->_mPrev = position.mCurrent->_mPrev; + position.mCurrent->_mPrev->_mNext = i.get(); + + // link into |this|, after-side + i->_mNext = position.mCurrent; + position.mCurrent->_mPrev = i.get(); + } + + // Inserts elements in [|first|, |last|), which are in |x|, + // into |this| before |position| and removes them from |x|. + void splice(iterator position, self_type& x, iterator first, + iterator last) + { + NS_ASSERTION(!x.empty(), "Can't insert from empty list."); + + if (first == last) + return; + + --last; // so we now want to move [first, last] + // remove from |x| + first->_mPrev->_mNext = last->_mNext; + last->_mNext->_mPrev = first->_mPrev; + + // use |mCurrent| to prevent DEBUG_PASS_END assertions + // link into |this|, before-side + first->_mPrev = position.mCurrent->_mPrev; + position.mCurrent->_mPrev->_mNext = first.get(); + + // link into |this|, after-side + last->_mNext = position.mCurrent; + position.mCurrent->_mPrev = last.get(); + } + +}; + + +// Many of these implementations of operator= don't work yet. I don't +// know why. + +#ifdef NS_LINELIST_DEBUG_PASS_END + + // NOTE: ASSIGN_FROM is meant to be used *only* as the entire body + // of a function and therefore lacks PR_{BEGIN,END}_MACRO +#define ASSIGN_FROM(other_) \ + mCurrent = other_.mCurrent; \ + mListLink = other_.mListLink; \ + return *this; + +#else /* !NS_LINELIST_DEBUG_PASS_END */ + +#define ASSIGN_FROM(other_) \ + mCurrent = other_.mCurrent; \ + return *this; + +#endif /* !NS_LINELIST_DEBUG_PASS_END */ + +inline +nsLineList_iterator& +nsLineList_iterator::operator=(const nsLineList_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_iterator& +nsLineList_iterator::operator=(const nsLineList_reverse_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_reverse_iterator& +nsLineList_reverse_iterator::operator=(const nsLineList_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_reverse_iterator& +nsLineList_reverse_iterator::operator=(const nsLineList_reverse_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_iterator& +nsLineList_const_iterator::operator=(const nsLineList_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_iterator& +nsLineList_const_iterator::operator=(const nsLineList_reverse_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_iterator& +nsLineList_const_iterator::operator=(const nsLineList_const_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_iterator& +nsLineList_const_iterator::operator=(const nsLineList_const_reverse_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_reverse_iterator& +nsLineList_const_reverse_iterator::operator=(const nsLineList_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_reverse_iterator& +nsLineList_const_reverse_iterator::operator=(const nsLineList_reverse_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_reverse_iterator& +nsLineList_const_reverse_iterator::operator=(const nsLineList_const_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_reverse_iterator& +nsLineList_const_reverse_iterator::operator=(const nsLineList_const_reverse_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + + //---------------------------------------------------------------------- class nsLineIterator : public nsILineIteratorNavigator { @@ -441,7 +1462,7 @@ public: nsIFrame **aFirstVisual, nsIFrame **aLastVisual); #endif - nsresult Init(nsLineBox* aLines, PRBool aRightToLeft); + nsresult Init(nsLineList& aLines, PRBool aRightToLeft); protected: PRInt32 NumLines() const { diff --git a/mozilla/layout/generic/nsLineLayout.cpp b/mozilla/layout/generic/nsLineLayout.cpp index c9e13f4a266..7ffad424fc1 100644 --- a/mozilla/layout/generic/nsLineLayout.cpp +++ b/mozilla/layout/generic/nsLineLayout.cpp @@ -1647,8 +1647,6 @@ nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics) // Record ascent and update max-ascent and max-descent values pfd->mAscent = aMetrics.ascent; pfd->mDescent = aMetrics.descent; -//XXX mCarriedOutTopMargin = aMetrics.mCarriedOutTopMargin; - mCarriedOutBottomMargin = aMetrics.mCarriedOutBottomMargin; // If the band was updated during the reflow of that frame then we // need to adjust any prior frames that were reflowed. @@ -1710,8 +1708,8 @@ void nsLineLayout::DumpPerSpanData(PerSpanData* psd, PRInt32 aIndent) { nsFrame::IndentBy(stdout, aIndent); - printf("%p: left=%d x=%d right=%d\n", psd, psd->mLeftEdge, - psd->mX, psd->mRightEdge); + printf("%p: left=%d x=%d right=%d\n", NS_STATIC_CAST(void*, psd), + psd->mLeftEdge, psd->mX, psd->mRightEdge); PerFrameData* pfd = psd->mFirstFrame; while (nsnull != pfd) { nsFrame::IndentBy(stdout, aIndent+1); @@ -2129,7 +2127,6 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd) nsCOMPtr fm; rc->GetFontMetrics(*getter_AddRefs(fm)); - PRBool zeroEffectiveSpanBox = PR_FALSE; PRBool preMode = (mStyleText->mWhiteSpace == NS_STYLE_WHITESPACE_PRE) || (mStyleText->mWhiteSpace == NS_STYLE_WHITESPACE_MOZ_PRE_WRAP); @@ -2189,6 +2186,13 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd) // This code works correctly for preMode, because a blank line // in PRE mode is encoded as a text node with a LF in it, since // text nodes with only whitespace are considered in preMode. + // + // Much of this logic is shared with the various implementations of + // nsIFrame::IsEmpty since they need to duplicate the way it makes + // some lines empty. However, nsIFrame::IsEmpty can't be reused here + // since this code sets zeroEffectiveSpanBox even when there are + // non-empty children. + PRBool zeroEffectiveSpanBox = PR_FALSE; if ((emptyContinuation || !InStrictMode()) && ((psd == mRootSpan) || ((0 == spanFramePFD->mBorderPadding.top) && diff --git a/mozilla/layout/generic/nsLineLayout.h b/mozilla/layout/generic/nsLineLayout.h index 4b451253af4..bb4d4935099 100644 --- a/mozilla/layout/generic/nsLineLayout.h +++ b/mozilla/layout/generic/nsLineLayout.h @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:cindent:ts=2:et:sw=2: * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file @@ -105,10 +106,6 @@ public: nsHTMLReflowMetrics* aMetrics, PRBool& aPushedFrame); - nscoord GetCarriedOutBottomMargin() const { - return mCarriedOutBottomMargin; - } - nsresult AddBulletFrame(nsIFrame* aFrame, const nsHTMLReflowMetrics& aMetrics); @@ -269,7 +266,7 @@ public: protected: // This state is constant for a given block frame doing line layout nsISpaceManager* mSpaceManager; - const nsStyleText* mStyleText; + const nsStyleText* mStyleText; // for the block const nsHTMLReflowState* mBlockReflowState; nsBlockReflowState* mBlockRS;/* XXX hack! */ nscoord mMinLineHeight; @@ -294,7 +291,6 @@ protected: nscoord mBottomEdge; nscoord mMaxTopBoxHeight; nscoord mMaxBottomBoxHeight; - nscoord mCarriedOutBottomMargin; // Final computed line-height value after VerticalAlignFrames for // the block has been called. diff --git a/mozilla/layout/generic/nsObjectFrame.cpp b/mozilla/layout/generic/nsObjectFrame.cpp index fab6eff72e6..f5bc2a7f58d 100644 --- a/mozilla/layout/generic/nsObjectFrame.cpp +++ b/mozilla/layout/generic/nsObjectFrame.cpp @@ -120,7 +120,7 @@ static NS_DEFINE_CID(kPrintOptionsCID, NS_PRINTOPTIONS_CID); #include "nsIJVMManager.h" #include "nsContentCID.h" -static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID); +static NS_DEFINE_CID(kRangeCID, NS_RANGE_CID); // XXX temporary for Mac double buffering pref static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); @@ -362,10 +362,10 @@ NS_IMETHODIMP_(nsrefcnt) nsObjectFrame::Release(void) return 1; } -static NS_DEFINE_IID(kViewCID, NS_VIEW_CID); -static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID); -static NS_DEFINE_IID(kCAppShellCID, NS_APPSHELL_CID); -static NS_DEFINE_IID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); +static NS_DEFINE_CID(kViewCID, NS_VIEW_CID); +static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID); +static NS_DEFINE_CID(kCAppShellCID, NS_APPSHELL_CID); +static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); PRIntn nsObjectFrame::GetSkipSides() const diff --git a/mozilla/layout/generic/nsPlaceholderFrame.cpp b/mozilla/layout/generic/nsPlaceholderFrame.cpp index a90ecd11f42..754e6868a80 100644 --- a/mozilla/layout/generic/nsPlaceholderFrame.cpp +++ b/mozilla/layout/generic/nsPlaceholderFrame.cpp @@ -87,6 +87,13 @@ nsPlaceholderFrame::GetFrameType(nsIAtom** aType) const return NS_OK; } +NS_IMETHODIMP +nsPlaceholderFrame::IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool *aResult) +{ + *aResult = PR_TRUE; + return NS_OK; +} + #ifdef DEBUG NS_IMETHODIMP nsPlaceholderFrame::Paint(nsIPresContext* aPresContext, @@ -122,12 +129,12 @@ nsPlaceholderFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aInden IndentBy(out, aIndent); ListTag(out); #ifdef DEBUG_waterson - fprintf(out, " [parent=%p]", mParent); + fprintf(out, " [parent=%p]", NS_STATIC_CAST(void*, mParent)); #endif nsIView* view; GetView(aPresContext, &view); if (nsnull != view) { - fprintf(out, " [view=%p]", view); + fprintf(out, " [view=%p]", (void*)view); } fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height); if (0 != mState) { diff --git a/mozilla/layout/generic/nsPlaceholderFrame.h b/mozilla/layout/generic/nsPlaceholderFrame.h index a75851d77bd..ea55a15b0a7 100644 --- a/mozilla/layout/generic/nsPlaceholderFrame.h +++ b/mozilla/layout/generic/nsPlaceholderFrame.h @@ -82,6 +82,8 @@ public: NS_IMETHOD GetFrameName(nsString& aResult) const; #endif + NS_IMETHOD IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool *aResult); + protected: nsIFrame* mOutOfFlowFrame; }; diff --git a/mozilla/layout/generic/nsSpaceManager.cpp b/mozilla/layout/generic/nsSpaceManager.cpp index 0a5f464b23f..cddd37f6eb5 100644 --- a/mozilla/layout/generic/nsSpaceManager.cpp +++ b/mozilla/layout/generic/nsSpaceManager.cpp @@ -43,6 +43,7 @@ #include "nsVoidArray.h" #include "nsIFrame.h" #include "nsString.h" +#include "nsIPresShell.h" #ifdef DEBUG #include "nsIFrameDebug.h" #endif @@ -80,13 +81,30 @@ nsSpaceManager::BandList::Clear() } } +///////////////////////////////////////////////////////////////////////////// + +// PresShell Arena allocate callback (for nsIntervalSet use below) +static void* PSArenaAllocCB(size_t aSize, void* aClosure) +{ + void *rv; + NS_STATIC_CAST(nsIPresShell*, aClosure)->AllocateFrame(aSize, &rv); + return rv; +} + +// PresShell Arena free callback (for nsIntervalSet use below) +static void PSArenaFreeCB(size_t aSize, void* aPtr, void* aClosure) +{ + NS_STATIC_CAST(nsIPresShell*, aClosure)->FreeFrame(aSize, aPtr); +} + ///////////////////////////////////////////////////////////////////////////// // nsSpaceManager -nsSpaceManager::nsSpaceManager(nsIFrame* aFrame) - : mFrame(aFrame) +nsSpaceManager::nsSpaceManager(nsIPresShell* aPresShell, nsIFrame* aFrame) + : mFrame(aFrame), + mFloatDamage(PSArenaAllocCB, PSArenaFreeCB, aPresShell) { - NS_INIT_REFCNT(); + NS_INIT_ISUPPORTS(); mX = mY = 0; mFrameInfoMap = nsnull; } @@ -118,7 +136,8 @@ NS_IMPL_RELEASE_WITH_DESTROY(nsSpaceManager, LastRelease()) // static -nsSpaceManager *nsSpaceManager::Create(nsIFrame* aFrame) +nsSpaceManager *nsSpaceManager::Create(nsIPresShell* aPresShell, + nsIFrame* aFrame) { if (sCachedSpaceManagerCount > 0) { // We have cached unused instances of this class, return a cached @@ -129,11 +148,11 @@ nsSpaceManager *nsSpaceManager::Create(nsIFrame* aFrame) // Re-initialize the cached space manager by calling its // constructor (using placement new), the destructor was called // when the space manager was put in the cache. - return new (spaceManager) nsSpaceManager(aFrame); + return new (spaceManager) nsSpaceManager(aPresShell, aFrame); } // The cache is empty, this means we haveto create a new instance. - return new nsSpaceManager(aFrame); + return new nsSpaceManager(aPresShell, aFrame); } @@ -1008,6 +1027,23 @@ nsSpaceManager::ClearRegions() return NS_OK; } +NS_IMETHODIMP_(PRBool) nsSpaceManager::HasFloatDamage() +{ + return !mFloatDamage.IsEmpty(); +} + +NS_IMETHODIMP_(void) nsSpaceManager::IncludeInDamage(nscoord aIntervalBegin, + nscoord aIntervalEnd) +{ + mFloatDamage.IncludeInterval(aIntervalBegin + mY, aIntervalEnd + mY); +} + +NS_IMETHODIMP_(PRBool) nsSpaceManager::IntersectsDamage(nscoord aIntervalBegin, + nscoord aIntervalEnd) +{ + return mFloatDamage.Intersects(aIntervalBegin + mY, aIntervalEnd + mY); +} + #ifdef DEBUG NS_IMETHODIMP nsSpaceManager::List(FILE* out) diff --git a/mozilla/layout/generic/nsSpaceManager.h b/mozilla/layout/generic/nsSpaceManager.h index 0bf055222d3..98870ac2719 100644 --- a/mozilla/layout/generic/nsSpaceManager.h +++ b/mozilla/layout/generic/nsSpaceManager.h @@ -39,6 +39,9 @@ #include "nsISpaceManager.h" #include "prclist.h" +#include "nsIntervalSet.h" + +class nsIPresShell; #define NS_SPACE_MANAGER_CACHE_SIZE 4 @@ -48,7 +51,8 @@ */ class nsSpaceManager : public nsISpaceManager { public: - static nsSpaceManager *Create(nsIFrame* aFrame); + static nsSpaceManager *Create(nsIPresShell* aPresShell, + nsIFrame* aFrame); static void Shutdown(); // nsISupports @@ -76,13 +80,19 @@ public: NS_IMETHOD ClearRegions(); + NS_IMETHOD_(PRBool) HasFloatDamage(); + NS_IMETHOD_(void) IncludeInDamage(nscoord aIntervalBegin, + nscoord aIntervalEnd); + NS_IMETHOD_(PRBool) IntersectsDamage(nscoord aIntervalBegin, + nscoord aIntervalEnd); + #ifdef DEBUG NS_IMETHOD List(FILE* out); void SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const; #endif protected: - nsSpaceManager(nsIFrame* aFrame); + nsSpaceManager(nsIPresShell* aPresShell, nsIFrame* aFrame); // Structure that maintains information about the region associated // with a particular frame @@ -166,6 +176,7 @@ protected: nscoord mX, mY; // translation from local to global coordinate space BandList mBandList; // header/sentinel for circular linked list of band rects FrameInfo* mFrameInfoMap; + nsIntervalSet mFloatDamage; protected: virtual ~nsSpaceManager(); diff --git a/mozilla/layout/generic/nsTextFrame.cpp b/mozilla/layout/generic/nsTextFrame.cpp index 11b06826b2c..0de1df899be 100644 --- a/mozilla/layout/generic/nsTextFrame.cpp +++ b/mozilla/layout/generic/nsTextFrame.cpp @@ -474,6 +474,8 @@ public: PRBool aCheckVis, PRBool* aIsVisible); + NS_IMETHOD IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool* aResult); + #ifdef ACCESSIBILITY NS_IMETHOD GetAccessible(nsIAccessible** aAccessible); #endif @@ -5608,6 +5610,24 @@ nsTextFrame::GetFrameType(nsIAtom** aType) const return NS_OK; } +NS_IMETHODIMP +nsTextFrame::IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool* aResult) +{ + // XXXldb Should this check aIsQuirkMode as well??? + if (aIsPre) { + *aResult = PR_FALSE; + return NS_OK; + } + + nsCOMPtr textContent( do_QueryInterface(mContent) ); + if (! textContent) { + NS_NOTREACHED("text frame has no text content"); + *aResult = PR_TRUE; + return NS_ERROR_UNEXPECTED; + } + return textContent->IsOnlyWhitespace(aResult); +} + #ifdef DEBUG NS_IMETHODIMP nsTextFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const @@ -5637,7 +5657,7 @@ nsTextFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) cons nsIView* view; GetView(aPresContext, &view); if (nsnull != view) { - fprintf(out, " [view=%p]", view); + fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, view)); } PRInt32 totalContentLength; @@ -5651,15 +5671,15 @@ nsTextFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) cons isComplete ? 'T':'F'); if (nsnull != mNextSibling) { - fprintf(out, " next=%p", mNextSibling); + fprintf(out, " next=%p", NS_STATIC_CAST(void*, mNextSibling)); } nsIFrame* prevInFlow; GetPrevInFlow(&prevInFlow); if (nsnull != prevInFlow) { - fprintf(out, " prev-in-flow=%p", prevInFlow); + fprintf(out, " prev-in-flow=%p", NS_STATIC_CAST(void*, prevInFlow)); } if (nsnull != mNextInFlow) { - fprintf(out, " next-in-flow=%p", mNextInFlow); + fprintf(out, " next-in-flow=%p", NS_STATIC_CAST(void*, mNextInFlow)); } // Output the rect and state @@ -5671,7 +5691,7 @@ nsTextFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) cons fprintf(out, " [state=%08x]", mState); } } - fprintf(out, " sc=%p<\n", mStyleContext); + fprintf(out, " sc=%p<\n", NS_STATIC_CAST(void*, mStyleContext)); // Output the text aIndent++; diff --git a/mozilla/layout/generic/nsTextTransformer.cpp b/mozilla/layout/generic/nsTextTransformer.cpp index f314affa5fc..8fb2bdc0914 100644 --- a/mozilla/layout/generic/nsTextTransformer.cpp +++ b/mozilla/layout/generic/nsTextTransformer.cpp @@ -100,7 +100,7 @@ nsAutoTextBuffer::GrowTo(PRInt32 aNewSize, PRBool aCopyToHead) //---------------------------------------------------------------------- -static NS_DEFINE_IID(kUnicharUtilCID, NS_UNICHARUTIL_CID); +static NS_DEFINE_CID(kUnicharUtilCID, NS_UNICHARUTIL_CID); static nsICaseConversion* gCaseConv = nsnull; diff --git a/mozilla/layout/html/base/src/Makefile.in b/mozilla/layout/html/base/src/Makefile.in index e4099e45569..1119c6146ea 100644 --- a/mozilla/layout/html/base/src/Makefile.in +++ b/mozilla/layout/html/base/src/Makefile.in @@ -60,7 +60,6 @@ REQUIRES = xpcom \ $(NULL) CPPSRCS = \ - nsSelectsAreaFrame.cpp \ nsAbsoluteContainingBlock.cpp \ nsAreaFrame.cpp \ nsBRFrame.cpp \ @@ -74,10 +73,11 @@ CPPSRCS = \ nsFirstLetterFrame.cpp \ nsFrame.cpp \ nsFrameManager.cpp \ + nsGfxScrollFrame.cpp \ nsHRFrame.cpp \ nsHTMLContainerFrame.cpp \ - nsHTMLIIDs.cpp \ nsHTMLFrame.cpp \ + nsHTMLIIDs.cpp \ nsHTMLReflowCommand.cpp \ nsHTMLReflowState.cpp \ nsImageFrame.cpp \ @@ -90,15 +90,15 @@ CPPSRCS = \ nsPageFrame.cpp \ nsPlaceholderFrame.cpp \ nsPresShell.cpp \ - nsSimplePageSequence.cpp \ nsScrollFrame.cpp \ + nsScrollPortFrame.cpp \ + nsSelectsAreaFrame.cpp \ + nsSimplePageSequence.cpp \ nsSpacerFrame.cpp \ nsSplittableFrame.cpp \ nsTextFrame.cpp \ nsTextTransformer.cpp \ nsViewportFrame.cpp \ - nsGfxScrollFrame.cpp \ - nsScrollPortFrame.cpp \ $(NULL) ifdef IBMBIDI CPPSRCS += \ diff --git a/mozilla/layout/html/base/src/makefile.win b/mozilla/layout/html/base/src/makefile.win index d8f409f9633..dcabba08c7e 100644 --- a/mozilla/layout/html/base/src/makefile.win +++ b/mozilla/layout/html/base/src/makefile.win @@ -59,118 +59,73 @@ DEFINES=-D_IMPL_NS_HTML -DWIN32_LEAN_AND_MEAN DEFINES = $(DEFINES) -DXP_NEW_SELECTION !endif -EXPORTS = \ - nsIHTMLContent.h \ - nsILineIterator.h \ - nsHTMLParts.h \ +EXPORTS = \ + nsIHTMLContent.h \ + nsILineIterator.h \ + nsHTMLParts.h \ !ifdef IBMBIDI - nsTextTransformer.h \ - nsBidiFrames.h \ -!endif + nsTextTransformer.h \ + nsBidiFrames.h \ +!endif $(NULL) -CPPSRCS= \ - nsSelectsAreaframe.cpp \ - nsScrollPortFrame.cpp \ - nsGfxScrollFrame.cpp \ - nsAbsoluteContainingBlock.cpp \ - nsAreaFrame.cpp \ - nsBRFrame.cpp \ - nsBlockBandData.cpp \ - nsBlockFrame.cpp \ - nsBlockReflowContext.cpp \ - nsBlockReflowState.cpp \ - nsBulletFrame.cpp \ - nsContainerFrame.cpp \ - nsDST.cpp \ - nsFirstLetterFrame.cpp \ - nsFrame.cpp \ - nsFrameManager.cpp \ - nsHRFrame.cpp \ - nsHTMLContainerFrame.cpp \ - nsHTMLIIDs.cpp \ - nsHTMLFrame.cpp \ - nsHTMLReflowCommand.cpp \ - nsHTMLReflowState.cpp \ - nsImageFrame.cpp \ - nsImageMap.cpp \ - nsInlineFrame.cpp \ - nsLeafFrame.cpp \ - nsLineBox.cpp \ - nsLineLayout.cpp \ - nsObjectFrame.cpp \ - nsPageFrame.cpp \ - nsPlaceholderFrame.cpp \ - nsPresShell.cpp \ - nsSimplePageSequence.cpp \ - nsScrollFrame.cpp \ - nsSpacerFrame.cpp \ - nsSplittableFrame.cpp \ - nsTextFrame.cpp \ - nsTextTransformer.cpp \ - nsViewportFrame.cpp \ +CPP_OBJS= \ + .\$(OBJDIR)\nsAbsoluteContainingBlock.obj \ + .\$(OBJDIR)\nsAreaFrame.obj \ + .\$(OBJDIR)\nsBRFrame.obj \ + .\$(OBJDIR)\nsBlockBandData.obj \ + .\$(OBJDIR)\nsBlockFrame.obj \ + .\$(OBJDIR)\nsBlockReflowContext.obj \ + .\$(OBJDIR)\nsBlockReflowState.obj \ + .\$(OBJDIR)\nsBulletFrame.obj \ + .\$(OBJDIR)\nsContainerFrame.obj \ + .\$(OBJDIR)\nsDST.obj \ + .\$(OBJDIR)\nsFirstLetterFrame.obj \ + .\$(OBJDIR)\nsFrame.obj \ + .\$(OBJDIR)\nsFrameManager.obj \ + .\$(OBJDIR)\nsGfxScrollFrame.obj \ + .\$(OBJDIR)\nsHRFrame.obj \ + .\$(OBJDIR)\nsHTMLContainerFrame.obj \ + .\$(OBJDIR)\nsHTMLFrame.obj \ + .\$(OBJDIR)\nsHTMLIIDs.obj \ + .\$(OBJDIR)\nsHTMLReflowCommand.obj \ + .\$(OBJDIR)\nsHTMLReflowState.obj \ + .\$(OBJDIR)\nsImageFrame.obj \ + .\$(OBJDIR)\nsImageMap.obj \ + .\$(OBJDIR)\nsInlineFrame.obj \ + .\$(OBJDIR)\nsLeafFrame.obj \ + .\$(OBJDIR)\nsLineBox.obj \ + .\$(OBJDIR)\nsLineLayout.obj \ + .\$(OBJDIR)\nsObjectFrame.obj \ + .\$(OBJDIR)\nsPageFrame.obj \ + .\$(OBJDIR)\nsPlaceholderFrame.obj \ + .\$(OBJDIR)\nsPresShell.obj \ + .\$(OBJDIR)\nsScrollFrame.obj \ + .\$(OBJDIR)\nsScrollPortFrame.obj \ + .\$(OBJDIR)\nsSelectsAreaFrame.obj \ + .\$(OBJDIR)\nsSimplePageSequence.obj \ + .\$(OBJDIR)\nsSpacerFrame.obj \ + .\$(OBJDIR)\nsSplittableFrame.obj \ + .\$(OBJDIR)\nsTextFrame.obj \ + .\$(OBJDIR)\nsTextTransformer.obj \ + .\$(OBJDIR)\nsViewportFrame.obj \ !ifdef IBMBIDI - nsBidiFrames.cpp \ -!endif + .\$(OBJDIR)\nsBidiFrames.obj \ +!endif $(NULL) -CPP_OBJS= \ - .\$(OBJDIR)\nsSelectsAreaFrame.obj \ - .\$(OBJDIR)\nsScrollPortFrame.obj \ - .\$(OBJDIR)\nsGfxScrollFrame.obj \ - .\$(OBJDIR)\nsAbsoluteContainingBlock.obj \ - .\$(OBJDIR)\nsAreaFrame.obj \ - .\$(OBJDIR)\nsBRFrame.obj \ - .\$(OBJDIR)\nsBlockBandData.obj \ - .\$(OBJDIR)\nsBlockFrame.obj \ - .\$(OBJDIR)\nsBlockReflowContext.obj \ - .\$(OBJDIR)\nsBlockReflowState.obj \ - .\$(OBJDIR)\nsBulletFrame.obj \ - .\$(OBJDIR)\nsContainerFrame.obj \ - .\$(OBJDIR)\nsDST.obj \ - .\$(OBJDIR)\nsFirstLetterFrame.obj \ - .\$(OBJDIR)\nsFrame.obj \ - .\$(OBJDIR)\nsFrameManager.obj \ - .\$(OBJDIR)\nsHRFrame.obj \ - .\$(OBJDIR)\nsHTMLContainerFrame.obj \ - .\$(OBJDIR)\nsHTMLFrame.obj \ - .\$(OBJDIR)\nsHTMLIIDs.obj \ - .\$(OBJDIR)\nsHTMLReflowCommand.obj \ - .\$(OBJDIR)\nsHTMLReflowState.obj \ - .\$(OBJDIR)\nsImageFrame.obj \ - .\$(OBJDIR)\nsImageMap.obj \ - .\$(OBJDIR)\nsInlineFrame.obj \ - .\$(OBJDIR)\nsLeafFrame.obj \ - .\$(OBJDIR)\nsLineBox.obj \ - .\$(OBJDIR)\nsLineLayout.obj \ - .\$(OBJDIR)\nsObjectFrame.obj \ - .\$(OBJDIR)\nsPageFrame.obj \ - .\$(OBJDIR)\nsPlaceholderFrame.obj \ - .\$(OBJDIR)\nsPresShell.obj \ - .\$(OBJDIR)\nsSimplePageSequence.obj \ - .\$(OBJDIR)\nsScrollFrame.obj \ - .\$(OBJDIR)\nsSpacerFrame.obj \ - .\$(OBJDIR)\nsSplittableFrame.obj \ - .\$(OBJDIR)\nsTextFrame.obj \ - .\$(OBJDIR)\nsTextTransformer.obj \ - .\$(OBJDIR)\nsViewportFrame.obj \ -!ifdef IBMBIDI - .\$(OBJDIR)\nsBidiFrames.obj \ -!endif - $(NULL) - -LINCS=\ - -I..\..\style\src \ - -I..\..\forms\src \ - -I..\..\..\base\src \ - -I..\..\..\xul\content\src \ - -I..\..\..\xul\base\src \ +LINCS= \ + -I..\..\style\src \ + -I..\..\forms\src \ + -I..\..\..\base\src \ + -I..\..\..\xul\content\src \ + -I..\..\..\xul\base\src \ $(NULL) LCFLAGS = \ - $(LCFLAGS) \ - $(DEFINES) \ - $(NULL) + $(LCFLAGS) \ + $(DEFINES) \ + $(NULL) include <$(DEPTH)\config\rules.mak> diff --git a/mozilla/layout/html/base/src/nsBlockFrame.cpp b/mozilla/layout/html/base/src/nsBlockFrame.cpp index 2c02c4ab715..f2eb958e39c 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.cpp +++ b/mozilla/layout/html/base/src/nsBlockFrame.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -71,6 +72,7 @@ #include "nsIFocusTracker.h" #include "nsIFrameSelection.h" #include "nsSpaceManager.h" +#include "nsIntervalSet.h" #include "prenv.h" #include "plstr.h" #include "nsGUIEvent.h" @@ -339,7 +341,8 @@ nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) *aInstancePtr = (void*) tmp; return NS_OK; } - if ( aIID.Equals(NS_GET_IID(nsILineIterator)) || aIID.Equals(NS_GET_IID(nsILineIteratorNavigator)) ) + if ( aIID.Equals(NS_GET_IID(nsILineIterator)) || + aIID.Equals(NS_GET_IID(nsILineIteratorNavigator)) ) { nsLineIterator* it = new nsLineIterator; if (!it) { @@ -379,18 +382,18 @@ nsBlockFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) con nsIView* view; GetView(aPresContext, &view); if (nsnull != view) { - fprintf(out, " [view=%p]", view); + fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, view)); } if (nsnull != mNextSibling) { - fprintf(out, " next=%p", mNextSibling); + fprintf(out, " next=%p", NS_STATIC_CAST(void*, mNextSibling)); } // Output the flow linkage if (nsnull != mPrevInFlow) { - fprintf(out, " prev-in-flow=%p", mPrevInFlow); + fprintf(out, " prev-in-flow=%p", NS_STATIC_CAST(void*, mPrevInFlow)); } if (nsnull != mNextInFlow) { - fprintf(out, " next-in-flow=%p", mNextInFlow); + fprintf(out, " next-in-flow=%p", NS_STATIC_CAST(void*, mNextInFlow)); } // Output the rect and state @@ -400,27 +403,28 @@ nsBlockFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) con } PRInt32 numInlineLines = 0; PRInt32 numBlockLines = 0; - if (nsnull != mLines) { - nsLineBox* line = mLines; - while (nsnull != line) { - if (line->IsBlock()) { + if (! mLines.empty()) { + for (const_line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { + if (line->IsBlock()) numBlockLines++; - } - else { + else numInlineLines++; - } - line = line->mNext; } } - fprintf(out, " sc=%p(i=%d,b=%d)<\n", mStyleContext, numInlineLines, numBlockLines); + fprintf(out, " sc=%p(i=%d,b=%d)<\n", + NS_STATIC_CAST(void*, mStyleContext), numInlineLines, numBlockLines); aIndent++; // Output the lines - if (nsnull != mLines) { - nsLineBox* line = mLines; - while (nsnull != line) { + if (! mLines.empty()) { + for (const_line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { line->List(aPresContext, out, aIndent); - line = line->mNext; } } @@ -444,7 +448,7 @@ nsBlockFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) con while (nsnull != kid) { nsIFrameDebug* frameDebug; - if (NS_SUCCEEDED(kid->QueryInterface(NS_GET_IID(nsIFrameDebug), (void**)&frameDebug))) { + if (NS_SUCCEEDED(CallQueryInterface(kid, &frameDebug))) { frameDebug->List(aPresContext, out, aIndent + 1); } kid->GetNextSibling(&kid); @@ -491,12 +495,14 @@ nsBlockFrame::FirstChild(nsIPresContext* aPresContext, return mAbsoluteContainer.FirstChild(this, aListName, aFirstChild); } else if (nsnull == aListName) { - *aFirstChild = (nsnull != mLines) ? mLines->mFirstChild : nsnull; + *aFirstChild = (mLines.empty()) ? nsnull : mLines.front()->mFirstChild; return NS_OK; } else if (aListName == nsLayoutAtoms::overflowList) { - nsLineBox* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); - *aFirstChild = overflowLines ? overflowLines->mFirstChild : nsnull; + nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); + *aFirstChild = overflowLines + ? overflowLines->front()->mFirstChild + : nsnull; return NS_OK; } else if (aListName == nsLayoutAtoms::floaterList) { @@ -689,7 +695,9 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, // only when there are actually floats to manage. Otherwise things // like tables will gain significant bloat. if (NS_BLOCK_SPACE_MGR & mState) { - nsSpaceManager* rawPtr = nsSpaceManager::Create(this); + nsCOMPtr shell; + aPresContext->GetShell(getter_AddRefs(shell)); + nsSpaceManager* rawPtr = nsSpaceManager::Create(shell, this); if (!rawPtr) { return NS_ERROR_OUT_OF_MEMORY; } @@ -710,7 +718,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, if (eReflowReason_Resize != aReflowState.reason) { #ifdef IBMBIDI - if (mLines) { + if (! mLines.empty()) { PRBool bidiEnabled; aPresContext->GetBidiEnabled(&bidiEnabled); if (bidiEnabled) { @@ -719,11 +727,15 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, if (bidiUtils) { PRBool forceReflow; nsresult rc = bidiUtils->Resolve(aPresContext, this, - mLines->mFirstChild, forceReflow); + mLines.front()->mFirstChild, + forceReflow); if (NS_SUCCEEDED(rc) && forceReflow) { // Mark everything dirty // XXXldb This should be done right. - for (nsLineBox* line = mLines; line; line = line->mNext) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { line->MarkDirty(); } } @@ -792,6 +804,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, break; case eReflowReason_StyleChange: + DrainOverflowLines(aPresContext); rv = PrepareStyleChangedReflow(state); break; @@ -998,7 +1011,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, printf(": status=%x (%scomplete) metrics=%d,%d carriedMargin=%d", aStatus, NS_FRAME_IS_COMPLETE(aStatus) ? "" : "not ", aMetrics.width, aMetrics.height, - aMetrics.mCarriedOutBottomMargin); + aMetrics.mCarriedOutBottomMargin.get()); if (mState & NS_FRAME_OUTSIDE_CHILDREN) { printf(" combinedArea={%d,%d,%d,%d}", aMetrics.mOverflowArea.x, @@ -1018,7 +1031,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, PRTime end = PR_Now(); PRInt32 ectc = nsLineBox::GetCtorCount(); - PRInt32 numLines = nsLineBox::ListLength(mLines); + PRInt32 numLines = mLines.size(); if (!numLines) numLines = 1; PRTime delta, perLineDelta, lines; LL_I2L(lines, numLines); @@ -1177,9 +1190,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // exactly one line and that line is not a block line AND that // line doesn't end in a BR of any sort THEN we remain a compact // frame. - if ((nsnull == mLines) || - ((nsnull == mLines->mNext) && !mLines->IsBlock() && - (NS_STYLE_CLEAR_NONE == mLines->GetBreakType()) + if ((mLines.empty()) || + ((mLines.front() == mLines.back()) && !mLines.front()->IsBlock() && + (NS_STYLE_CLEAR_NONE == mLines.front()->GetBreakType()) /*XXX && (computedWidth <= aState.mCompactMarginWidth) */ )) { compact = PR_TRUE; @@ -1252,7 +1265,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, reflowState.reason = eReflowReason_Resize; reflowState.mSpaceManager->ClearRegions(); +#ifdef DEBUG nscoord oldDesiredWidth = aMetrics.width; +#endif nsBlockReflowState state(reflowState, aState.mPresContext, this, aMetrics, NS_BLOCK_MARGIN_ROOT & mState); ReflowDirtyLines(state); @@ -1285,8 +1300,8 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // unless the bottom of the last line adjoins the bottom of our // content area. if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { - if (aState.mY + aState.mPrevBottomMargin != aMetrics.height) { - aState.mPrevBottomMargin = 0; + if (aState.mY + aState.mPrevBottomMargin.get() != aMetrics.height) { + aState.mPrevBottomMargin.Zero(); } } } @@ -1298,7 +1313,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // When we are a bottom-margin root make sure that our last // childs bottom margin is fully applied. // XXX check for a fit - autoHeight += aState.mPrevBottomMargin; + autoHeight += aState.mPrevBottomMargin.get(); } autoHeight += borderPadding.bottom; @@ -1339,8 +1354,14 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } // Return bottom margin information - aMetrics.mCarriedOutBottomMargin = - aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? 0 : aState.mPrevBottomMargin; + // rbs says he hit this assertion occasionally (see bug 86947), so + // just set the margin to zero and we'll figure out why later + //NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(), + // "someone else set the margin"); + if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) + aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin; + else + aMetrics.mCarriedOutBottomMargin.Zero(); #ifdef DEBUG_blocks if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) { @@ -1383,8 +1404,10 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // XXX_perf: This can be done incrementally nscoord xa = 0, ya = 0, xb = aMetrics.width, yb = aMetrics.height; if (NS_STYLE_OVERFLOW_HIDDEN != aReflowState.mStyleDisplay->mOverflow) { - nsLineBox* line = mLines; - while (nsnull != line) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { // Compute min and max x/y values for the reflowed frame's // combined areas nsRect lineCombinedArea; @@ -1405,7 +1428,6 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, if (ymost > yb) { yb = ymost; } - line = line->mNext; } // Factor the bullet in; normally the bullet will be factored into @@ -1484,12 +1506,17 @@ nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState) { // Determine the line being impacted PRBool isFloater; - nsLineBox* prevLine; - nsLineBox* line = FindLineFor(aState.mNextRCFrame, &prevLine, &isFloater); - if (! line) { + line_iterator line; + FindLineFor(aState.mNextRCFrame, &isFloater, &line); + if (line == end_lines()) { // This assertion actually fires on lots of pages // (e.g., bugzilla, bugzilla query page), so limit it // to a few people until we fix the problem causing it. + // + // I think waterson explained once why it was happening -- I think + // it has something to do with the interaction of the unconstrained + // reflow in multi-pass reflow with the reflow command's chain, but + // I don't remember the details. #if defined(DEBUG_dbaron) || defined(DEBUG_waterson) NS_NOTREACHED("We don't have a line for the target of the reflow. " "Being inefficient"); @@ -1506,19 +1533,21 @@ nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState) if (line->IsInline()) { aState.SetFlag(BRS_ISINLINEINCRREFLOW, PR_TRUE); - - // Don't mark the prevLine as dirty if BRS_ISINLINEINCRREFLOW - prevLine = nsnull; } - // Figure out which line to mark dirty. - MarkLineDirty(line, prevLine); + // Just mark this line dirty. We never need to mark the + // previous line dirty since either: + // * the line is a block, and there would never be a chance to pull + // something up + // * It's an incremental reflow to something within an inline, which + // we know must be very limited. + line->MarkDirty(); return NS_OK; } nsresult -nsBlockFrame::MarkLineDirty(nsLineBox* aLine, nsLineBox* aPrevLine) +nsBlockFrame::MarkLineDirty(line_iterator aLine) { // Mark aLine dirty aLine->MarkDirty(); @@ -1526,22 +1555,23 @@ nsBlockFrame::MarkLineDirty(nsLineBox* aLine, nsLineBox* aPrevLine) if (gNoisyReflow) { IndentBy(stdout, gNoiseIndent); ListTag(stdout); - printf(": mark line %p dirty\n", aLine); + printf(": mark line %p dirty\n", NS_STATIC_CAST(void*, aLine.get())); } #endif // Mark previous line dirty if its an inline line so that it can // maybe pullup something from the line just affected. // XXX We don't need to do this if aPrevLine ends in a break-after... - // XXXldb Couldn't we additionally limit this to when - // !aLine->IsBlock() ? - if (aPrevLine && !aPrevLine->IsBlock()) { - aPrevLine->MarkDirty(); + if (aLine != mLines.front() && + aLine->IsInline() && + aLine.prev()->IsInline()) { + aLine.prev()->MarkDirty(); #ifdef DEBUG if (gNoisyReflow) { IndentBy(stdout, gNoiseIndent); ListTag(stdout); - printf(": mark prev-line %p dirty\n", aPrevLine); + printf(": mark prev-line %p dirty\n", + NS_STATIC_CAST(void*, aLine.prev().get())); } #endif } @@ -1562,10 +1592,11 @@ nsBlockFrame::UpdateBulletPosition(nsBlockReflowState& aState) if (mBullet && HaveOutsideBullet()) { // We now have an inside bullet, but used to have an outside // bullet. Adjust the frame line list - if (mLines) { + if (! mLines.empty()) { // if we have a line already, then move the bullet to the front of the // first line nsIFrame* child = nsnull; + nsLineBox* firstLine = mLines.front(); #ifdef DEBUG // bullet should not have any siblings if it was an outside bullet @@ -1574,20 +1605,20 @@ nsBlockFrame::UpdateBulletPosition(nsBlockReflowState& aState) NS_ASSERTION(!next, "outside bullet should not have siblings"); #endif // move bullet to front and chain the previous frames, and update the line count - child = mLines->mFirstChild; - mLines->mFirstChild = mBullet; + child = firstLine->mFirstChild; + firstLine->mFirstChild = mBullet; mBullet->SetNextSibling(child); - PRInt32 count = mLines->GetChildCount(); - mLines->SetChildCount(count+1); + PRInt32 count = firstLine->GetChildCount(); + firstLine->SetChildCount(count+1); // dirty it here in case the caller does not - mLines->MarkDirty(); + firstLine->MarkDirty(); } else { // no prior lines, just create a new line for the bullet nsLineBox* line = aState.NewLineBox(mBullet, 1, PR_FALSE); if (!line) { return NS_ERROR_OUT_OF_MEMORY; } - mLines = line; + mLines.push_back(line); } } mState &= ~NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET; @@ -1597,24 +1628,24 @@ nsBlockFrame::UpdateBulletPosition(nsBlockReflowState& aState) // We now have an outside bullet, but used to have an inside // bullet. Take the bullet frame out of the first lines frame // list. - if ((nsnull != mLines) && (mBullet == mLines->mFirstChild)) { + if ((! mLines.empty()) && (mBullet == mLines.front()->mFirstChild)) { nsIFrame* next; mBullet->GetNextSibling(&next); mBullet->SetNextSibling(nsnull); - PRInt32 count = mLines->GetChildCount() - 1; + PRInt32 count = mLines.front()->GetChildCount() - 1; NS_ASSERTION(count >= 0, "empty line w/o bullet"); - mLines->SetChildCount(count); + mLines.front()->SetChildCount(count); if (0 == count) { - nsLineBox* nextLine = mLines->mNext; - aState.FreeLineBox(mLines); - mLines = nextLine; - if (nsnull != nextLine) { - nextLine->MarkDirty(); + nsLineBox* oldFront = mLines.front(); + mLines.pop_front(); + aState.FreeLineBox(oldFront); + if (! mLines.empty()) { + mLines.front()->MarkDirty(); } } else { - mLines->mFirstChild = next; - mLines->MarkDirty(); + mLines.front()->mFirstChild = next; + mLines.front()->MarkDirty(); } } } @@ -1632,10 +1663,11 @@ nsBlockFrame::PrepareStyleChangedReflow(nsBlockReflowState& aState) nsresult rv = UpdateBulletPosition(aState); // Mark everything dirty - nsLineBox* line = mLines; - while (nsnull != line) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { line->MarkDirty(); - line = line->mNext; } return rv; } @@ -1684,7 +1716,6 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) } #endif - nsLineBox* line = mLines; if (tryAndSkipLines) { nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left; @@ -1714,8 +1745,10 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) #endif PRBool wrapping = !aState.GetFlag(BRS_NOWRAP); - while (nsnull != line) { - + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { // We let child blocks make their own decisions the same // way we are here. // @@ -1724,7 +1757,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) if (line->IsBlock() || line->HasPercentageChild() || (wrapping && - ((line->mNext && !line->HasBreak()) || + ((line != mLines.back() && !line->HasBreak()) || line->ResizeReflowOptimizationDisabled() || line->HasFloaters() || line->IsImpactedByFloater() || @@ -1742,7 +1775,8 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) if (gNoisyReflow && !line->IsDirty() && wrapping) { IndentBy(stdout, gNoiseIndent + 1); printf("skipped: line=%p next=%p %s %s %s%s%s breakType=%d xmost=%d\n", - line, line->mNext, + NS_STATIC_CAST(void*, line.get()), + NS_STATIC_CAST(void*, line.next().get()), line->IsBlock() ? "block" : "inline", "wrapping", line->HasBreak() ? "has-break " : "", @@ -1752,15 +1786,15 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) line->mBounds.XMost()); } #endif - - line = line->mNext; } } else { // Mark everything dirty - while (nsnull != line) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { line->MarkDirty(); - line = line->mNext; } } return NS_OK; @@ -1768,22 +1802,23 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) //---------------------------------------- -nsLineBox* +PRBool nsBlockFrame::FindLineFor(nsIFrame* aFrame, - nsLineBox** aPrevLineResult, - PRBool* aIsFloaterResult) + PRBool* aIsFloaterResult, + line_iterator* aFoundLine) { - // This assertion actually fires on lots of pages - // (e.g., bugzilla, bugzilla query page), so limit it - // to a few people until we fix the problem causing it. + // This assertion actually fires on lots of pages (e.g., bugzilla, + // bugzilla query page), so limit it to a few people until we fix the + // problem causing it. It's related to the similarly |#ifdef|ed + // assertion in |PrepareChildIncrementalReflow|. #if defined(DEBUG_dbaron) || defined(DEBUG_waterson) NS_PRECONDITION(aFrame, "why pass a null frame?"); #endif - nsLineBox* prevLine = nsnull; - nsLineBox* line = mLines; PRBool isFloater = PR_FALSE; - while (nsnull != line) { + line_iterator line = begin_lines(), + line_end = end_lines(); + for ( ; line != line_end; ++line) { if (line->Contains(aFrame)) { break; } @@ -1798,14 +1833,12 @@ nsBlockFrame::FindLineFor(nsIFrame* aFrame, fc = fc->Next(); } } - prevLine = line; - line = line->mNext; } done: *aIsFloaterResult = isFloater; - *aPrevLineResult = prevLine; - return line; + *aFoundLine = line; + return (line != line_end); } // SEC: added GetCurrentLine() for bug 45152 @@ -1819,118 +1852,117 @@ nsBlockFrame::GetCurrentLine(nsBlockReflowState *aState, nsLineBox ** aOutCurren return NS_OK; } +/** + * Remember regions of reflow damage from floaters that changed size (so + * far, only vertically, which is a bug) in the space manager so that we + * can mark any impacted lines dirty in |PropagateFloaterDamage|. + */ void -nsBlockFrame::RecoverStateFrom(nsBlockReflowState& aState, - nsLineBox* aLine, - nsRect* aDamageRect) +nsBlockFrame::RememberFloaterDamage(nsBlockReflowState& aState, + nsLineBox* aLine, + const nsRect& aOldCombinedArea) { - PRBool applyTopMargin = PR_FALSE; - if (aLine->IsBlock()) { - nsIFrame* framePrevInFlow; - aLine->mFirstChild->GetPrevInFlow(&framePrevInFlow); - if (nsnull == framePrevInFlow) { - applyTopMargin = ShouldApplyTopMargin(aState, aLine); - } - } + nsRect lineCombinedArea; + aLine->GetCombinedArea(&lineCombinedArea); + if (lineCombinedArea != aLine->mBounds && + lineCombinedArea != aOldCombinedArea) { + // The line's combined-area changed. Therefore we need to damage + // the lines below that were previously (or are now) impacted by + // the change. It's possible that a floater shrunk or grew so + // use the larger of the impacted area. - aState.RecoverStateFrom(aLine, applyTopMargin, aDamageRect); + // XXXldb If just the width of the floater changed, then this code + // won't be triggered and the code below (in |PropagateFloaterDamage|) + // won't kick in for "non-Block lines". See + // "XXX: Maybe the floater itself changed size?" below. + // + // This is a major flaw in this code. + // + nscoord newYMost = lineCombinedArea.YMost(); + nscoord oldYMost = aOldCombinedArea.YMost(); + nscoord impactYB = newYMost < oldYMost ? oldYMost : newYMost; + nscoord impactYA = lineCombinedArea.y; + nsISpaceManager *spaceManager = aState.mReflowState.mSpaceManager; + spaceManager->IncludeInDamage(impactYA, impactYB); + } } /** - * Propagate reflow "damage" from the just reflowed line (aLine) to - * any subsequent lines that were affected. The only thing that causes - * damage is a change to the impact that floaters make. + * Propagate reflow "damage" from from earlier lines to the current + * line. The reflow damage comes from the following sources: + * 1. The regions of floater damage remembered in + * |RememberFloaterDamage|. + * 2. The combination of nonzero |aDeltaY| and any impact by a floater, + * either the previous reflow or now. * - * XXX_perf: This is O(N^2) because we loop over all following lines - * for each line that is damaged. See bug 61962. + * When entering this function, |aLine| is still at its old position and + * |aDeltaY| indicates how much it will later be slid (assuming it + * doesn't get marked dirty and reflowed entirely). */ void -nsBlockFrame::PropagateReflowDamage(nsBlockReflowState& aState, - nsLineBox* aLine, - const nsRect& aOldCombinedArea, - nscoord aDeltaY) +nsBlockFrame::PropagateFloaterDamage(nsBlockReflowState& aState, + nsLineBox* aLine, + nscoord aDeltaY) { - // See if the line has a relevant combined area, and if it does if - // the combined area has changed. - nsRect lineCombinedArea; - aLine->GetCombinedArea(&lineCombinedArea); - if (lineCombinedArea != aLine->mBounds) { - if (lineCombinedArea != aOldCombinedArea) { - // The line's combined-area changed. Therefore we need to damage - // the lines below that were previously (or are now) impacted by - // the change. It's possible that a floater shrunk or grew so - // use the larger of the impacted area. - nscoord newYMost = lineCombinedArea.YMost(); - nscoord oldYMost = aOldCombinedArea.YMost(); - nscoord impactYB = newYMost < oldYMost ? oldYMost : newYMost; - nscoord impactYA = lineCombinedArea.y; + NS_PRECONDITION(!aLine->IsDirty(), "should never be called on dirty lines"); - // Loop over each subsequent line and mark them dirty if they - // intersect the impacted area. Note: we cannot stop after the - // first non-intersecting line because lines might be - // overlapping because of negative margins. - nsLineBox* next = aLine->mNext; - while (nsnull != next) { - nscoord lineYA = next->mBounds.y + aDeltaY; - nscoord lineYB = lineYA + next->mBounds.height; - if ((lineYB >= impactYA) && (lineYA < impactYB)) { - next->MarkDirty(); - } - next = next->mNext; - } - } - else { - // The line's combined area didn't change from last - // time. Therefore just sliding subsequent lines will work. + // Check the damage region recorded in the float damage. + nsISpaceManager *spaceManager = aState.mReflowState.mSpaceManager; + if (spaceManager->HasFloatDamage()) { + nscoord lineYA = aLine->mBounds.y + aDeltaY; + nscoord lineYB = lineYA + aLine->mBounds.height; + if (spaceManager->IntersectsDamage(lineYA, lineYB)) { + aLine->MarkDirty(); return; } } if (aDeltaY) { - nsLineBox* next = aLine->mNext; - while (nsnull != next) { - if (!next->IsDirty()) { - // Cases we need to find: - // - // 1. the line was impacted by a floater and now isn't - // 2. the line wasn't impacted by a floater and now is - // - //XXXPerf: An optimization: if the line was and is completely - //impacted by a floater and the floater hasn't changed size, - //then we don't need to mark the line dirty. - aState.GetAvailableSpace(next->mBounds.y + aDeltaY); - PRBool wasImpactedByFloater = next->IsImpactedByFloater(); - PRBool isImpactedByFloater = aState.IsImpactedByFloater() ? PR_TRUE : PR_FALSE; + // Cases we need to find: + // + // 1. the line was impacted by a floater and now isn't + // 2. the line wasn't impacted by a floater and now is + // 3. the line is impacted by a floater both before and after and + // the floater has changed position relative to the line (or it's + // a different floater). (XXXPerf we don't currently + // check whether the floater changed size. We currently just + // mark blocks dirty and ignore any possibility of damage to + // inlines by it being a different floater with a different + // size.) + // + // XXXPerf: An optimization: if the line was and is completely + // impacted by a floater and the floater hasn't changed size, + // then we don't need to mark the line dirty. + aState.GetAvailableSpace(aLine->mBounds.y + aDeltaY); + PRBool wasImpactedByFloater = aLine->IsImpactedByFloater(); + PRBool isImpactedByFloater = aState.IsImpactedByFloater(); #ifdef REALLY_NOISY_REFLOW - printf("nsBlockFrame::PropagateReflowDamage %p was = %d, is=%d\n", - this, wasImpactedByFloater, isImpactedByFloater); + printf("nsBlockFrame::PropagateFloaterDamage %p was = %d, is=%d\n", + this, wasImpactedByFloater, isImpactedByFloater); #endif - if (wasImpactedByFloater != isImpactedByFloater) { - next->MarkDirty(); - } - else if (isImpactedByFloater) { - //XXX: Maybe the floater itself changed size? - if (next->IsBlock()) { - //XXXPerf - // Case: - // It's possible that more/less of the line is impacted by - // the floater than last time. So reflow. - next->MarkDirty(); - } - } - } - next = next->mNext; + // Mark the line dirty if: + // 1. It used to be impacted by a floater and now isn't, or vice + // versa. + // 2. It is impacted by a floater and it is a block, which means + // that more or less of the line could be impacted than was in + // the past. (XXXPerf This could be optimized further, since + // we're marking the whole line dirty.) + if ((wasImpactedByFloater != isImpactedByFloater) || + (isImpactedByFloater && aLine->IsBlock())) { + aLine->MarkDirty(); } } } +// NOTE: The first parameter *must* be passed by value. static PRBool -WrappedLinesAreDirty(nsLineBox* aLine) +WrappedLinesAreDirty(nsLineList::iterator aLine, + const nsLineList::iterator aLineEnd) { if (aLine->IsInline()) { while (aLine->IsLineWrapped()) { - aLine = aLine->mNext; - if (!aLine) { + ++aLine; + if (aLine == aLineEnd) { break; } @@ -1972,6 +2004,8 @@ PRBool nsBlockFrame::IsIncrementalDamageConstrained(const nsBlockReflowState& aS return PR_FALSE; // default case, damage is not constrained (or unknown) } +static void PlaceFrameView(nsIPresContext* aPresContext, nsIFrame* aFrame); + /** * Reflow the dirty lines */ @@ -1980,6 +2014,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) { nsresult rv = NS_OK; PRBool keepGoing = PR_TRUE; + PRBool repositionViews = PR_FALSE; // should we really need this? #ifdef DEBUG if (gNoisyReflow) { @@ -2008,24 +2043,30 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) aState.mReflowState.reason == eReflowReason_Dirty; + // the amount by which we will slide the current line if it is not + // dirty nscoord deltaY = 0; - // Reflow the lines that are already ours - aState.mPrevLine = nsnull; - nsLineBox* line = mLines; + // whether we did NOT reflow the previous line and thus we need to + // recompute the carried out margin before the line if we want to + // reflow it or if its previous margin is dirty + PRBool needToRecoverMargin = PR_FALSE; - while (line) { + // Reflow the lines that are already ours + line_iterator line = begin_lines(), line_end = end_lines(); + for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) { #ifdef DEBUG if (gNoisyReflow) { nsRect lca; line->GetCombinedArea(&lca); IndentBy(stdout, gNoiseIndent); printf("line=%p mY=%d dirty=%s oldBounds={%d,%d,%d,%d} oldCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d\n", - line, aState.mY, line->IsDirty() ? "yes" : "no", + NS_STATIC_CAST(void*, line.get()), aState.mY, + line->IsDirty() ? "yes" : "no", line->mBounds.x, line->mBounds.y, line->mBounds.width, line->mBounds.height, lca.x, lca.y, lca.width, lca.height, - deltaY, aState.mPrevBottomMargin); + deltaY, aState.mPrevBottomMargin.get()); gNoiseIndent++; } #endif @@ -2033,11 +2074,55 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // If we're supposed to update our maximum width, then we'll also need to // reflow this line if it's line wrapped and any of the continuing lines // are dirty - if (line->IsDirty() || (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && ::WrappedLinesAreDirty(line))) { + if (!line->IsDirty() && + (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && + ::WrappedLinesAreDirty(line, line_end))) { + line->MarkDirty(); + } + + // Make sure |aState.mPrevBottomMargin| is at the correct position + // before calling PropagateFloaterDamage. + if (needToRecoverMargin) { + needToRecoverMargin = PR_FALSE; + // We need to reconstruct the bottom margin only if we didn't + // reflow the previous line and we do need to reflow (or repair + // the top position of) the next line. + if (line->IsDirty() || line->IsPreviousMarginDirty()) + aState.ReconstructMarginAbove(line); + } + + if (line->IsPreviousMarginDirty() && !line->IsDirty()) { + // If the previous margin is dirty and we're not going to reflow + // the line we need to pull out the correct top margin and set + // |deltaY| correctly. + // If there's float damage we might end up doing this work twice, + // but whatever... + if (line->IsBlock()) { + // XXXPerf We could actually make this faster by stealing code + // from the top of nsBlockFrame::ReflowBlockFrame, but it's an + // edge case that will generally happen at most once in a given + // reflow (insertion of a new line before a block) + line->MarkDirty(); + } else { + deltaY = aState.mY + aState.mPrevBottomMargin.get() - line->mBounds.y; + } + } + line->ClearPreviousMarginDirty(); + + // See if there's any reflow damage that requires that we mark the + // line dirty. + if (!line->IsDirty()) { + PropagateFloaterDamage(aState, line, deltaY); + } + + // Now repair the line and update |aState.mY| by calling + // |ReflowLine| or |SlideLine|. + if (line->IsDirty()) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. - nscoord oldHeight = line->mBounds.height; + nscoord oldY = line->mBounds.y; + nscoord oldYMost = line->mBounds.YMost(); nsRect oldCombinedArea; line->GetCombinedArea(&oldCombinedArea); @@ -2053,35 +2138,40 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) } if (!keepGoing) { if (0 == line->GetChildCount()) { - DeleteLine(aState, line); + DeleteLine(aState, line, line_end); } break; } - nscoord newHeight = line->mBounds.height; - deltaY += newHeight - oldHeight; + if (oldY == 0 && deltaY != line->mBounds.y) { + // This means the current line was just reflowed for the first + // time. Thus we must mark the the previous margin of the next + // line dirty. + // XXXldb Move this into where we insert the line! (or will + // that mess up deltaY manipulation?) + if (line.next() != end_lines()) { + line.next()->MarkPreviousMarginDirty(); + // since it's marked dirty, nobody will care about |deltaY| + } + } else { + deltaY = line->mBounds.YMost() - oldYMost; + } - // If the next line is clean then check and see if reflowing the - // current line "damaged" the next line. Damage occurs when the - // current line contains floaters that intrude upon the - // subsequent lines. - nsLineBox* next = line->mNext; - if (next && !next->IsDirty()) { - PropagateReflowDamage(aState, line, oldCombinedArea, deltaY); - } - } - else { - // XXX what if the slid line doesn't fit because we are in a - // vertically constrained situation? - // Recover state as if we reflowed this line - nsRect damageRect; - RecoverStateFrom(aState, line, incrementalReflow ? &damageRect : 0); - if (incrementalReflow && !damageRect.IsEmpty()) { -#ifdef NOISY_BLOCK_INVALIDATE - printf("%p invalidate 5 (%d, %d, %d, %d)\n", - this, damageRect.x, damageRect.y, damageRect.width, damageRect.height); -#endif - Invalidate(aState.mPresContext, damageRect); - } + // Remember any things that could potentially be changes in the + // positions of floaters in this line so that we later damage the + // lines adjacent to those changes. + RememberFloaterDamage(aState, line, oldCombinedArea); + } else { + if (deltaY != 0) + SlideLine(aState, line, deltaY); + else + repositionViews = PR_TRUE; + + // XXX EVIL O(N^2) EVIL + aState.RecoverStateFrom(line, deltaY); + + // Keep mY up to date in case we're propagating reflow damage. + aState.mY = line->mBounds.YMost(); + needToRecoverMargin = PR_TRUE; } #ifdef DEBUG @@ -2091,45 +2181,50 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) line->GetCombinedArea(&lca); IndentBy(stdout, gNoiseIndent); printf("line=%p mY=%d newBounds={%d,%d,%d,%d} newCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d\n", - line, aState.mY, + NS_STATIC_CAST(void*, line.get()), aState.mY, line->mBounds.x, line->mBounds.y, line->mBounds.width, line->mBounds.height, lca.x, lca.y, lca.width, lca.height, - deltaY, aState.mPrevBottomMargin); + deltaY, aState.mPrevBottomMargin.get()); } #endif - - // If this is an inline frame then its time to stop - aState.mPrevLine = line; - line = line->mNext; - aState.AdvanceToNextLine(); } + if (needToRecoverMargin) { + // Is this expensive? + aState.ReconstructMarginAbove(line); + } + + // Should we really have to do this? + if (repositionViews) + ::PlaceFrameView(aState.mPresContext, this); + // Pull data from a next-in-flow if there's still room for more // content here. while (keepGoing && (nsnull != aState.mNextInFlow)) { // Grab first line from our next-in-flow - line = aState.mNextInFlow->mLines; - if (nsnull == line) { + nsBlockFrame* nextInFlow = aState.mNextInFlow; + line_iterator nifLine = nextInFlow->begin_lines(); + if (nifLine == nextInFlow->end_lines()) { aState.mNextInFlow = (nsBlockFrame*) aState.mNextInFlow->mNextInFlow; continue; } // XXX See if the line is not dirty; if it's not maybe we can // avoid the pullup if it can't fit? - aState.mNextInFlow->mLines = line->mNext; - line->mNext = nsnull; - if (0 == line->GetChildCount()) { + nsLineBox *toMove = nifLine; + nextInFlow->mLines.erase(nifLine); + if (0 == toMove->GetChildCount()) { // The line is empty. Try the next one. - NS_ASSERTION(nsnull == line->mFirstChild, "bad empty line"); - aState.FreeLineBox(line); + NS_ASSERTION(nsnull == toMove->mFirstChild, "bad empty line"); + aState.FreeLineBox(toMove); continue; } // XXX move to a subroutine: run-in, overflow, pullframe and this do this // Make the children in the line ours. - nsIFrame* frame = line->mFirstChild; + nsIFrame* frame = toMove->mFirstChild; nsIFrame* lastFrame = nsnull; - PRInt32 n = line->GetChildCount(); + PRInt32 n = toMove->GetChildCount(); while (--n >= 0) { frame->SetParent(this); // When pushing and pulling frames we need to check for whether any @@ -2141,15 +2236,9 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) lastFrame->SetNextSibling(nsnull); // Add line to our line list - if (nsnull == aState.mPrevLine) { - NS_ASSERTION(nsnull == mLines, "bad aState.mPrevLine"); - mLines = line; - } - else { - NS_ASSERTION(nsnull == aState.mPrevLine->mNext, "bad aState.mPrevLine"); - aState.mPrevLine->mNext = line; - aState.mPrevChild->SetNextSibling(line->mFirstChild); - } + if (aState.mPrevChild) + aState.mPrevChild->SetNextSibling(toMove->mFirstChild); + line = mLines.before_insert(end_lines(), toMove); // If line contains floaters, remove them from aState.mNextInFlow's // floater list. They will be pushed onto this blockframe's floater @@ -2175,27 +2264,27 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // (we have to loop here because reflowing the line may case a new // line to be created; see SplitLine's callers for examples of // when this happens). - while (nsnull != line) { - rv = ReflowLine(aState, line, &keepGoing, incrementalReflow /* force invalidate */); + while (line != end_lines()) { + rv = ReflowLine(aState, line, &keepGoing, + incrementalReflow /* force invalidate */); if (NS_FAILED(rv)) { return rv; } if (!keepGoing) { if (0 == line->GetChildCount()) { - DeleteLine(aState, line); + DeleteLine(aState, line, line_end); } break; } // If this is an inline frame then its time to stop - aState.mPrevLine = line; - line = line->mNext; + ++line; aState.AdvanceToNextLine(); } } // Handle an odd-ball case: a list-item with no lines - if (mBullet && HaveOutsideBullet() && !mLines) { + if (mBullet && HaveOutsideBullet() && mLines.empty()) { nsHTMLReflowMetrics metrics(nsnull); ReflowBullet(aState, metrics); @@ -2219,19 +2308,21 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) void nsBlockFrame::DeleteLine(nsBlockReflowState& aState, - nsLineBox* aLine) + nsLineList::iterator aLine, + nsLineList::iterator aLineEnd) { NS_PRECONDITION(0 == aLine->GetChildCount(), "can't delete !empty line"); if (0 == aLine->GetChildCount()) { - if (nsnull == aState.mPrevLine) { - NS_ASSERTION(aLine == mLines, "huh"); - mLines = nsnull; - } - else { - NS_ASSERTION(aState.mPrevLine->mNext == aLine, "bad prev-line"); - aState.mPrevLine->mNext = aLine->mNext; - } - aState.FreeLineBox(aLine); + NS_ASSERTION(aState.mCurrentLine == aLine, + "using function more generally than designed, " + "but perhaps OK now"); + nsLineBox *line = aLine; + aLine = mLines.erase(aLine); + aState.FreeLineBox(line); + // Mark the previous margin of the next line dirty since we need to + // recompute its top position. + if (aLine != aLineEnd) + aLine->MarkPreviousMarginDirty(); } } @@ -2242,7 +2333,7 @@ nsBlockFrame::DeleteLine(nsBlockReflowState& aState, */ nsresult nsBlockFrame::ReflowLine(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRBool aDamageDirtyArea) { @@ -2333,10 +2424,11 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, // XXX This approach doesn't work when floaters are involved in which case // we'll either need to recover the floater state that applies to the // unconstrained reflow or keep it around in a separate space manager... - PRBool isBeginningLine = !aState.mPrevLine || !aState.mPrevLine->IsLineWrapped(); + PRBool isBeginningLine = aState.mCurrentLine == begin_lines() || + !aState.mCurrentLine.prev()->IsLineWrapped(); if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) { nscoord oldY = aState.mY; - nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; + nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin); PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); // First reflow the line with an unconstrained width. When doing this @@ -2429,7 +2521,7 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, */ nsresult nsBlockFrame::PullFrame(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool aDamageDeletedLines, nsIFrame*& aFrameResult) { @@ -2438,8 +2530,8 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, aFrameResult = nsnull; // First check our remaining lines - while (nsnull != aLine->mNext) { - rv = PullFrameFrom(aState, aLine, &aLine->mNext, PR_FALSE, + while (end_lines() != aLine.next()) { + rv = PullFrameFrom(aState, aLine, mLines, aLine.next(), PR_FALSE, aDamageDeletedLines, aFrameResult, stopPulling); if (NS_FAILED(rv) || stopPulling) { return rv; @@ -2449,13 +2541,13 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, // Pull frames from the next-in-flow(s) until we can't nsBlockFrame* nextInFlow = aState.mNextInFlow; while (nsnull != nextInFlow) { - nsLineBox* line = nextInFlow->mLines; - if (nsnull == line) { + if (nextInFlow->mLines.empty()) { nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow; aState.mNextInFlow = nextInFlow; continue; } - rv = PullFrameFrom(aState, aLine, &nextInFlow->mLines, PR_TRUE, + rv = PullFrameFrom(aState, aLine, nextInFlow->mLines, + nextInFlow->mLines.begin(), PR_TRUE, aDamageDeletedLines, aFrameResult, stopPulling); if (NS_FAILED(rv) || stopPulling) { return rv; @@ -2465,11 +2557,10 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, } /** - * Try to pull a frame out of a line pointed at by aFromList. If a - * frame is pulled then aPulled will be set to PR_TRUE. In addition, - * if aUpdateGeometricParent is set then the pulled frames geometric - * parent will be updated (e.g. when pulling from a next-in-flows line - * list). + * Try to pull a frame out of a line pointed at by aFromLine. If a frame + * is pulled then aPulled will be set to PR_TRUE. In addition, if + * aUpdateGeometricParent is set then the pulled frames geometric parent + * will be updated (e.g. when pulling from a next-in-flows line list). * * Note: pulling a frame from a line that is a place-holder frame * doesn't automatically remove the corresponding floater from the @@ -2481,13 +2572,14 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, nsresult nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState, nsLineBox* aLine, - nsLineBox** aFromList, + nsLineList& aFromContainer, + nsLineList::iterator aFromLine, PRBool aUpdateGeometricParent, PRBool aDamageDeletedLines, nsIFrame*& aFrameResult, PRBool& aStopPulling) { - nsLineBox* fromLine = *aFromList; + nsLineBox* fromLine = aFromLine; NS_ABORT_IF_FALSE(fromLine, "bad line to pull from"); NS_ABORT_IF_FALSE(fromLine->GetChildCount(), "empty line"); NS_ABORT_IF_FALSE(aLine->GetChildCount(), "empty line"); @@ -2517,7 +2609,9 @@ nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState, if (aDamageDeletedLines) { Invalidate(aState.mPresContext, fromLine->mBounds); } - *aFromList = fromLine->mNext; + if (aFromLine.next() != end_lines()) + aFromLine.next()->MarkPreviousMarginDirty(); + aFromContainer.erase(aFromLine); aState.FreeLineBox(fromLine); } @@ -2563,15 +2657,19 @@ PlaceFrameView(nsIPresContext* aPresContext, nsContainerFrame::PositionChildViews(aPresContext, aFrame); } -// XXXldb If I understand correctly what's going on here, this should never -// be called with nonzero |aDY| on a line for which |IsBlock| is false. -// XXX_perf Shouldn't we optimize for |aDY == 0|? void nsBlockFrame::SlideLine(nsBlockReflowState& aState, nsLineBox* aLine, nscoord aDY) { + NS_PRECONDITION(aDY != 0, "why slide a line nowhere?"); + + PRBool doInvalidate = !aLine->mBounds.IsEmpty(); + if (doInvalidate) + Invalidate(aState.mPresContext, aLine->mBounds); // Adjust line state aLine->SlideBy(aDY); + if (doInvalidate) + Invalidate(aState.mPresContext, aLine->mBounds); // Adjust the frames in the line nsIFrame* kid = aLine->mFirstChild; @@ -2634,8 +2732,9 @@ nsresult nsBlockFrame::UpdateSpaceManager(nsIPresContext* aPresContext, nsISpaceManager* aSpaceManager) { - nsLineBox* line = mLines; - while (nsnull != line) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { // Place the floaters in the spacemanager if (line->HasFloaters()) { nsFloaterCache* fc = line->GetFirstFloater(); @@ -2663,7 +2762,7 @@ nsBlockFrame::UpdateSpaceManager(nsIPresContext* aPresContext, // the reflow began). nsBlockFrame* bf; nsresult rv = line->mFirstChild->QueryInterface(kBlockFrameCID, - (void**) &bf); + NS_REINTERPRET_CAST(void**, &bf)); if (NS_SUCCEEDED(rv)) { nsPoint origin; bf->GetOrigin(origin); @@ -2676,8 +2775,6 @@ nsBlockFrame::UpdateSpaceManager(nsIPresContext* aPresContext, aSpaceManager->Translate(-origin.x, -origin.y); } } - - line = line->mNext; } return NS_OK; @@ -2725,7 +2822,7 @@ nsBlockFrame::AttributeChanged(nsIPresContext* aPresContext, // make the assumption that all related list items share a // common block parent. // XXXldb I think that's a bad assumption. - while (nextAncestor != nsnull) { + while (nextAncestor) { if (NS_OK == nextAncestor->QueryInterface(kBlockFrameCID, (void**)&blockParent)) { break; @@ -2758,6 +2855,77 @@ nsBlockFrame::AttributeChanged(nsIPresContext* aPresContext, return rv; } +inline PRBool +IsBorderZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) +{ + return ((aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0)); +} + +inline PRBool +IsPaddingZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) +{ + return (aUnit == eStyleUnit_Null || + (aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) || + (aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0)); +} + +inline PRBool +IsMarginZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) +{ + return (aUnit == eStyleUnit_Null || + aUnit == eStyleUnit_Auto || + (aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) || + (aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0)); +} + +NS_IMETHODIMP +nsBlockFrame::IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool *aResult) +{ + // XXXldb In hindsight, I'm not sure why I made this check the margin, + // but it seems to work right and I'm a little hesitant to change it. + const nsStyleMargin* margin = NS_STATIC_CAST(const nsStyleMargin*, + mStyleContext->GetStyleData(eStyleStruct_Margin)); + const nsStyleBorder* border = NS_STATIC_CAST(const nsStyleBorder*, + mStyleContext->GetStyleData(eStyleStruct_Border)); + const nsStylePadding* padding = NS_STATIC_CAST(const nsStylePadding*, + mStyleContext->GetStyleData(eStyleStruct_Padding)); + nsStyleCoord coord; + if ((border->IsBorderSideVisible(NS_SIDE_TOP) && + !IsBorderZero(border->mBorder.GetTopUnit(), + border->mBorder.GetTop(coord))) || + (border->IsBorderSideVisible(NS_SIDE_BOTTOM) && + !IsBorderZero(border->mBorder.GetTopUnit(), + border->mBorder.GetTop(coord))) || + !IsPaddingZero(padding->mPadding.GetTopUnit(), + padding->mPadding.GetTop(coord)) || + !IsPaddingZero(padding->mPadding.GetBottomUnit(), + padding->mPadding.GetBottom(coord)) || + !IsMarginZero(margin->mMargin.GetTopUnit(), + margin->mMargin.GetTop(coord)) || + !IsMarginZero(margin->mMargin.GetBottomUnit(), + margin->mMargin.GetBottom(coord))) { + *aResult = PR_FALSE; + return NS_OK; + } + + + const nsStyleText* styleText = NS_STATIC_CAST(const nsStyleText*, + mStyleContext->GetStyleData(eStyleStruct_Text)); + PRBool isPre = + ((NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) || + (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace)); + *aResult = PR_TRUE; + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) + { + line->IsEmpty(aIsQuirkMode, isPre, aResult); + if (! *aResult) + break; + } + return NS_OK; +} + PRBool nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, nsLineBox* aLine) @@ -2776,8 +2944,9 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, } // Determine if this line is "essentially" the first line - nsLineBox* line = mLines; - while (line != aLine) { + for (line_iterator line = begin_lines(); + line != aLine; + ++line) { if (line->IsBlock()) { // A line which preceeds aLine contains a block; therefore the // top margin applies. @@ -2788,7 +2957,6 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, } // No need to apply the top margin if the line has floaters. We // should collapse anyway (bug 44419) - line = line->mNext; } // The line being reflowed is "essentially" the first line in the @@ -2800,17 +2968,18 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, nsIFrame* nsBlockFrame::GetTopBlockChild() { - nsIFrame* firstChild = mLines ? mLines->mFirstChild : nsnull; + nsIFrame* firstChild = mLines.empty() ? nsnull : mLines.front()->mFirstChild; if (firstChild) { - if (mLines->IsBlock()) { + if (mLines.front()->IsBlock()) { // Winner return firstChild; } // If the first line is not a block line then the second line must // be a block line otherwise the top child can't be a block. - nsLineBox* next = mLines->mNext; - if ((nsnull == next) || !next->IsBlock()) { + line_iterator next = begin_lines(); + ++next; + if ((next == end_lines()) || !next->IsBlock()) { // There is no line after the first line or its not a block so // don't bother trying to skip over the first line. return nsnull; @@ -2830,7 +2999,7 @@ nsBlockFrame::GetTopBlockChild() // See if each frame is a text frame that contains nothing but // whitespace. - PRInt32 n = mLines->GetChildCount(); + PRInt32 n = mLines.front()->GetChildCount(); while (--n >= 0) { nsIContent* content; nsresult rv = firstChild->GetContent(&content); @@ -2868,7 +3037,7 @@ nsBlockFrame::GetTopBlockChild() nsresult nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing) { NS_PRECONDITION(*aKeepReflowGoing, "bad caller"); @@ -2931,14 +3100,13 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState, frame, availSpace); - // Now compute the collapsed margin-top value - topMargin = - nsBlockReflowContext::ComputeCollapsedTopMargin(aState.mPresContext, - reflowState); - - // And collapse it with the previous bottom margin to get the final value - topMargin = - nsBlockReflowContext::MaxMargin(topMargin, aState.mPrevBottomMargin); + // Now compute the collapsed margin-top value into aState.mPrevBottomMargin + nsCollapsingMargin oldPrevBottomMargin = aState.mPrevBottomMargin; + nsBlockReflowContext::ComputeCollapsedTopMargin(aState.mPresContext, + reflowState, + aState.mPrevBottomMargin); + topMargin = aState.mPrevBottomMargin.get(); + aState.mPrevBottomMargin = oldPrevBottomMargin; // perhaps not needed // Temporarily advance the running Y value so that the // GetAvailableSpace method will return the right available @@ -2996,7 +3164,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) { // None of the child block fits. - PushLines(aState); + PushLines(aState, aLine.prev()); *aKeepReflowGoing = PR_FALSE; aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; } @@ -3005,11 +3173,12 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Try to place the child block PRBool isAdjacentWithTop = aState.IsAdjacentWithTop(); - nscoord collapsedBottomMargin; + nsCollapsingMargin collapsedBottomMargin; nsRect combinedArea(0,0,0,0); *aKeepReflowGoing = brc.PlaceBlock(isAdjacentWithTop, computedOffsets, - &collapsedBottomMargin, + collapsedBottomMargin, aLine->mBounds, combinedArea); + aLine->SetCarriedOutBottomMargin(collapsedBottomMargin); if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { // Mark the line as dirty so once we known the final shrink wrap width @@ -3033,7 +3202,6 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Advance to new Y position nscoord newY = aLine->mBounds.YMost(); aState.mY = newY; - aLine->SetCarriedOutBottomMargin(brc.GetCarriedOutBottomMargin()); // Continue the block frame now if it didn't completely fit in // the available space. @@ -3052,8 +3220,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, if (nsnull == line) { return NS_ERROR_OUT_OF_MEMORY; } - line->mNext = aLine->mNext; - aLine->mNext = line; + mLines.after_insert(aLine, line); // Do not count the continuation child on the line it used // to be on @@ -3062,8 +3229,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Advance to next line since some of the block fit. That way // only the following lines will be pushed. - aState.mPrevLine = aLine; - PushLines(aState); + PushLines(aState, aLine); aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; *aKeepReflowGoing = PR_FALSE; @@ -3078,7 +3244,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, printf(" prevBottomMargin=%d, setting to zero\n", aState.mPrevBottomMargin); #endif - aState.mPrevBottomMargin = 0; + aState.mPrevBottomMargin.Zero(); } else { #ifdef NOISY_VERTICAL_MARGINS @@ -3086,7 +3252,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, printf(": reflow complete for "); nsFrame::ListTag(stdout, frame); printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n", - aState.mPrevBottomMargin, collapsedBottomMargin); + aState.mPrevBottomMargin, collapsedBottomMargin.get()); #endif aState.mPrevBottomMargin = collapsedBottomMargin; } @@ -3095,7 +3261,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, printf(": frame="); nsFrame::ListTag(stdout, frame); printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n", - aLine->GetCarriedOutBottomMargin(), collapsedBottomMargin, + brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(), aState.mPrevBottomMargin); #endif @@ -3146,8 +3312,9 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // contains a block (example:

  • \n

    ... ). This is where // the second case can happen. if (mBullet && HaveOutsideBullet() && - ((aLine == mLines) || - ((0 == mLines->mBounds.height) && (aLine == mLines->mNext)))) { + ((aLine == mLines.front()) || + ((0 == mLines.front()->mBounds.height) && + (aLine == begin_lines().next())))) { // Reflow the bullet nsHTMLReflowMetrics metrics(nsnull); ReflowBullet(aState, metrics); @@ -3172,7 +3339,9 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Tall bullets won't look particularly nice here... nsRect bbox; mBullet->GetRect(bbox); - nscoord bulletTopMargin = applyTopMargin ? collapsedBottomMargin : 0; + nscoord bulletTopMargin = applyTopMargin + ? collapsedBottomMargin.get() + : 0; bbox.y = aState.BorderPadding().top + ascent - metrics.ascent + bulletTopMargin; mBullet->SetRect(aState.mPresContext, bbox); @@ -3180,7 +3349,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } else { // None of the block fits. Determine the correct reflow status. - if (aLine == mLines) { + if (aLine == mLines.front()) { // If it's our very first line then we need to be pushed to // our parents next-in-flow. Therefore, return break-before // status for our reflow status. @@ -3189,7 +3358,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, else { // Push the line that didn't fit and any lines that follow it // to our next-in-flow. - PushLines(aState); + PushLines(aState, aLine.prev()); aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; } } @@ -3206,7 +3375,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsresult nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRBool aDamageDirtyArea, PRBool aUpdateMaximumWidth) @@ -3249,7 +3418,7 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, nsresult nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRUint8* aLineReflowStatus, PRBool aUpdateMaximumWidth, @@ -3273,7 +3442,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsresult nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRUint8* aLineReflowStatus, PRBool aUpdateMaximumWidth, @@ -3294,7 +3463,7 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsresult nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRUint8* aLineReflowStatus, PRBool aUpdateMaximumWidth, @@ -3307,7 +3476,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, // Setup initial coordinate system for reflowing the inline frames // into. Apply a previous block frame's bottom margin first. - aState.mY += aState.mPrevBottomMargin; + aState.mY += aState.mPrevBottomMargin.get(); aState.GetAvailableSpace(); PRBool impactedByFloaters = aState.IsImpactedByFloater() ? PR_TRUE : PR_FALSE; aLine->SetLineIsImpactedByFloater(impactedByFloaters); @@ -3369,15 +3538,16 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, // It is possible that one or more of next lines are empty // (because of DeleteChildsNextInFlow). If so, delete them now // in case we are finished. - nsLineBox* nextLine = aLine->mNext; - while ((nsnull != nextLine) && (0 == nextLine->GetChildCount())) { + ++aLine; + while ((aLine != end_lines()) && (0 == aLine->GetChildCount())) { // XXX Is this still necessary now that DeleteChildsNextInFlow // uses DoRemoveFrame? - aLine->mNext = nextLine->mNext; - NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line"); - aState.FreeLineBox(nextLine); - nextLine = aLine->mNext; + nsLineBox *toremove = aLine; + aLine = mLines.erase(aLine); + NS_ASSERTION(nsnull == toremove->mFirstChild, "bad empty line"); + aState.FreeLineBox(toremove); } + --aLine; break; } frame->GetNextSibling(&frame); @@ -3452,7 +3622,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nsresult nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, nsIFrame* aFrame, PRUint8* aLineReflowStatus) { @@ -3579,7 +3749,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) { // Mark next line dirty in case SplitLine didn't end up // pushing any frames. - nsLineBox* next = aLine->mNext; + nsLineBox* next = aLine.next(); if ((nsnull != next) && !next->IsBlock()) { next->MarkDirty(); } @@ -3623,7 +3793,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Mark next line dirty in case SplitLine didn't end up // pushing any frames. - nsLineBox* next = aLine->mNext; + nsLineBox* next = aLine.next(); if ((nsnull != next) && !next->IsBlock()) { next->MarkDirty(); } @@ -3664,7 +3834,7 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState, nsresult nsBlockFrame::SplitLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, nsIFrame* aFrame) { NS_ABORT_IF_FALSE(aLine->IsInline(), "illegal SplitLine on block line"); @@ -3675,7 +3845,8 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, #ifdef DEBUG if (gNoisyReflow) { nsFrame::IndentBy(stdout, gNoiseIndent); - printf("split line: from line=%p pushCount=%d aFrame=", aLine, pushCount); + printf("split line: from line=%p pushCount=%d aFrame=", + NS_STATIC_CAST(void*, aLine.get()), pushCount); if (aFrame) { nsFrame::ListTag(stdout, aFrame); } @@ -3698,8 +3869,7 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, if (!newLine) { return NS_ERROR_OUT_OF_MEMORY; } - newLine->mNext = aLine->mNext; - aLine->mNext = newLine; + mLines.after_insert(aLine, newLine); aLine->SetChildCount(aLine->GetChildCount() - pushCount); #ifdef DEBUG if (gReallyNoisyReflow) { @@ -3718,32 +3888,31 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, } PRBool -nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine) +nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, + line_iterator aLine) { - nsLineBox* next = aLine->mNext; - while (nsnull != next) { + while (++aLine != end_lines()) { // There is another line - if (0 != next->GetChildCount()) { + if (0 != aLine->GetChildCount()) { // If the next line is a block line then we must not justify // this line because it means that this line is the last in a // group of inline lines. - return !next->IsBlock(); + return !aLine->IsBlock(); } - // The next line is empty, try the next one - next = next->mNext; } // XXX Not sure about this part // Try our next-in-flows lines to answer the question nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow; while (nsnull != nextInFlow) { - nsLineBox* line = nextInFlow->mLines; - while (nsnull != line) { - if (0 != line->GetChildCount()) { + for (line_iterator line = nextInFlow->begin_lines(), + line_end = nextInFlow->end_lines(); + line != line_end; + ++line) + { + if (0 != line->GetChildCount()) return !line->IsBlock(); - } - line = line->mNext; } nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow; } @@ -3755,15 +3924,14 @@ nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine) nsresult nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; // Trim extra white-space from the line before placing the frames - PRBool trimmed = aLineLayout.TrimTrailingWhiteSpace(); - aLine->SetTrimmed(trimmed); + aLineLayout.TrimTrailingWhiteSpace(); // Vertically align the frames on this line. // @@ -3781,8 +3949,8 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // case is happening then the worst that will happen is that the // bullet frame will be reflowed twice. PRBool addedBullet = PR_FALSE; - if (mBullet && HaveOutsideBullet() && (aLine == mLines) && - (!aLineLayout.IsZeroHeight() || !aLine->mNext)) { + if (mBullet && HaveOutsideBullet() && (aLine == mLines.front()) && + (!aLineLayout.IsZeroHeight() || (aLine == mLines.back()))) { nsHTMLReflowMetrics metrics(nsnull); ReflowBullet(aState, metrics); aLineLayout.AddBulletFrame(mBullet, metrics); @@ -3792,7 +3960,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nscoord lineAscent; aLineLayout.VerticalAlignLine(aLine, maxElementSize, lineAscent); // Our ascent is the ascent of our first line - if (aLine == mLines) { + if (aLine == mLines.front()) { mAscent = lineAscent; } @@ -3807,19 +3975,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } } #ifdef DEBUG - { - static nscoord lastHeight = 0; - if (CRAZY_HEIGHT(aLine->mBounds.y)) { - lastHeight = aLine->mBounds.y; - if (abs(aLine->mBounds.y - lastHeight) > CRAZY_H/10) { - nsFrame::ListTag(stdout); - printf(": line=%p y=%d line.bounds.height=%d\n", - aLine, aLine->mBounds.y, aLine->mBounds.height); - } - } - else { - lastHeight = 0; - } + { + static nscoord lastHeight = 0; + if (CRAZY_HEIGHT(aLine->mBounds.y)) { + lastHeight = aLine->mBounds.y; + if (abs(aLine->mBounds.y - lastHeight) > CRAZY_H/10) { + nsFrame::ListTag(stdout); + printf(": line=%p y=%d line.bounds.height=%d\n", + NS_STATIC_CAST(void*, aLine.get()), + aLine->mBounds.y, aLine->mBounds.height); + } + } + else { + lastHeight = 0; + } } #endif @@ -3870,8 +4039,8 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aState.mPresContext->GetBidiUtils(&bidiUtils); if (bidiUtils && bidiUtils->IsSuccessful() ) { - nsIFrame* nextInFlow = (aLine->mNext) - ? aLine->mNext->mFirstChild : nsnull; + nsIFrame* nextInFlow = (aLine.next() != end_lines()) + ? aLine.next()->mFirstChild : nsnull; bidiUtils->ReorderFrames(aState.mPresContext, aState.mReflowState.rendContext, @@ -3900,28 +4069,29 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, if (aLine->mBounds.height > 0) { // This line has some height. Therefore the application of the // previous-bottom-margin should stick. - aState.mPrevBottomMargin = 0; + aState.mPrevBottomMargin.Zero(); newY = aLine->mBounds.YMost(); } else { // Don't let the previous-bottom-margin value affect the newY // coordinate (it was applied in ReflowInlineFrames speculatively) // since the line is empty. - nscoord dy = -aState.mPrevBottomMargin; + nscoord dy = -aState.mPrevBottomMargin.get(); newY = aState.mY + dy; aLine->SlideBy(dy); // keep our ascent in sync - if (mLines == aLine) { + if (mLines.front() == aLine) { mAscent += dy; } } // See if the line fit. If it doesn't we need to push it. Our first // line will always fit. - if ((mLines != aLine) && (newY > aState.mBottomEdge)) { + if ((mLines.front() != aLine) && (newY > aState.mBottomEdge)) { // Push this line and all of it's children and anything else that // follows to our next-in-flow - PushLines(aState); + NS_ASSERTION((aState.mCurrentLine == aLine), "oops"); + PushLines(aState, aLine.prev()); // Stop reflow and whack the reflow status if reflow hasn't // already been stopped. @@ -4137,7 +4307,7 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, #ifdef DEBUG if (CRAZY_WIDTH(xmost)) { ListTag(stdout); - printf(": line=%p xmost=%d\n", aLine, xmost); + printf(": line=%p xmost=%d\n", NS_STATIC_CAST(void*, aLine), xmost); } #endif if (xmost > aState.mKidXMost) { @@ -4149,36 +4319,48 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, } void -nsBlockFrame::PushLines(nsBlockReflowState& aState) +nsBlockFrame::PushLines(nsBlockReflowState& aState, + nsLineList::iterator aLineBefore) { - NS_ASSERTION(nsnull != aState.mPrevLine, "bad push"); + nsLineList::iterator overBegin(aLineBefore.next()); + NS_ASSERTION(overBegin != begin_lines(), "bad push"); - nsLineBox* lastLine = aState.mPrevLine; - nsLineBox* nextLine = lastLine->mNext; - - if (nextLine) { - lastLine->mNext = nsnull; - SetOverflowLines(aState.mPresContext, nextLine); + if (overBegin != end_lines()) { + // XXXldb use presshell arena! + nsLineList* overflowLines = new nsLineList(); + overflowLines->splice(overflowLines->end(), mLines, overBegin, + end_lines()); + NS_ASSERTION(!overflowLines->empty(), "should not be empty"); + // this takes ownership but it won't delete it immediately so we + // can keep using it. + SetOverflowLines(aState.mPresContext, overflowLines); // Mark all the overflow lines dirty so that they get reflowed when // they are pulled up by our next-in-flow. // XXXldb Can this get called O(N) times making the whole thing O(N^2)? - while (nsnull != nextLine) { - nextLine->MarkDirty(); - nextLine = nextLine->mNext; + for (line_iterator line = overflowLines->begin(), + line_end = overflowLines->end(); + line != line_end; + ++line) + { + line->MarkDirty(); } } // Break frame sibling list - nsIFrame* lastFrame = lastLine->LastChild(); - lastFrame->SetNextSibling(nsnull); + aLineBefore->LastChild()->SetNextSibling(nsnull); #ifdef DEBUG VerifyOverflowSituation(aState.mPresContext); #endif } + +// The overflowLines property is stored as a pointer to a line list, +// which must be deleted. However, the following functions all maintain +// the invariant that the property is never set if the list is empty. + PRBool nsBlockFrame::DrainOverflowLines(nsIPresContext* aPresContext) { @@ -4186,18 +4368,20 @@ nsBlockFrame::DrainOverflowLines(nsIPresContext* aPresContext) VerifyOverflowSituation(aPresContext); #endif PRBool drained = PR_FALSE; - nsLineBox* overflowLines; + nsLineList* overflowLines; // First grab the prev-in-flows overflow lines nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow; if (nsnull != prevBlock) { overflowLines = prevBlock->GetOverflowLines(aPresContext, PR_TRUE); if (nsnull != overflowLines) { + NS_ASSERTION(! overflowLines->empty(), + "overflow lines should never be set and empty"); drained = PR_TRUE; // Make all the frames on the overflow line list mine nsIFrame* lastFrame = nsnull; - nsIFrame* frame = overflowLines->mFirstChild; + nsIFrame* frame = overflowLines->front()->mFirstChild; while (nsnull != frame) { frame->SetParent(this); @@ -4234,42 +4418,40 @@ nsBlockFrame::DrainOverflowLines(nsIPresContext* aPresContext) } // Join the line lists - if (nsnull == mLines) { - mLines = overflowLines; - } - else { + NS_ASSERTION(lastFrame, "overflow list was created with no frames"); + if (! mLines.empty()) { // Join the sibling lists together - lastFrame->SetNextSibling(mLines->mFirstChild); - - // Place overflow lines at the front of our line list - nsLineBox* lastLine = nsLineBox::LastLine(overflowLines); - lastLine->mNext = mLines; - mLines = overflowLines; + lastFrame->SetNextSibling(mLines.front()->mFirstChild); } + // Place overflow lines at the front of our line list + mLines.splice(mLines.begin(), *overflowLines); + NS_ASSERTION(overflowLines->empty(), "splice should empty list"); + delete overflowLines; } } // Now grab our own overflow lines overflowLines = GetOverflowLines(aPresContext, PR_TRUE); if (overflowLines) { + NS_ASSERTION(! overflowLines->empty(), + "overflow lines should never be set and empty"); // This can happen when we reflow and not everything fits and then // we are told to reflow again before a next-in-flow is created // and reflows. - nsLineBox* lastLine = nsLineBox::LastLine(mLines); - if (nsnull == lastLine) { // if we had no lines before the drain operation - mLines = overflowLines; // set our mLines to the overflow - } - else { // otherwise, append the overflow to the mLines list - lastLine->mNext = overflowLines; - nsIFrame* lastFrame = lastLine->LastChild(); - lastFrame->SetNextSibling(overflowLines->mFirstChild); + + if (! mLines.empty()) { + mLines.back()->LastChild()->SetNextSibling( + overflowLines->front()->mFirstChild ); } + // append the overflow to mLines + mLines.splice(mLines.end(), *overflowLines); drained = PR_TRUE; + delete overflowLines; } return drained; } -nsLineBox* +nsLineList* nsBlockFrame::GetOverflowLines(nsIPresContext* aPresContext, PRBool aRemoveProperty) const { @@ -4282,14 +4464,18 @@ nsBlockFrame::GetOverflowLines(nsIPresContext* aPresContext, if (frameManager) { PRUint32 options = 0; - void* value; + nsLineList* value; if (aRemoveProperty) { options |= NS_IFRAME_MGR_REMOVE_PROP; } - frameManager->GetFrameProperty((nsIFrame*)this, nsLayoutAtoms::overflowLinesProperty, - options, &value); - return (nsLineBox*)value; + frameManager->GetFrameProperty(NS_CONST_CAST(nsBlockFrame*, this), + nsLayoutAtoms::overflowLinesProperty, + options, + NS_REINTERPRET_CAST(void**, &value)); + NS_ASSERTION(!value || !value->empty(), + "value should never be stored as empty"); + return value; } } @@ -4304,26 +4490,34 @@ DestroyOverflowLines(nsIPresContext* aPresContext, void* aPropertyValue) { if (aPropertyValue) { - nsLineBox* lines = (nsLineBox*) aPropertyValue; - nsLineBox::DeleteLineList(aPresContext, lines); + nsLineList* lines = NS_STATIC_CAST(nsLineList*, aPropertyValue); + nsLineBox::DeleteLineList(aPresContext, *lines); + delete lines; } } +// This takes ownership of aOverflowLines. +// XXX We should allocate overflowLines from presShell arena! nsresult nsBlockFrame::SetOverflowLines(nsIPresContext* aPresContext, - nsLineBox* aOverflowFrames) + nsLineList* aOverflowLines) { nsCOMPtr presShell; nsresult rv = NS_ERROR_FAILURE; + NS_ASSERTION(aOverflowLines, "null lines"); + NS_ASSERTION(! aOverflowLines->empty(), "empty lines"); + aPresContext->GetShell(getter_AddRefs(presShell)); if (presShell) { nsCOMPtr frameManager; presShell->GetFrameManager(getter_AddRefs(frameManager)); if (frameManager) { - rv = frameManager->SetFrameProperty(this, nsLayoutAtoms::overflowLinesProperty, - aOverflowFrames, DestroyOverflowLines); + rv = frameManager->SetFrameProperty(this, + nsLayoutAtoms::overflowLinesProperty, + aOverflowLines, + DestroyOverflowLines); // Verify that we didn't overwrite an existing overflow list NS_ASSERTION(rv != NS_IFRAME_MGR_PROP_OVERWRITTEN, "existing overflow list"); @@ -4338,9 +4532,8 @@ nsBlockFrame::SetOverflowLines(nsIPresContext* aPresContext, nsIFrame* nsBlockFrame::LastChild() { - if (mLines) { - nsLineBox* line = nsLineBox::LastLine(mLines); - return line->LastChild(); + if (! mLines.empty()) { + return mLines.back()->LastChild(); } return nsnull; } @@ -4370,7 +4563,7 @@ nsBlockFrame::AppendFrames(nsIPresContext* aPresContext, // Find the proper last-child for where the append should go nsIFrame* lastKid = nsnull; - nsLineBox* lastLine = nsLineBox::LastLine(mLines); + nsLineBox* lastLine = mLines.empty() ? nsnull : mLines.back(); if (lastLine) { lastKid = lastLine->LastChild(); } @@ -4452,17 +4645,22 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext, aPresContext->GetShell(getter_AddRefs(presShell)); // Attempt to find the line that contains the previous sibling - nsLineBox* prevSibLine = nsnull; + nsLineList::iterator prevSibLine = end_lines(); PRInt32 prevSiblingIndex = -1; if (aPrevSibling) { + // XXX_perf This is technically O(N^2) in some cases, but by using + // RFind instead of Find, we make it O(N) in the most common case, + // which is appending cotent. + // Find the line that contains the previous sibling - prevSibLine = nsLineBox::FindLineContaining(mLines, aPrevSibling, - &prevSiblingIndex); - NS_ASSERTION(nsnull != prevSibLine, "prev sibling not in line list"); - if (nsnull == prevSibLine) { - // Note: defensive code! FindLineContaining must not return - // null in this case, so if it does... + if (! nsLineBox::RFindLineContaining(aPrevSibling, + begin_lines(), prevSibLine, + &prevSiblingIndex)) { + // Note: defensive code! RFindLineContaining must not return + // false in this case, so if it does... + NS_NOTREACHED("prev sibling not in line list"); aPrevSibling = nsnull; + prevSibLine = end_lines(); } } @@ -4481,8 +4679,7 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext, if (!line) { return NS_ERROR_OUT_OF_MEMORY; } - line->mNext = prevSibLine->mNext; - prevSibLine->mNext = line; + mLines.after_insert(prevSibLine, line); prevSibLine->SetChildCount(prevSibLine->GetChildCount() - rem); prevSibLine->MarkDirty(); } @@ -4490,8 +4687,8 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext, // Now (partially) join the sibling lists together aPrevSibling->SetNextSibling(aFrameList); } - else if (mLines) { - prevSiblingNextFrame = mLines->mFirstChild; + else if (! mLines.empty()) { + prevSiblingNextFrame = mLines.front()->mFirstChild; } // Walk through the new frames being added and update the line data @@ -4502,24 +4699,23 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext, // If the frame is a block frame, or if there is no previous line // or if the previous line is a block line then make a new line. - if (isBlock || !prevSibLine || prevSibLine->IsBlock()) { + if (isBlock || prevSibLine == end_lines() || prevSibLine->IsBlock()) { // Create a new line for the frame and add its line to the line // list. nsLineBox* line = NS_NewLineBox(presShell, newFrame, 1, isBlock); if (!line) { return NS_ERROR_OUT_OF_MEMORY; } - if (prevSibLine) { + if (prevSibLine != end_lines()) { // Append new line after prevSibLine - line->mNext = prevSibLine->mNext; - prevSibLine->mNext = line; + mLines.after_insert(prevSibLine, line); + ++prevSibLine; } else { // New line is going before the other lines - line->mNext = mLines; - mLines = line; + mLines.push_front(line); + prevSibLine = begin_lines(); } - prevSibLine = line; } else { prevSibLine->SetChildCount(prevSibLine->GetChildCount() + 1); @@ -4579,20 +4775,18 @@ nsBlockFrame::RemoveFrame(nsIPresContext* aPresContext, mFloaters.RemoveFrame(aOldFrame); // Find which line contains the floater - nsLineBox* line = mLines; - while (nsnull != line) { + line_iterator line = begin_lines(), line_end = end_lines(); + for ( ; line != line_end; ++line) { if (line->IsInline() && line->RemoveFloater(aOldFrame)) { aOldFrame->Destroy(aPresContext); - goto found_it; + break; } - line = line->mNext; } - found_it: // Mark every line at and below the line where the floater was dirty - while (nsnull != line) { + // XXXldb This could be done more efficiently. + for ( ; line != line_end; ++line) { line->MarkDirty(); - line = line->mNext; } } #ifdef IBMBIDI @@ -4627,11 +4821,11 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, // Find the line and the previous sibling that contains // deletedFrame; we also find the pointer to the line. nsBlockFrame* flow = this; - nsLineBox** linep = &flow->mLines; - nsLineBox* line = flow->mLines; - nsLineBox* prevLine = nsnull; + nsLineList& lines = flow->mLines; + nsLineList::iterator line = lines.begin(), + line_end = lines.end(); nsIFrame* prevSibling = nsnull; - while (nsnull != line) { + for ( ; line != line_end; ++line) { nsIFrame* frame = line->mFirstChild; PRInt32 n = line->GetChildCount(); while (--n >= 0) { @@ -4641,13 +4835,10 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, prevSibling = frame; frame->GetNextSibling(&frame); } - linep = &line->mNext; - prevLine = line; - line = line->mNext; } found_frame:; #ifdef NS_DEBUG - NS_ASSERTION(nsnull != line, "can't find deleted frame in lines"); + NS_ASSERTION(line != line_end, "can't find deleted frame in lines"); if (nsnull != prevSibling) { nsIFrame* tmp; prevSibling->GetNextSibling(&tmp); @@ -4657,7 +4848,7 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, // Remove frame and all of its continuations while (nsnull != aDeletedFrame) { - while ((nsnull != line) && (nsnull != aDeletedFrame)) { + while ((line != line_end) && (nsnull != aDeletedFrame)) { #ifdef NS_DEBUG nsIFrame* parent; aDeletedFrame->GetParent(&parent); @@ -4680,16 +4871,19 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, if (line->mFirstChild == aDeletedFrame) { line->mFirstChild = nextFrame; } - if (prevLine && !prevLine->IsBlock()) { + + --line; + if (line != line_end && !line->IsBlock()) { // Since we just removed a frame that follows some inline // frames, we need to reflow the previous line. - prevLine->MarkDirty(); + line->MarkDirty(); } + ++line; // Take aDeletedFrame out of the sibling list. Note that // prevSibling will only be nsnull when we are deleting the very // first frame. - if (nsnull != prevSibling) { + if (prevSibling) { prevSibling->SetNextSibling(nextFrame); } @@ -4703,8 +4897,7 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, nsSplittableFrame::RemoveFromFlow(aDeletedFrame); } #ifdef NOISY_REMOVE_FRAME - printf("DoRemoveFrame: prevLine=%p line=%p frame=", - prevLine, line); + printf("DoRemoveFrame: line=%p frame=", line); nsFrame::ListTag(stdout, aDeletedFrame); printf(" prevSibling=%p nextInFlow=%p\n", prevSibling, nextInFlow); #endif @@ -4712,24 +4905,29 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, aDeletedFrame = nextInFlow; // If line is empty, remove it now - nsLineBox* next = line->mNext; PRInt32 lineChildCount = line->GetChildCount(); if (0 == --lineChildCount) { - *linep = next; - line->mNext = nsnull; + nsLineBox *cur = line; + line = lines.erase(line); // Invalidate the space taken up by the line. // XXX We need to do this if we're removing a frame as a result of // a call to RemoveFrame(), but we may not need to do this in all // cases... nsRect lineCombinedArea; - line->GetCombinedArea(&lineCombinedArea); + cur->GetCombinedArea(&lineCombinedArea); #ifdef NOISY_BLOCK_INVALIDATE printf("%p invalidate 10 (%d, %d, %d, %d)\n", this, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height); #endif Invalidate(aPresContext, lineCombinedArea); - line->Destroy(presShell); - line = next; + cur->Destroy(presShell); + + // If we're removing a line, ReflowDirtyLines isn't going to + // know that it needs to slide lines unless something is marked + // dirty. So mark the previous margin of the next line dirty if + // there is one. + if (line != line_end) + line->MarkPreviousMarginDirty(); } else { // Make the line that just lost a frame dirty @@ -4739,9 +4937,7 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, // If we just removed the last frame on the line then we need // to advance to the next line. if (isLastFrameOnLine) { - prevLine = line; - linep = &line->mNext; - line = next; + ++line; } } @@ -4768,9 +4964,9 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext, NS_ASSERTION(nsnull != flow, "whoops, continuation without a parent"); // add defensive pointer check for bug 56894 if(flow) { - prevLine = nsnull; - line = flow->mLines; - linep = &flow->mLines; + lines = flow->mLines; + line = lines.begin(); + line_end = lines.end(); prevSibling = nsnull; } } @@ -4799,7 +4995,7 @@ nsBlockFrame::DeleteChildsNextInFlow(nsIPresContext* aPresContext, nsBlockFrame* parent; nextInFlow->GetParent((nsIFrame**)&parent); NS_PRECONDITION(nsnull != parent, "next-in-flow with no parent"); - NS_PRECONDITION(nsnull != parent->mLines, "next-in-flow with weird parent"); + NS_PRECONDITION(! parent->mLines.empty(), "next-in-flow with weird parent"); // NS_PRECONDITION(nsnull == parent->mOverflowLines, "parent with overflow"); parent->DoRemoveFrame(aPresContext, nextInFlow); #ifdef IBMBIDI @@ -4861,7 +5057,9 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, PRBool isAdjacentWithTop = aState.IsAdjacentWithTop(); nsReflowStatus frameReflowStatus; - nsresult rv = brc.ReflowBlock(floater, availSpace, PR_TRUE, 0, isAdjacentWithTop, + nsCollapsingMargin margin; + nsresult rv = brc.ReflowBlock(floater, availSpace, PR_TRUE, margin, + isAdjacentWithTop, aComputedOffsetsResult, frameReflowStatus); if (NS_SUCCEEDED(rv) && isAutoWidth) { nscoord maxElementWidth = brc.GetMaxElementSize().width; @@ -4870,7 +5068,9 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, // width. Reflow it again, this time pinning the width to the // maxElementSize. availSpace.width = maxElementWidth; - rv = brc.ReflowBlock(floater, availSpace, PR_TRUE, 0, isAdjacentWithTop, + nsCollapsingMargin marginMES; + rv = brc.ReflowBlock(floater, availSpace, PR_TRUE, marginMES, + isAdjacentWithTop, aComputedOffsetsResult, frameReflowStatus); } } @@ -4893,9 +5093,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, const nsMargin& m = brc.GetMargin(); aMarginResult.top = brc.GetTopMargin(); aMarginResult.right = m.right; - aMarginResult.bottom = - nsBlockReflowContext::MaxMargin(brc.GetCarriedOutBottomMargin(), - m.bottom); + brc.GetCarriedOutBottomMargin().Include(m.bottom); + aMarginResult.bottom = brc.GetCarriedOutBottomMargin().get(); aMarginResult.left = m.left; const nsHTMLReflowMetrics& metrics = brc.GetMetrics(); @@ -4903,6 +5102,10 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState, // Set the rect, make sure the view is properly sized and positioned, // and tell the frame we're done reflowing it + // XXXldb This seems like the wrong place to be doing this -- shouldn't + // we be doing this in nsBlockReflowState::FlowAndPlaceFloater after + // we've positioned the floater, and shouldn't we be doing the equivalent + // of |::PlaceFrameView| here? floater->SizeTo(aState.mPresContext, metrics.width, metrics.height); nsIView* view; floater->GetView(aState.mPresContext, &view); @@ -4944,16 +5147,18 @@ nsBlockFrame::GetSkipSides() const } #ifdef DEBUG -static void ComputeCombinedArea(nsLineBox* aLine, +static void ComputeCombinedArea(nsLineList& aLines, nscoord aWidth, nscoord aHeight, nsRect& aResult) { nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight; - while (nsnull != aLine) { + for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end(); + line != line_end; + ++line) { // Compute min and max x/y values for the reflowed frame's // combined areas nsRect lineCombinedArea; - aLine->GetCombinedArea(&lineCombinedArea); + line->GetCombinedArea(&lineCombinedArea); nscoord x = lineCombinedArea.x; nscoord y = lineCombinedArea.y; nscoord xmost = x + lineCombinedArea.width; @@ -4970,7 +5175,6 @@ static void ComputeCombinedArea(nsLineBox* aLine, if (ymost > yb) { yb = ymost; } - aLine = aLine->mNext; } aResult.x = xa; @@ -5154,7 +5358,9 @@ nsBlockFrame::PaintFloaters(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect) { - for (nsLineBox* line = mLines; nsnull != line; line = line->mNext) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { if (!line->HasFloaters()) { continue; } @@ -5194,7 +5400,9 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext, } #endif - for (nsLineBox* line = mLines; nsnull != line; line = line->mNext) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { // If the line's combined area (which includes child frames that // stick outside of the line's bounding box or our bounding box) // intersects the dirty rect then paint the line. @@ -5206,7 +5414,8 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext, line->GetCombinedArea(&lineCombinedArea); nsFrame::IndentBy(stdout, depth+1); printf("draw line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n", - line, line->mBounds.x, line->mBounds.y, + NS_STATIC_CAST(void*, line.get()), + line->mBounds.x, line->mBounds.y, line->mBounds.width, line->mBounds.height, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height); @@ -5231,7 +5440,8 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext, line->GetCombinedArea(&lineCombinedArea); nsFrame::IndentBy(stdout, depth+1); printf("skip line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n", - line, line->mBounds.x, line->mBounds.y, + NS_STATIC_CAST(void*, line.get()), + line->mBounds.x, line->mBounds.y, line->mBounds.width, line->mBounds.height, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height); @@ -5252,7 +5462,7 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext, if (gLamePaintMetrics) { PRTime end = PR_Now(); - PRInt32 numLines = nsLineBox::ListLength(mLines); + PRInt32 numLines = mLines.size(); if (!numLines) numLines = 1; PRTime lines, deltaPerLine, delta; LL_I2L(lines, numLines); @@ -5386,15 +5596,13 @@ nsBlockFrame::HandleEvent(nsIPresContext* aPresContext, nsIFrame *resultFrame = nsnull;//this will be passed the handle event when we //can tell who to pass it to - nsCOMPtr it; nsIFrame *mainframe = this; - nsCOMPtr tracker; - aPresContext->GetShell(getter_AddRefs(shell)); + aPresContext->GetShell(getter_AddRefs(shell)); if (!shell) return NS_OK; - result = shell->QueryInterface(NS_GET_IID(nsIFocusTracker),getter_AddRefs(tracker)); + nsCOMPtr tracker( do_QueryInterface(shell, &result) ); - result = mainframe->QueryInterface(NS_GET_IID(nsILineIterator),getter_AddRefs(it)); + nsCOMPtr it( do_QueryInterface(mainframe, &result) ); nsIView* parentWithView; nsPoint origin; nsPeekOffsetStruct pos; @@ -5426,7 +5634,7 @@ nsBlockFrame::HandleEvent(nsIPresContext* aPresContext, if (NS_SUCCEEDED(result) && pos.mResultFrame){ if (result == NS_OK) - result = pos.mResultFrame->QueryInterface(NS_GET_IID(nsILineIterator),getter_AddRefs(it));//if this fails thats ok + it = do_QueryInterface(pos.mResultFrame, &result);//if this fails thats ok resultFrame = pos.mResultFrame; mainframe = resultFrame; } @@ -5590,18 +5798,19 @@ nsBlockFrame::ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild) // Mark the line containing the child frame dirty. PRBool isFloater; - nsLineBox* prevLine; - nsLineBox* line = FindLineFor(aChild, &prevLine, &isFloater); + line_iterator fline; + FindLineFor(aChild, &isFloater, &fline); if (!isFloater) { - if (line) - MarkLineDirty(line, prevLine); + if (fline != end_lines()) + MarkLineDirty(fline); } else { - line = mLines; - while (nsnull != line) { + // XXXldb Could we do this more efficiently? + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { line->MarkDirty(); - line = line->mNext; } } } @@ -5658,27 +5867,28 @@ nsBlockFrame::ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild) #ifdef NS_DEBUG static PRBool -InLineList(nsLineBox* aLines, nsIFrame* aFrame) +InLineList(nsLineList& aLines, nsIFrame* aFrame) { - while (nsnull != aLines) { - nsIFrame* frame = aLines->mFirstChild; - PRInt32 n = aLines->GetChildCount(); + for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end(); + line != line_end; + ++line) { + nsIFrame* frame = line->mFirstChild; + PRInt32 n = line->GetChildCount(); while (--n >= 0) { if (frame == aFrame) { return PR_TRUE; } frame->GetNextSibling(&frame); } - aLines = aLines->mNext; } return PR_FALSE; } static PRBool -InSiblingList(nsLineBox* aLine, nsIFrame* aFrame) +InSiblingList(nsLineList& aLines, nsIFrame* aFrame) { - if (nsnull != aLine) { - nsIFrame* frame = aLine->mFirstChild; + if (! aLines.empty()) { + nsIFrame* frame = aLines.front()->mFirstChild; while (nsnull != frame) { if (frame == aFrame) { return PR_TRUE; @@ -5700,8 +5910,8 @@ nsBlockFrame::IsChild(nsIPresContext* aPresContext, nsIFrame* aFrame) if (InLineList(mLines, aFrame) && InSiblingList(mLines, aFrame)) { return PR_TRUE; } - nsLineBox* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); - if (InLineList(overflowLines, aFrame) && InSiblingList(overflowLines, aFrame)) { + nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); + if (InLineList(*overflowLines, aFrame) && InSiblingList(*overflowLines, aFrame)) { return PR_TRUE; } return PR_FALSE; @@ -5724,12 +5934,12 @@ nsBlockFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const PRUint32 sum = sizeof(*this); // Add in size of each line object - nsLineBox* line = mLines; - while (line) { + for (const_line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { PRUint32 lineBoxSize; nsIAtom* atom = line->SizeOf(aHandler, &lineBoxSize); aHandler->AddSize(atom, lineBoxSize); - line = line->mNext; } *aResult = sum; @@ -5900,8 +6110,10 @@ nsBlockFrame::RenumberListsInBlock(nsIPresContext* aPresContext, while (nsnull != aBlockFrame) { // Examine each line in the block - nsLineBox* line = aBlockFrame->mLines; - while (line) { + for (line_iterator line = aBlockFrame->begin_lines(), + line_end = aBlockFrame->end_lines(); + line != line_end; + ++line) { nsIFrame* kid = line->mFirstChild; PRInt32 n = line->GetChildCount(); while (--n >= 0) { @@ -5912,7 +6124,6 @@ nsBlockFrame::RenumberListsInBlock(nsIPresContext* aPresContext, } kid->GetNextSibling(&kid); } - line = line->mNext; } // Advance to the next continuation @@ -6038,8 +6249,9 @@ nsBlockFrame::BuildFloaterList() { nsIFrame* head = nsnull; nsIFrame* current = nsnull; - nsLineBox* line = mLines; - while (nsnull != line) { + for (line_iterator line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { if (line->HasFloaters()) { nsFloaterCache* fc = line->GetFirstFloater(); while (fc) { @@ -6054,7 +6266,6 @@ nsBlockFrame::BuildFloaterList() fc = fc->Next(); } } - line = line->mNext; } // Terminate end of floater list just in case a floater was removed @@ -6073,8 +6284,7 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK) if (!gVerifyLines) { return; } - nsLineBox* line = mLines; - if (!line) { + if (mLines.empty()) { return; } @@ -6082,7 +6292,10 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK) // set properly. PRInt32 count = 0; PRBool seenBlock = PR_FALSE; - while (nsnull != line) { + line_iterator line, line_end; + for (line = begin_lines(), line_end = end_lines(); + line != line_end; + ++line) { if (aFinalCheckOK) { NS_ABORT_IF_FALSE(line->GetChildCount(), "empty line"); if (line->IsBlock()) { @@ -6093,12 +6306,11 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK) } } count += line->GetChildCount(); - line = line->mNext; } // Then count the frames PRInt32 frameCount = 0; - nsIFrame* frame = mLines->mFirstChild; + nsIFrame* frame = mLines.front()->mFirstChild; while (nsnull != frame) { frameCount++; frame->GetNextSibling(&frame); @@ -6106,17 +6318,16 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK) NS_ASSERTION(count == frameCount, "bad line list"); // Next: test that each line has right number of frames on it - line = mLines; - nsLineBox* prevLine = nsnull; - while (nsnull != line) { + for (line = begin_lines(), line_end = end_lines(); + line != line_end; + ) { count = line->GetChildCount(); frame = line->mFirstChild; while (--count >= 0) { frame->GetNextSibling(&frame); } - prevLine = line; - line = line->mNext; - if ((nsnull != line) && (0 != line->GetChildCount())) { + ++line; + if ((line != line_end) && (0 != line->GetChildCount())) { NS_ASSERTION(frame == line->mFirstChild, "bad line list"); } } @@ -6130,10 +6341,10 @@ nsBlockFrame::VerifyOverflowSituation(nsIPresContext* aPresContext) { nsBlockFrame* flow = (nsBlockFrame*) GetFirstInFlow(); while (nsnull != flow) { - nsLineBox* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); + nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_FALSE); if (nsnull != overflowLines) { - NS_ASSERTION(nsnull != overflowLines->mFirstChild, - "bad overflow list"); + NS_ASSERTION(! overflowLines->empty(), "should not be empty if present"); + NS_ASSERTION(overflowLines->front()->mFirstChild, "bad overflow list"); } flow = (nsBlockFrame*) flow->mNextInFlow; } diff --git a/mozilla/layout/html/base/src/nsBlockFrame.h b/mozilla/layout/html/base/src/nsBlockFrame.h index 7c501433fc7..efc8f52558b 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.h +++ b/mozilla/layout/html/base/src/nsBlockFrame.h @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -40,12 +41,14 @@ #include "nsHTMLContainerFrame.h" #include "nsHTMLParts.h" #include "nsAbsoluteContainingBlock.h" +#include "nsLineBox.h" class nsBlockReflowState; class nsBulletFrame; class nsLineBox; class nsFirstLineFrame; class nsILineIterator; +class nsIntervalSet; /** * Child list name indices * @see #GetAdditionalChildListName() @@ -71,6 +74,22 @@ extern const nsIID kBlockFrameCID; */ class nsBlockFrame : public nsBlockFrameSuper { +public: + typedef nsLineList::iterator line_iterator; + typedef nsLineList::const_iterator const_line_iterator; + typedef nsLineList::reverse_iterator reverse_line_iterator; + typedef nsLineList::const_reverse_iterator const_reverse_line_iterator; + +protected: + line_iterator begin_lines() { return mLines.begin(); } + line_iterator end_lines() { return mLines.end(); } + const_line_iterator begin_lines() const { return mLines.begin(); } + const_line_iterator end_lines() const { return mLines.end(); } + reverse_line_iterator rbegin_lines() { return mLines.rbegin(); } + reverse_line_iterator rend_lines() { return mLines.rend(); } + const_reverse_line_iterator rbegin_lines() const { return mLines.rbegin(); } + const_reverse_line_iterator rend_lines() const { return mLines.rend(); } + public: friend nsresult NS_NewBlockFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aFlags); @@ -130,6 +149,8 @@ public: PRBool aCheckVis, PRBool* aIsVisible); + NS_IMETHOD IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool* aResult); + // nsIHTMLReflow NS_IMETHOD Reflow(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, @@ -173,8 +194,10 @@ public: nsresult UpdateSpaceManager(nsIPresContext* aPresContext, nsISpaceManager* aSpaceManager); - nsLineBox* FindLineFor(nsIFrame* aFrame, nsLineBox** aPrevLineResult, - PRBool* aIsFloaterResult); + // returns true on success and false if aFoundLine is set to end_lines() + PRBool FindLineFor(nsIFrame* aFrame, + PRBool* aIsFloaterResult, + line_iterator* aFoundLine); static nsresult GetCurrentLine(nsBlockReflowState *aState, nsLineBox **aOutCurrentLine); @@ -278,11 +301,6 @@ protected: /** reflow all lines that have been marked dirty */ nsresult ReflowDirtyLines(nsBlockReflowState& aState); - /** set aState to what it would be if we had done a full reflow to this point. */ - void RecoverStateFrom(nsBlockReflowState& aState, - nsLineBox* aLine, - nsRect* aDamageRect); - //---------------------------------------- // Methods for line reflow /** @@ -295,18 +313,21 @@ protected: * this indicates that frames may have changed size, for example */ nsresult ReflowLine(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRBool aDamageDirtyArea = PR_FALSE); nsresult PlaceLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRBool aUpdateMaximumWidth); - nsresult MarkLineDirty (nsLineBox* aLine, - nsLineBox* aPrevLine); + /** + * Mark |aLine| dirty, and, if necessary because of possible + * pull-up, mark the previous line dirty as well. + */ + nsresult MarkLineDirty(line_iterator aLine); // XXX blech void PostPlaceLine(nsBlockReflowState& aState, @@ -318,9 +339,12 @@ protected: nsSize* aMaxElementSize); // XXX where to go - PRBool ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine); + PRBool ShouldJustifyLine(nsBlockReflowState& aState, + line_iterator aLine); - void DeleteLine(nsBlockReflowState& aState, nsLineBox* aLine); + void DeleteLine(nsBlockReflowState& aState, + nsLineList::iterator aLine, + nsLineList::iterator aLineEnd); //---------------------------------------- // Methods for individual frame reflow @@ -329,32 +353,32 @@ protected: nsLineBox* aLine); nsresult ReflowBlockFrame(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepGoing); nsresult ReflowInlineFrames(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepLineGoing, PRBool aDamageDirtyArea, PRBool aUpdateMaximumWidth = PR_FALSE); nsresult DoReflowInlineFrames(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRUint8* aLineReflowStatus, PRBool aUpdateMaximumWidth, PRBool aDamageDirtyArea); nsresult DoReflowInlineFramesAuto(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRUint8* aLineReflowStatus, PRBool aUpdateMaximumWidth, PRBool aDamageDirtyArea); nsresult DoReflowInlineFramesMalloc(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool* aKeepReflowGoing, PRUint8* aLineReflowStatus, PRBool aUpdateMaximumWidth, @@ -362,7 +386,7 @@ protected: nsresult ReflowInlineFrame(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, nsIFrame* aFrame, PRUint8* aLineReflowStatus); @@ -382,23 +406,25 @@ protected: nsresult SplitLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, - nsLineBox* aLine, + line_iterator aLine, nsIFrame* aFrame); nsresult PullFrame(nsBlockReflowState& aState, - nsLineBox* aLine, + line_iterator aLine, PRBool aDamageDeletedLine, nsIFrame*& aFrameResult); nsresult PullFrameFrom(nsBlockReflowState& aState, nsLineBox* aToLine, - nsLineBox** aFromList, + nsLineList& aFromContainer, + nsLineList::iterator aFromLine, PRBool aUpdateGeometricParent, PRBool aDamageDeletedLines, nsIFrame*& aFrameResult, PRBool& aStopPulling); - void PushLines(nsBlockReflowState& aState); + void PushLines(nsBlockReflowState& aState, + nsLineList::iterator aLineBefore); //---------------------------------------- //XXX @@ -412,10 +438,13 @@ protected: nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect); - void PropagateReflowDamage(nsBlockReflowState& aState, + void RememberFloaterDamage(nsBlockReflowState& aState, nsLineBox* aLine, - const nsRect& aOldCombinedArea, - nscoord aDeltaY); + const nsRect& aOldCombinedArea); + + void PropagateFloaterDamage(nsBlockReflowState& aState, + nsLineBox* aLine, + nscoord aDeltaY); void BuildFloaterList(); @@ -440,11 +469,11 @@ protected: //---------------------------------------- - nsLineBox* GetOverflowLines(nsIPresContext* aPresContext, - PRBool aRemoveProperty) const; + nsLineList* GetOverflowLines(nsIPresContext* aPresContext, + PRBool aRemoveProperty) const; nsresult SetOverflowLines(nsIPresContext* aPresContext, - nsLineBox* aOverflowFrames); + nsLineList* aOverflowLines); nsIFrame* LastChild(); @@ -458,7 +487,7 @@ protected: // Ascent of our first line to support 'vertical-align: baseline' in table-cells nscoord mAscent; - nsLineBox* mLines; + nsLineList mLines; // List of all floaters in this block nsFrameList mFloaters; diff --git a/mozilla/layout/html/base/src/nsBlockReflowContext.cpp b/mozilla/layout/html/base/src/nsBlockReflowContext.cpp index 9b2eec23b32..c03a10c1f2f 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowContext.cpp +++ b/mozilla/layout/html/base/src/nsBlockReflowContext.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -80,25 +81,31 @@ nsBlockReflowContext::nsBlockReflowContext(nsIPresContext* aPresContext, mMetrics.mFlags |= NS_REFLOW_CALC_MAX_WIDTH; } -nscoord +void nsBlockReflowContext::ComputeCollapsedTopMargin(nsIPresContext* aPresContext, - nsHTMLReflowState& aRS) + nsHTMLReflowState& aRS, + /* inout */ nsCollapsingMargin& aMargin) { // Get aFrame's top margin - nscoord topMargin = aRS.mComputedMargin.top; + aMargin.Include(aRS.mComputedMargin.top); + +#ifdef NOISY_VERTICAL_MARGINS + nsFrame::ListTag(stdout, aRS.frame); + printf(": %d => %d\n", aRS.mComputedMargin.top, aMargin.get()); +#endif // Calculate aFrame's generational top-margin from its child // blocks. Note that if aFrame has a non-zero top-border or // top-padding then this step is skipped because it will be a margin // root. It is also skipped if the frame is a margin root for other // reasons. - nscoord generationalTopMargin = 0; if (0 == aRS.mComputedBorderPadding.top) { nsFrameState state; aRS.frame->GetFrameState(&state); if (!(state & NS_BLOCK_MARGIN_ROOT)) { nsBlockFrame* bf; - if (NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, (void**)&bf))) { + if (NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, + NS_REINTERPRET_CAST(void**, &bf)))) { // Ask the block frame for the top block child that we should // try to collapse the top margin with. @@ -109,7 +116,7 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(nsIPresContext* aPresContext, nsIFrame* childFrame = bf->GetTopBlockChild(); if (nsnull != childFrame) { - // Here is where we recurse. Now that we have determined that a + // Here is where we recur. Now that we have determined that a // generational collapse is required we need to compute the // child blocks margin and so in so that we can look into // it. For its margins to be computed we need to have a reflow @@ -117,24 +124,16 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(nsIPresContext* aPresContext, nsSize availSpace(aRS.mComputedWidth, aRS.mComputedHeight); nsHTMLReflowState reflowState(aPresContext, aRS, childFrame, availSpace); - generationalTopMargin = - ComputeCollapsedTopMargin(aPresContext, reflowState); + ComputeCollapsedTopMargin(aPresContext, reflowState, aMargin); } } } } - // Now compute the collapsed top-margin value. At this point we have - // the child frames effective top margin value. - nscoord collapsedTopMargin = MaxMargin(topMargin, generationalTopMargin); - #ifdef NOISY_VERTICAL_MARGINS nsFrame::ListTag(stdout, aRS.frame); - printf(": topMargin=%d generationalTopMargin=%d => %d\n", - topMargin, generationalTopMargin, collapsedTopMargin); + printf(": => %d\n", aMargin.get()); #endif - - return collapsedTopMargin; } struct nsBlockHorizontalAlign { @@ -245,7 +244,7 @@ nsresult nsBlockReflowContext::ReflowBlock(nsIFrame* aFrame, const nsRect& aSpace, PRBool aApplyTopMargin, - nscoord aPrevBottomMargin, + nsCollapsingMargin& aPrevBottomMargin, PRBool aIsAdjacentWithTop, nsMargin& aComputedOffsets, nsReflowStatus& aFrameReflowStatus) @@ -422,7 +421,7 @@ nsBlockReflowContext::DoReflowBlock(nsHTMLReflowState &aReflowState, nsIFrame* aFrame, const nsRect& aSpace, PRBool aApplyTopMargin, - nscoord aPrevBottomMargin, + nsCollapsingMargin& aPrevBottomMargin, PRBool aIsAdjacentWithTop, nsMargin& aComputedOffsets, nsReflowStatus& aFrameReflowStatus) @@ -438,32 +437,25 @@ nsBlockReflowContext::DoReflowBlock(nsHTMLReflowState &aReflowState, mIsTable = NS_STYLE_DISPLAY_TABLE == aReflowState.mStyleDisplay->mDisplay; mComputedWidth = aReflowState.mComputedWidth; - nscoord topMargin = 0; if (aApplyTopMargin) { // Compute the childs collapsed top margin (its margin collpased // with its first childs top-margin -- recursively). - topMargin = ComputeCollapsedTopMargin(mPresContext, aReflowState); + ComputeCollapsedTopMargin(mPresContext, aReflowState, aPrevBottomMargin); #ifdef NOISY_VERTICAL_MARGINS nsFrame::ListTag(stdout, mOuterReflowState.frame); printf(": reflowing "); nsFrame::ListTag(stdout, aFrame); - printf(" prevBottomMargin=%d, collapsedTopMargin=%d => %d\n", - aPrevBottomMargin, topMargin, - MaxMargin(topMargin, aPrevBottomMargin)); + printf(" margin => %d\n", aPrevBottomMargin.get()); #endif - // Collapse that value with the previous bottom margin to perform - // the sibling to sibling collaspe. - topMargin = MaxMargin(topMargin, aPrevBottomMargin); - // Adjust the available height if its constrained so that the // child frame doesn't think it can reflow into its margin area. - if (aApplyTopMargin && (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) { - aReflowState.availableHeight -= topMargin; + if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) { + aReflowState.availableHeight -= aPrevBottomMargin.get(); } } - mTopMargin = topMargin; + mTopMargin = aPrevBottomMargin.get(); // Compute x/y coordinate where reflow will begin. Use the rules // from 10.3.3 to determine what to apply. At this point in the @@ -473,7 +465,7 @@ nsBlockReflowContext::DoReflowBlock(nsHTMLReflowState &aReflowState, mStyleMargin = aReflowState.mStyleMargin; mStylePadding = aReflowState.mStylePadding; nscoord x; - nscoord y = aSpace.y + topMargin; + nscoord y = aSpace.y + mTopMargin; // If it's a right floated element, then calculate the x-offset // differently @@ -698,14 +690,13 @@ nsBlockReflowContext::DoReflowBlock(nsHTMLReflowState &aReflowState, PRBool nsBlockReflowContext::PlaceBlock(PRBool aForceFit, const nsMargin& aComputedOffsets, - nscoord* aBottomMarginResult, + nsCollapsingMargin& aBottomMarginResult, nsRect& aInFlowBounds, nsRect& aCombinedRect) { // Compute collapsed bottom margin value - nscoord collapsedBottomMargin = MaxMargin(mMetrics.mCarriedOutBottomMargin, - mMargin.bottom); - *aBottomMarginResult = collapsedBottomMargin; + aBottomMarginResult = mMetrics.mCarriedOutBottomMargin; + aBottomMarginResult.Include(mMargin.bottom); // See if the block will fit in the available space PRBool fits = PR_TRUE; @@ -723,8 +714,7 @@ nsBlockReflowContext::PlaceBlock(PRBool aForceFit, { // Collapse the bottom margin with the top margin that was already // applied. - nscoord newBottomMargin = MaxMargin(collapsedBottomMargin, mTopMargin); - *aBottomMarginResult = newBottomMargin; + aBottomMarginResult.Include(mTopMargin); #ifdef NOISY_VERTICAL_MARGINS printf(" "); nsFrame::ListTag(stdout, mOuterReflowState.frame); diff --git a/mozilla/layout/html/base/src/nsBlockReflowContext.h b/mozilla/layout/html/base/src/nsBlockReflowContext.h index 5f12c739557..30422ec4b03 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowContext.h +++ b/mozilla/layout/html/base/src/nsBlockReflowContext.h @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -70,20 +71,20 @@ public: nsresult ReflowBlock(nsIFrame* aFrame, const nsRect& aSpace, PRBool aApplyTopMargin, - nscoord aPrevBottomMargin, + nsCollapsingMargin& aPrevBottomMargin, PRBool aIsAdjacentWithTop, nsMargin& aComputedOffsets, nsReflowStatus& aReflowStatus); PRBool PlaceBlock(PRBool aForceFit, const nsMargin& aComputedOffsets, - nscoord* aBottomMarginResult, + nsCollapsingMargin& aBottomMarginResult /* out */, nsRect& aInFlowBounds, nsRect& aCombinedRect); void AlignBlockHorizontally(nscoord aWidth, nsBlockHorizontalAlign&); - nscoord GetCarriedOutBottomMargin() const { + nsCollapsingMargin& GetCarriedOutBottomMargin() { return mMetrics.mCarriedOutBottomMargin; } @@ -111,25 +112,9 @@ public: return mBlockShouldInvalidateItself; } - // Compute the largest of two adjacent vertical margins, as per the - // CSS2 spec section 8.3.1 - static nscoord MaxMargin(nscoord a, nscoord b) { - if (a < 0) { - if (b < 0) { - if (a < b) return a; - return b; - } - return b + a; - } - else if (b < 0) { - return a + b; - } - if (a > b) return a; - return b; - } - - static nscoord ComputeCollapsedTopMargin(nsIPresContext* aPresContext, - nsHTMLReflowState& aRS); + static void ComputeCollapsedTopMargin(nsIPresContext* aPresContext, + nsHTMLReflowState& aRS, + /* inout */ nsCollapsingMargin& aMargin); protected: nsStyleUnit GetRealMarginLeftUnit(); @@ -140,7 +125,7 @@ protected: nsIFrame* aFrame, const nsRect& aSpace, PRBool aApplyTopMargin, - nscoord aPrevBottomMargin, + nsCollapsingMargin& aPrevBottomMargin, PRBool aIsAdjacentWithTop, nsMargin& aComputedOffsets, nsReflowStatus& aReflowStatus); diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.cpp b/mozilla/layout/html/base/src/nsBlockReflowState.cpp index 01b19bec913..a41e46a14fb 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.cpp +++ b/mozilla/layout/html/base/src/nsBlockReflowState.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -60,7 +61,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mReflowState(aReflowState), mLastFloaterY(0), mNextRCFrame(nsnull), - mPrevBottomMargin(0), + mPrevBottomMargin(), mLineNumber(0), mFlags(0) { @@ -93,7 +94,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mReflowStatus = NS_FRAME_COMPLETE; mPresContext = aPresContext; - mBlock->GetNextInFlow((nsIFrame**)&mNextInFlow); + mBlock->GetNextInFlow(NS_REINTERPRET_CAST(nsIFrame**, &mNextInFlow)); mKidXMost = 0; // Compute content area width (the content area is inside the border @@ -143,8 +144,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mBand.Init(mSpaceManager, mContentArea); mPrevChild = nsnull; - mCurrentLine = nsnull; - mPrevLine = nsnull; + mCurrentLine = aFrame->end_lines(); const nsStyleText* styleText; mBlock->GetStyleData(eStyleStruct_Text, @@ -366,7 +366,7 @@ nsBlockReflowState::ClearPastFloaters(PRUint8 aBreakType) case NS_STYLE_CLEAR_RIGHT: case NS_STYLE_CLEAR_LEFT_AND_RIGHT: // Apply the previous margin before clearing - saveY = mY + mPrevBottomMargin; + saveY = mY + mPrevBottomMargin.get(); ClearFloaters(saveY, aBreakType); #ifdef NOISY_FLOATER_CLEARING nsFrame::ListTag(stdout, mBlock); @@ -398,7 +398,9 @@ nsBlockReflowState::ClearPastFloaters(PRUint8 aBreakType) // XXXldb This doesn't handle collapsing with negative margins // correctly, although it's arguable what "correct" is. - mPrevBottomMargin = deltaY; + // XXX Are all the other margins included by this point? + mPrevBottomMargin.Zero(); + mPrevBottomMargin.Include(deltaY); mY = saveY; // Force margin to be applied in this circumstance @@ -408,73 +410,77 @@ nsBlockReflowState::ClearPastFloaters(PRUint8 aBreakType) // Put mY back to its original value since no clearing // happened. That way the previous blocks bottom margin will // be applied properly. - mY = saveY - mPrevBottomMargin; + mY = saveY - mPrevBottomMargin.get(); } break; } return applyTopMargin; } -// Recover the collapsed vertical margin values for aLine. Note that -// the values are not collapsed with aState.mPrevBottomMargin, nor are -// they collapsed with each other when the line height is zero. +/* + * Reconstruct the vertical margin before the line |aLine| in order to + * do an incremental reflow that begins with |aLine| without reflowing + * the line before it. |aLine| may point to the fencepost at the end of + * the line list, and it is used this way since we (for now, anyway) + * always need to recover margins at the end of a block. + * + * The reconstruction involves walking backward through the line list to + * find any collapsed margins preceding the line that would have been in + * the reflow state's |mPrevBottomMargin| when we reflowed that line in + * a full reflow (under the rule in CSS2 that all adjacent vertical + * margins of blocks collapse). + */ void -nsBlockReflowState::RecoverVerticalMargins(nsLineBox* aLine, - PRBool aApplyTopMargin, - nscoord* aTopMarginResult, - nscoord* aBottomMarginResult) +nsBlockReflowState::ReconstructMarginAbove(nsLineList::iterator aLine) { - if (aLine->IsBlock()) { - // Update band data - GetAvailableSpace(); + mPrevBottomMargin.Zero(); + nsBlockFrame *block = mBlock; - // Setup reflow state to compute the block childs top and bottom - // margins - nsIFrame* frame = aLine->mFirstChild; - nsRect availSpaceRect; - const nsStyleDisplay* display; - frame->GetStyleData(eStyleStruct_Display, - (const nsStyleStruct*&) display); - nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; - frame->IsSplittable(splitType); - ComputeBlockAvailSpace(frame, splitType, display, availSpaceRect); - nsSize availSpace(availSpaceRect.width, availSpaceRect.height); - nsHTMLReflowState reflowState(mPresContext, mReflowState, - frame, availSpace); + const nsStyleText* styleText = NS_STATIC_CAST(const nsStyleText*, + block->mStyleContext->GetStyleData(eStyleStruct_Text)); + PRBool isPre = + ((NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) || + (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace)); - // Compute collapsed top margin - nscoord topMargin = 0; - if (aApplyTopMargin) { - topMargin = - nsBlockReflowContext::ComputeCollapsedTopMargin(mPresContext, - reflowState); + nsCompatibility mode; + mPresContext->GetCompatibilityMode(&mode); + PRBool isQuirkMode = mode == eCompatibility_NavQuirks; + + nsLineList::iterator firstLine = block->begin_lines(); + for (;;) { + --aLine; + if (aLine->IsBlock()) { + mPrevBottomMargin = aLine->GetCarriedOutBottomMargin(); + break; + } + PRBool isEmpty; + aLine->IsEmpty(isQuirkMode, isPre, &isEmpty); + if (! isEmpty) { + break; + } + if (aLine == firstLine) { + // If the top margin was carried out (and thus already applied), + // set it to zero. Either way, we're done. + if ((0 == mReflowState.mComputedBorderPadding.top) && + !(block->mState & NS_BLOCK_MARGIN_ROOT)) { + mPrevBottomMargin.Zero(); + } + break; } - - // Compute collapsed bottom margin - nscoord bottomMargin = reflowState.mComputedMargin.bottom; - bottomMargin = - nsBlockReflowContext::MaxMargin(bottomMargin, - aLine->GetCarriedOutBottomMargin()); - *aTopMarginResult = topMargin; - *aBottomMarginResult = bottomMargin; - } - else { - // XXX_ib, see bug 44188 - *aTopMarginResult = 0; - *aBottomMarginResult = 0; } } void -nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, - PRBool aApplyTopMargin, - nsRect* aDamageRect) +nsBlockReflowState::RecoverStateFrom(nsLineList::iterator aLine, + nscoord aDeltaY) { // Make the line being recovered the current line mCurrentLine = aLine; // Update aState.mPrevChild as if we had reflowed all of the frames // in this line. + // XXXldb This is expensive in some cases, since it requires walking + // |GetNextSibling|. mPrevChild = aLine->LastChild(); // Recover mKidXMost and mMaxElementSize @@ -506,83 +512,6 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, UpdateMaximumWidth(aLine->mMaximumWidth); } - // The line may have clear before semantics. - if (aLine->IsBlock() && aLine->HasBreak()) { - // Clear past floaters before the block if the clear style is not none - aApplyTopMargin = ClearPastFloaters(aLine->GetBreakType()); -#ifdef NOISY_VERTICAL_MARGINS - nsFrame::ListTag(stdout, mBlock); - printf(": RecoverStateFrom: y=%d child ", mY); - nsFrame::ListTag(stdout, aLine->mFirstChild); - printf(" has clear of %d => %s, mPrevBottomMargin=%d\n", aLine->mBreakType, - aApplyTopMargin ? "applyTopMargin" : "nope", mPrevBottomMargin); -#endif - } - - // Recover mPrevBottomMargin and calculate the line's new Y - // coordinate (newLineY) - nscoord newLineY = mY; - nsRect lineCombinedArea; - aLine->GetCombinedArea(&lineCombinedArea); - if (aLine->IsBlock()) { - if ((0 == aLine->mBounds.height) && (0 == lineCombinedArea.height)) { - // The line's top and bottom margin values need to be collapsed - // with the mPrevBottomMargin to determine a new - // mPrevBottomMargin value. - nscoord topMargin, bottomMargin; - RecoverVerticalMargins(aLine, aApplyTopMargin, - &topMargin, &bottomMargin); - nscoord m = nsBlockReflowContext::MaxMargin(bottomMargin, - mPrevBottomMargin); - m = nsBlockReflowContext::MaxMargin(m, topMargin); - mPrevBottomMargin = m; - } - else { - // Recover the top and bottom margins for this line - nscoord topMargin, bottomMargin; - RecoverVerticalMargins(aLine, aApplyTopMargin, - &topMargin, &bottomMargin); - - // Compute the collapsed top margin value - nscoord collapsedTopMargin = - nsBlockReflowContext::MaxMargin(topMargin, mPrevBottomMargin); - - // The lineY is just below the collapsed top margin value. The - // mPrevBottomMargin gets set to the bottom margin value for the - // line. - newLineY += collapsedTopMargin; - mPrevBottomMargin = bottomMargin; - } - } - else if (0 == aLine->GetHeight()) { - // For empty inline lines we leave the previous bottom margin - // alone so that it's collpased with the next line. - } - else { - // For non-empty inline lines the previous margin is applied - // before the line. Therefore apply it now and zero it out. - newLineY += mPrevBottomMargin; - mPrevBottomMargin = 0; - } - - // Save away the old combined area for later - nsRect oldCombinedArea = lineCombinedArea; - - // Slide the frames in the line by the computed delta. This also - // updates the lines Y coordinate and the combined area's Y - // coordinate. - nscoord finalDeltaY = newLineY - aLine->mBounds.y; - mBlock->SlideLine(*this, aLine, finalDeltaY); - // aLine has been slided, but... - // XXX it is not necessary to worry about the ascent of mBlock here, right? - // Indeed, depending on the status of the first line of mBlock, we can either have: - // case first line of mBlock is dirty : it will be reflowed by mBlock and so - // mBlock->mAscent will be recomputed by the block frame, and we will - // never enter into this RecoverStateFrom(aLine) function. - // case first line of mBlock is clean : it is untouched by the incremental reflow. - // In other words, aLine is never equals to mBlock->mLines in this function. - // so mBlock->mAscent will remain unchanged. - // Place floaters for this line into the space manager if (aLine->HasFloaters()) { // Undo border/padding translation since the nsFloaterCache's @@ -596,11 +525,11 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, nsRect r; nsFloaterCache* fc = aLine->GetFirstFloater(); while (fc) { - fc->mRegion.y += finalDeltaY; - fc->mCombinedArea.y += finalDeltaY; + fc->mRegion.y += aDeltaY; + fc->mCombinedArea.y += aDeltaY; nsIFrame* floater = fc->mPlaceholder->GetOutOfFlowFrame(); floater->GetRect(r); - floater->MoveTo(mPresContext, r.x, r.y + finalDeltaY); + floater->MoveTo(mPresContext, r.x, r.y + aDeltaY); #ifdef DEBUG if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisySpaceManager) { nscoord tx, ty; @@ -609,8 +538,8 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, printf("RecoverState: txy=%d,%d (%d,%d) ", tx, ty, mSpaceManagerX, mSpaceManagerY); nsFrame::ListTag(stdout, floater); - printf(" r.y=%d finalDeltaY=%d (sum=%d) region={%d,%d,%d,%d}\n", - r.y, finalDeltaY, r.y + finalDeltaY, + printf(" r.y=%d aDeltaY=%d (sum=%d) region={%d,%d,%d,%d}\n", + r.y, aDeltaY, r.y + aDeltaY, fc->mRegion.x, fc->mRegion.y, fc->mRegion.width, fc->mRegion.height); } @@ -627,32 +556,6 @@ nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine, // And then put the translation back again mSpaceManager->Translate(bp.left, bp.top); } - - // Recover mY - mY = aLine->mBounds.YMost(); - - // Compute the damage area - if (aDamageRect) { - if (0 == finalDeltaY) { - aDamageRect->Empty(); - } else { - aLine->GetCombinedArea(&lineCombinedArea); - aDamageRect->UnionRect(oldCombinedArea, lineCombinedArea); - } - } - -// XXX Does this do anything? It doesn't seem to work.... (bug 29413) - // It's possible that the line has clear after semantics - if (!aLine->IsBlock() && aLine->HasBreak()) { - PRUint8 breakType = aLine->GetBreakType(); - switch (breakType) { - case NS_STYLE_CLEAR_LEFT: - case NS_STYLE_CLEAR_RIGHT: - case NS_STYLE_CLEAR_LEFT_AND_RIGHT: - ClearFloaters(mY, breakType); - break; - } - } } PRBool @@ -662,7 +565,7 @@ nsBlockReflowState::IsImpactedByFloater() const printf("nsBlockReflowState::IsImpactedByFloater %p returned %d\n", this, mBand.GetFloaterCount()); #endif - return mBand.GetFloaterCount(); + return mBand.GetFloaterCount() > 0; } @@ -694,7 +597,7 @@ nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout, nsPlaceholderFrame* aPlaceholder, PRBool aInitialReflow) { - NS_PRECONDITION(nsnull != mCurrentLine, "null ptr"); + NS_PRECONDITION(mBlock->end_lines() != mCurrentLine, "null ptr"); // Allocate a nsFloaterCache for the floater nsFloaterCache* fc = mFloaterCacheFreeList.Alloc(); diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.h b/mozilla/layout/html/base/src/nsBlockReflowState.h index cd3a544203e..718867ac895 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.h +++ b/mozilla/layout/html/base/src/nsBlockReflowState.h @@ -56,12 +56,6 @@ public: ~nsBlockReflowState(); - /** - * Update our state when aLine is skipped over during incremental - * reflow. - */ - void RecoverStateFrom(nsLineBox* aLine, PRBool aPrevLineWasClean); - /** * Get the available reflow space for the current y coordinate. The * available space is relative to our coordinate system (0,0) is our @@ -107,19 +101,16 @@ public: void UpdateMaximumWidth(nscoord aMaximumWidth); - void RecoverVerticalMargins(nsLineBox* aLine, - PRBool aApplyTopMargin, - nscoord* aTopMarginResult, - nscoord* aBottomMarginResult); + // Reconstruct the previous bottom margin that goes above |aLine|. + void ReconstructMarginAbove(nsLineList::iterator aLine); void ComputeBlockAvailSpace(nsIFrame* aFrame, nsSplittableType aSplitType, const nsStyleDisplay* aDisplay, nsRect& aResult); - void RecoverStateFrom(nsLineBox* aLine, - PRBool aApplyTopMargin, - nsRect* aDamageRect); + void RecoverStateFrom(nsLineList::iterator aLine, + nscoord aDeltaY); void AdvanceToNextLine() { mLineNumber++; @@ -175,10 +166,8 @@ public: // dirty and is passed over during incremental reflow. // The current line being reflowed - nsLineBox* mCurrentLine; - - // The previous line just reflowed - nsLineBox* mPrevLine; + // If it is mBlock->end_lines(), then it is invalid. + nsLineList::iterator mCurrentLine; // The current Y coordinate in the block nscoord mY; @@ -213,7 +202,7 @@ public: nsIFrame* mNextRCFrame; // The previous child frames collapsed bottom margin value. - nscoord mPrevBottomMargin; + nsCollapsingMargin mPrevBottomMargin; // The current next-in-flow for the block. When lines are pulled // from a next-in-flow, this is used to know which next-in-flow to diff --git a/mozilla/layout/html/base/src/nsContainerFrame.cpp b/mozilla/layout/html/base/src/nsContainerFrame.cpp index dc3f064fed8..611acd94504 100644 --- a/mozilla/layout/html/base/src/nsContainerFrame.cpp +++ b/mozilla/layout/html/base/src/nsContainerFrame.cpp @@ -1069,28 +1069,28 @@ nsContainerFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) IndentBy(out, aIndent); ListTag(out); #ifdef DEBUG_waterson - fprintf(out, " [parent=%p]", mParent); + fprintf(out, " [parent=%p]", NS_STATIC_CAST(void*, mParent)); #endif nsIView* view; GetView(aPresContext, &view); if (nsnull != view) { - fprintf(out, " [view=%p]", view); + fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, view)); } if (nsnull != mNextSibling) { - fprintf(out, " next=%p", mNextSibling); + fprintf(out, " next=%p", NS_STATIC_CAST(void*, mNextSibling)); } if (nsnull != mPrevInFlow) { - fprintf(out, " prev-in-flow=%p", mPrevInFlow); + fprintf(out, " prev-in-flow=%p", NS_STATIC_CAST(void*, mPrevInFlow)); } if (nsnull != mNextInFlow) { - fprintf(out, " next-in-flow=%p", mNextInFlow); + fprintf(out, " next-in-flow=%p", NS_STATIC_CAST(void*, mNextInFlow)); } fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height); if (0 != mState) { fprintf(out, " [state=%08x]", mState); } - fprintf(out, " [content=%p]", mContent); - fprintf(out, " [sc=%p]", mStyleContext); + fprintf(out, " [content=%p]", NS_STATIC_CAST(void*, mContent)); + fprintf(out, " [sc=%p]", NS_STATIC_CAST(void*, mStyleContext)); // Output the children nsIAtom* listName = nsnull; diff --git a/mozilla/layout/html/base/src/nsFrame.cpp b/mozilla/layout/html/base/src/nsFrame.cpp index 3677c501e41..d91d401b537 100644 --- a/mozilla/layout/html/base/src/nsFrame.cpp +++ b/mozilla/layout/html/base/src/nsFrame.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -292,24 +293,22 @@ nsFrame::~nsFrame() nsresult nsFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) { - if (NULL == aInstancePtr) { - return NS_ERROR_NULL_POINTER; - } - static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); - static NS_DEFINE_IID(kClassIID, NS_GET_IID(nsIFrame)); + NS_PRECONDITION(aInstancePtr, "null out param"); #ifdef DEBUG if (aIID.Equals(NS_GET_IID(nsIFrameDebug))) { - *aInstancePtr = (void*)(nsIFrameDebug*)this; + *aInstancePtr = NS_STATIC_CAST(void*,NS_STATIC_CAST(nsIFrameDebug*,this)); return NS_OK; } #endif - if (aIID.Equals(kClassIID) || aIID.Equals(kISupportsIID)) { - *aInstancePtr = (void*)this; + if (aIID.Equals(NS_GET_IID(nsIFrame)) || + aIID.Equals(NS_GET_IID(nsISupports))) { + *aInstancePtr = NS_STATIC_CAST(void*,NS_STATIC_CAST(nsIFrame*,this)); return NS_OK; } + *aInstancePtr = nsnull; return NS_NOINTERFACE; } @@ -2143,7 +2142,7 @@ nsFrame::IsFrameTreeTooDeep(const nsHTMLReflowState& aReflowState, aMetrics.height = 0; aMetrics.ascent = 0; aMetrics.descent = 0; - aMetrics.mCarriedOutBottomMargin = 0; + aMetrics.mCarriedOutBottomMargin.Zero(); aMetrics.mOverflowArea.x = 0; aMetrics.mOverflowArea.y = 0; aMetrics.mOverflowArea.width = 0; @@ -2241,18 +2240,18 @@ nsFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) const IndentBy(out, aIndent); ListTag(out); #ifdef DEBUG_waterson - fprintf(out, " [parent=%p]", mParent); + fprintf(out, " [parent=%p]", NS_STATIC_CAST(void*, mParent)); #endif nsIView* view; GetView(aPresContext, &view); if (view) { - fprintf(out, " [view=%p]", view); + fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, view)); } fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height); if (0 != mState) { fprintf(out, " [state=%08x]", mState); } - fprintf(out, " [content=%p]", mContent); + fprintf(out, " [content=%p]", NS_STATIC_CAST(void*, mContent)); fputs("\n", out); return NS_OK; } @@ -2415,6 +2414,13 @@ nsFrame::IsVisibleForPainting(nsIPresContext * aPresContext, return rv; } +NS_IMETHODIMP +nsFrame::IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool *aResult) +{ + *aResult = PR_FALSE; + return NS_OK; +} + NS_IMETHODIMP nsFrame::GetSelectionController(nsIPresContext *aPresContext, nsISelectionController **aSelCon) { diff --git a/mozilla/layout/html/base/src/nsFrame.h b/mozilla/layout/html/base/src/nsFrame.h index 9bce3279770..03d44f20698 100644 --- a/mozilla/layout/html/base/src/nsFrame.h +++ b/mozilla/layout/html/base/src/nsFrame.h @@ -291,6 +291,10 @@ public: PRBool aCheckVis, PRBool* aIsVisible); + NS_IMETHOD IsEmpty(PRBool aIsQuirkMode, + PRBool aIsPre, + PRBool* aResult); + // nsIHTMLReflow NS_IMETHOD WillReflow(nsIPresContext* aPresContext); NS_IMETHOD Reflow(nsIPresContext* aPresContext, @@ -410,7 +414,7 @@ public: frameDebug->GetFrameName(tmp); } fputs(NS_LossyConvertUCS2toASCII(tmp).get(), out); - fprintf(out, "@%p", aFrame); + fprintf(out, "@%p", NS_STATIC_CAST(void*, aFrame)); } static void IndentBy(FILE* out, PRInt32 aIndent) { diff --git a/mozilla/layout/html/base/src/nsFrameManager.cpp b/mozilla/layout/html/base/src/nsFrameManager.cpp index 4dfd1950d53..74dc31e1c30 100644 --- a/mozilla/layout/html/base/src/nsFrameManager.cpp +++ b/mozilla/layout/html/base/src/nsFrameManager.cpp @@ -92,7 +92,7 @@ #endif // NEW_CONTEXT_PARENTAGE_INVARIANT // Class IID's -static NS_DEFINE_IID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); +static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); // IID's diff --git a/mozilla/layout/html/base/src/nsGfxScrollFrame.cpp b/mozilla/layout/html/base/src/nsGfxScrollFrame.cpp index 065afbde420..1ecf213e53f 100644 --- a/mozilla/layout/html/base/src/nsGfxScrollFrame.cpp +++ b/mozilla/layout/html/base/src/nsGfxScrollFrame.cpp @@ -70,9 +70,9 @@ #include "nsIGfxTextControlFrame.h" #include "nsIDOMHTMLTextAreaElement.h" -static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID); -static NS_DEFINE_IID(kScrollingViewCID, NS_SCROLLING_VIEW_CID); -static NS_DEFINE_IID(kViewCID, NS_VIEW_CID); +static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID); +static NS_DEFINE_CID(kScrollingViewCID, NS_SCROLLING_VIEW_CID); +static NS_DEFINE_CID(kViewCID, NS_VIEW_CID); diff --git a/mozilla/layout/html/base/src/nsHTMLContainerFrame.cpp b/mozilla/layout/html/base/src/nsHTMLContainerFrame.cpp index fb3ecbb15e7..00c88f78b90 100644 --- a/mozilla/layout/html/base/src/nsHTMLContainerFrame.cpp +++ b/mozilla/layout/html/base/src/nsHTMLContainerFrame.cpp @@ -64,7 +64,7 @@ #include "nsIStyleSet.h" #include "nsCOMPtr.h" -static NS_DEFINE_IID(kCChildCID, NS_CHILD_CID); +static NS_DEFINE_CID(kCChildCID, NS_CHILD_CID); NS_IMETHODIMP nsHTMLContainerFrame::Paint(nsIPresContext* aPresContext, @@ -551,7 +551,7 @@ nsHTMLContainerFrame::CreateViewForFrame(nsIPresContext* aPresContext, NS_ASSERTION(parentView, "no parent with view"); // Create a view - static NS_DEFINE_IID(kViewCID, NS_VIEW_CID); + static NS_DEFINE_CID(kViewCID, NS_VIEW_CID); nsresult result = nsComponentManager::CreateInstance(kViewCID, nsnull, diff --git a/mozilla/layout/html/base/src/nsHTMLReflowState.cpp b/mozilla/layout/html/base/src/nsHTMLReflowState.cpp index b496cdc68d9..356eb54d8e4 100644 --- a/mozilla/layout/html/base/src/nsHTMLReflowState.cpp +++ b/mozilla/layout/html/base/src/nsHTMLReflowState.cpp @@ -321,6 +321,7 @@ nsHTMLReflowState::DetermineFrameType(nsIFrame* aFrame, else if (NS_STYLE_FLOAT_NONE != aDisplay->mFloats) { frameType = NS_CSS_FRAME_TYPE_FLOATING; } + // XXXldb UMR in this case (else, else) we don't initialize frameType } else { switch (aDisplay->mDisplay) { @@ -826,16 +827,14 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsIPresContext* aPresContext, // the line containing the placeholder frame if (aBlockFrame) { nsIFrame* blockChild; - nsLineBox* lineBox; - nsLineBox* prevLineBox; + nsBlockFrame::line_iterator lineBox; PRBool isFloater; + nsBlockFrame* blockFrame = NS_STATIC_CAST(nsBlockFrame*, aBlockFrame); // We need the immediate child of the block frame, and that may not be // the placeholder frame blockChild = FindImmediateChildOf(aBlockFrame, aPlaceholderFrame); - lineBox = ((nsBlockFrame*)aBlockFrame)->FindLineFor(blockChild, &prevLineBox, - &isFloater); - if (lineBox) { + if (blockFrame->FindLineFor(blockChild, &isFloater, &lineBox)) { // The top of the hypothetical box is just below the line containing // the placeholder aHypotheticalBox.mTop = lineBox->mBounds.YMost(); diff --git a/mozilla/layout/html/base/src/nsILineIterator.h b/mozilla/layout/html/base/src/nsILineIterator.h index 0818190080a..4ec1c9fb607 100644 --- a/mozilla/layout/html/base/src/nsILineIterator.h +++ b/mozilla/layout/html/base/src/nsILineIterator.h @@ -64,12 +64,6 @@ // clear, it means that the line contains inline elements. #define NS_LINE_FLAG_IS_BLOCK 0x1 -// This bit, when set, indicates that the line has had a trailing -// white-space compressed. The bit is only set on lines that contain -// inline elements, and is only set when actual white-space is -// compressed. -#define NS_LINE_FLAG_IS_TRIMMED 0x2 - // This bit is set when the line ends in some sort of break. #define NS_LINE_FLAG_ENDS_IN_BREAK 0x4 diff --git a/mozilla/layout/html/base/src/nsInlineFrame.cpp b/mozilla/layout/html/base/src/nsInlineFrame.cpp index 18c2474145b..6157fda6f88 100644 --- a/mozilla/layout/html/base/src/nsInlineFrame.cpp +++ b/mozilla/layout/html/base/src/nsInlineFrame.cpp @@ -111,6 +111,84 @@ nsInlineFrame::GetFrameType(nsIAtom** aType) const return NS_OK; } +inline PRBool +IsBorderZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) +{ + return ((aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0)); +} + +inline PRBool +IsPaddingZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) +{ + return (aUnit == eStyleUnit_Null || + (aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) || + (aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0)); +} + +inline PRBool +IsMarginZero(nsStyleUnit aUnit, nsStyleCoord &aCoord) +{ + return (aUnit == eStyleUnit_Null || + aUnit == eStyleUnit_Auto || + (aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) || + (aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0)); +} + +NS_IMETHODIMP +nsInlineFrame::IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool* aResult) +{ + if (!aIsQuirkMode) { + *aResult = PR_FALSE; + return NS_OK; + } + const nsStyleMargin* margin = NS_STATIC_CAST(const nsStyleMargin*, + mStyleContext->GetStyleData(eStyleStruct_Margin)); + const nsStyleBorder* border = NS_STATIC_CAST(const nsStyleBorder*, + mStyleContext->GetStyleData(eStyleStruct_Border)); + const nsStylePadding* padding = NS_STATIC_CAST(const nsStylePadding*, + mStyleContext->GetStyleData(eStyleStruct_Padding)); + nsStyleCoord coord; + if ((border->IsBorderSideVisible(NS_SIDE_TOP) && + !IsBorderZero(border->mBorder.GetTopUnit(), + border->mBorder.GetTop(coord))) || + (border->IsBorderSideVisible(NS_SIDE_RIGHT) && + !IsBorderZero(border->mBorder.GetTopUnit(), + border->mBorder.GetTop(coord))) || + (border->IsBorderSideVisible(NS_SIDE_BOTTOM) && + !IsBorderZero(border->mBorder.GetTopUnit(), + border->mBorder.GetTop(coord))) || + (border->IsBorderSideVisible(NS_SIDE_LEFT) && + !IsBorderZero(border->mBorder.GetTopUnit(), + border->mBorder.GetTop(coord))) || + !IsPaddingZero(padding->mPadding.GetTopUnit(), + padding->mPadding.GetTop(coord)) || + !IsPaddingZero(padding->mPadding.GetRightUnit(), + padding->mPadding.GetRight(coord)) || + !IsPaddingZero(padding->mPadding.GetBottomUnit(), + padding->mPadding.GetBottom(coord)) || + !IsPaddingZero(padding->mPadding.GetLeftUnit(), + padding->mPadding.GetLeft(coord)) || + !IsMarginZero(margin->mMargin.GetTopUnit(), + margin->mMargin.GetTop(coord)) || + !IsMarginZero(margin->mMargin.GetRightUnit(), + margin->mMargin.GetRight(coord)) || + !IsMarginZero(margin->mMargin.GetBottomUnit(), + margin->mMargin.GetBottom(coord)) || + !IsMarginZero(margin->mMargin.GetLeftUnit(), + margin->mMargin.GetLeft(coord))) { + *aResult = PR_FALSE; + return NS_OK; + } + + *aResult = PR_TRUE; + for (nsIFrame *kid = mFrames.FirstChild(); kid; kid->GetNextSibling(&kid)) { + kid->IsEmpty(aIsQuirkMode, aIsPre, aResult); + if (! *aResult) + break; + } + return NS_OK; +} + NS_IMETHODIMP nsInlineFrame::AppendFrames(nsIPresContext* aPresContext, nsIPresShell& aPresShell, diff --git a/mozilla/layout/html/base/src/nsInlineFrame.h b/mozilla/layout/html/base/src/nsInlineFrame.h index 94db405a703..74033deaef6 100644 --- a/mozilla/layout/html/base/src/nsInlineFrame.h +++ b/mozilla/layout/html/base/src/nsInlineFrame.h @@ -90,6 +90,8 @@ public: #endif NS_IMETHOD GetFrameType(nsIAtom** aType) const; + NS_IMETHOD IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool* aResult); + // nsIHTMLReflow overrides NS_IMETHOD Reflow(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, diff --git a/mozilla/layout/html/base/src/nsLineBox.cpp b/mozilla/layout/html/base/src/nsLineBox.cpp index c7c5779444b..091cdbb31a9 100644 --- a/mozilla/layout/html/base/src/nsLineBox.cpp +++ b/mozilla/layout/html/base/src/nsLineBox.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -20,6 +21,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * L. David Baron * Pierre Phaneuf * * Alternatively, the contents of this file may be used under the terms of @@ -40,10 +42,12 @@ #include "nsIStyleContext.h" #include "nsLineLayout.h" #include "prprf.h" +#include "nsBlockFrame.h" +#include "nsITextContent.h" +#include "nsLayoutAtoms.h" #ifdef DEBUG #include "nsISizeOfHandler.h" -#include "nsLayoutAtoms.h" #endif #ifdef DEBUG @@ -55,11 +59,10 @@ MOZ_DECL_CTOR_COUNTER(nsLineBox) nsLineBox::nsLineBox(nsIFrame* aFrame, PRInt32 aCount, PRBool aIsBlock) : mFirstChild(aFrame), - mNext(nsnull), mBounds(0, 0, 0, 0), mMaxElementWidth(0), - mData(nsnull), - mMaximumWidth(-1) + mMaximumWidth(-1), + mData(nsnull) { MOZ_COUNT_CTOR(nsLineBox); #ifdef DEBUG @@ -139,7 +142,7 @@ ListFloaters(FILE* out, PRInt32 aIndent, const nsFloaterCacheList& aFloaters) nsFrame::IndentBy(out, aIndent); nsPlaceholderFrame* ph = fc->mPlaceholder; if (nsnull != ph) { - fprintf(out, "placeholder@%p ", ph); + fprintf(out, "placeholder@%p ", NS_STATIC_CAST(void*, ph)); nsIFrame* frame = ph->GetOutOfFlowFrame(); if (nsnull != frame) { nsIFrameDebug* frameDebug; @@ -163,19 +166,19 @@ ListFloaters(FILE* out, PRInt32 aIndent, const nsFloaterCacheList& aFloaters) } #endif +#ifdef DEBUG char* nsLineBox::StateToString(char* aBuf, PRInt32 aBufSize) const { PR_snprintf(aBuf, aBufSize, "%s,%s,%s,%s[0x%x]", IsBlock() ? "block" : "inline", IsDirty() ? "dirty" : "clean", + IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean", IsImpactedByFloater() ? "IMPACTED" : "NOT Impacted", - IsTrimmed() ? "trimmed" : "", mAllFlags); return aBuf; } -#ifdef DEBUG void nsLineBox::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) const { @@ -184,9 +187,10 @@ nsLineBox::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) const for (i = aIndent; --i >= 0; ) fputs(" ", out); char cbuf[100]; fprintf(out, "line %p: count=%d state=%s ", - this, GetChildCount(), StateToString(cbuf, sizeof(cbuf))); - if (0 != GetCarriedOutBottomMargin()) { - fprintf(out, "bm=%d ", GetCarriedOutBottomMargin()); + NS_STATIC_CAST(const void*, this), GetChildCount(), + StateToString(cbuf, sizeof(cbuf))); + if (IsBlock() && !GetCarriedOutBottomMargin().IsZero()) { + fprintf(out, "bm=%d ", GetCarriedOutBottomMargin().get()); } if (0 != mMaxElementWidth) { fprintf(out, "mew=%d ", mMaxElementWidth); @@ -253,67 +257,103 @@ nsLineBox::IndexOf(nsIFrame* aFrame) const return -1; } -void -nsLineBox::DeleteLineList(nsIPresContext* aPresContext, nsLineBox* aLine) +nsresult +nsLineBox::IsEmpty(PRBool aIsQuirkMode, PRBool aParentIsPre, + PRBool *aResult) const { - if (nsnull != aLine) { + if (IsBlock()) + return mFirstChild->IsEmpty(aIsQuirkMode, aParentIsPre, aResult); + + *aResult = PR_TRUE; + PRInt32 n; + nsIFrame *kid; + for (n = GetChildCount(), kid = mFirstChild; + n > 0; + --n, kid->GetNextSibling(&kid)) + { + kid->IsEmpty(aIsQuirkMode, aParentIsPre, aResult); + if (! *aResult) + break; + } + return NS_OK; +} + +void +nsLineBox::DeleteLineList(nsIPresContext* aPresContext, nsLineList& aLines) +{ + if (! aLines.empty()) { // Delete our child frames before doing anything else. In particular // we do all of this before our base class releases it's hold on the // view. - for (nsIFrame* child = aLine->mFirstChild; child; ) { + for (nsIFrame* child = aLines.front()->mFirstChild; child; ) { nsIFrame* nextChild; child->GetNextSibling(&nextChild); child->Destroy(aPresContext); child = nextChild; } - while (nsnull != aLine) { - nsLineBox* next = aLine->mNext; - delete aLine; - aLine = next; + while (! aLines.empty()) { + nsLineBox* line = aLines.front(); + aLines.pop_front(); + delete line; } } } nsLineBox* -nsLineBox::LastLine(nsLineBox* aLine) -{ - if (nsnull != aLine) { - while (nsnull != aLine->mNext) { - aLine = aLine->mNext; - } - } - return aLine; -} - -nsLineBox* -nsLineBox::FindLineContaining(nsLineBox* aLine, nsIFrame* aFrame, +nsLineBox::FindLineContaining(nsLineList& aLines, nsIFrame* aFrame, PRInt32* aFrameIndexInLine) { - NS_PRECONDITION(aFrameIndexInLine && aLine && aFrame, "null ptr"); - while (nsnull != aLine) { - PRInt32 ix = aLine->IndexOf(aFrame); + NS_PRECONDITION(aFrameIndexInLine && !aLines.empty() && aFrame, "null ptr"); + for (nsLineList::iterator line = aLines.begin(), + line_end = aLines.end(); + line != line_end; + ++line) + { + PRInt32 ix = line->IndexOf(aFrame); if (ix >= 0) { *aFrameIndexInLine = ix; - return aLine; + return line; } - aLine = aLine->mNext; } *aFrameIndexInLine = -1; return nsnull; } -nscoord +PRBool +nsLineBox::RFindLineContaining(nsIFrame* aFrame, + const nsLineList::iterator& aBegin, + nsLineList::iterator& aEnd, + PRInt32* aFrameIndexInLine) +{ + NS_PRECONDITION(aFrame, "null ptr"); + while (aBegin != aEnd) { + --aEnd; + PRInt32 ix = aEnd->IndexOf(aFrame); + if (ix >= 0) { + *aFrameIndexInLine = ix; + return PR_TRUE; + } + } + *aFrameIndexInLine = -1; + return PR_FALSE; +} + +nsCollapsingMargin nsLineBox::GetCarriedOutBottomMargin() const { - return (IsBlock() && mBlockData) ? mBlockData->mCarriedOutBottomMargin : 0; + NS_ASSERTION(IsBlock(), + "GetCarriedOutBottomMargin called on non-block line."); + return (IsBlock() && mBlockData) + ? mBlockData->mCarriedOutBottomMargin + : nsCollapsingMargin(); } void -nsLineBox::SetCarriedOutBottomMargin(nscoord aValue) +nsLineBox::SetCarriedOutBottomMargin(nsCollapsingMargin aValue) { if (IsBlock()) { - if (aValue) { + if (! aValue.IsZero()) { if (!mBlockData) { mBlockData = new ExtraBlockData(mBounds); } @@ -338,7 +378,7 @@ nsLineBox::MaybeFreeData() mInlineData = nsnull; } } - else if (0 == mBlockData->mCarriedOutBottomMargin) { + else if (mBlockData->mCarriedOutBottomMargin.IsZero()) { delete mBlockData; mBlockData = nsnull; } @@ -462,17 +502,6 @@ nsLineBox::GetCombinedArea(nsRect* aResult) } #ifdef DEBUG -PRInt32 -nsLineBox::ListLength(nsLineBox* aLine) -{ - PRInt32 count = 0; - while (aLine) { - count++; - aLine = aLine->mNext; - } - return count; -} - nsIAtom* nsLineBox::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const { @@ -533,17 +562,12 @@ nsLineIterator::~nsLineIterator() NS_IMPL_ISUPPORTS2(nsLineIterator, nsILineIterator, nsILineIteratorNavigator) nsresult -nsLineIterator::Init(nsLineBox* aLines, PRBool aRightToLeft) +nsLineIterator::Init(nsLineList& aLines, PRBool aRightToLeft) { mRightToLeft = aRightToLeft; // Count the lines - PRInt32 numLines = 0; - nsLineBox* line = aLines; - while (line) { - numLines++; - line = line->mNext; - } + PRInt32 numLines = aLines.size(); if (0 == numLines) { // Use gDummyLines so that we don't need null pointer checks in // the accessor methods @@ -560,10 +584,11 @@ nsLineIterator::Init(nsLineBox* aLines, PRBool aRightToLeft) return NS_ERROR_OUT_OF_MEMORY; } nsLineBox** lp = mLines; - line = aLines; - while (line) { + for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end() ; + line != line_end; + ++line) + { *lp++ = line; - line = line->mNext; } mNumLines = numLines; return NS_OK; @@ -618,8 +643,6 @@ nsLineIterator::GetLine(PRInt32 aLineNumber, flags |= NS_LINE_FLAG_IS_BLOCK; } else { - if (line->IsTrimmed()) - flags |= NS_LINE_FLAG_IS_TRIMMED; if (line->HasBreak()) flags |= NS_LINE_FLAG_ENDS_IN_BREAK; } @@ -634,13 +657,12 @@ nsLineIterator::FindLineContaining(nsIFrame* aFrame, { nsLineBox* line = mLines[0]; PRInt32 lineNumber = 0; - while (line) { + while (lineNumber != mNumLines) { if (line->Contains(aFrame)) { *aLineNumberResult = lineNumber; return NS_OK; } - line = line->mNext; - lineNumber++; + line = mLines[++lineNumber]; } *aLineNumberResult = -1; return NS_OK; @@ -656,13 +678,12 @@ nsLineIterator::FindLineAt(nscoord aY, return NS_OK; } PRInt32 lineNumber = 0; - while (line) { + while (lineNumber != mNumLines) { if ((aY >= line->mBounds.y) && (aY < line->mBounds.YMost())) { *aLineNumberResult = lineNumber; return NS_OK; } - line = line->mNext; - lineNumber++; + line = mLines[++lineNumber]; } *aLineNumberResult = mNumLines; return NS_OK; @@ -686,7 +707,7 @@ nsLineIterator::CheckLineOrder(PRInt32 aLine, PRInt32 lineFrameCount; PRUint32 lineFlags; - nsresult result; + nsresult result = NS_OK; // an RTL paragraph is always considered as reordered // in an LTR paragraph, find out by examining the coordinates of each frame in the line diff --git a/mozilla/layout/html/base/src/nsLineBox.h b/mozilla/layout/html/base/src/nsLineBox.h index 4f1526d4bac..3c823ab8612 100644 --- a/mozilla/layout/html/base/src/nsLineBox.h +++ b/mozilla/layout/html/base/src/nsLineBox.h @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:ts=2:et:sw=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -20,6 +21,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * L. David Baron * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -161,12 +163,45 @@ need to rearrange the mBits bitfield; nsLineBox* NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame, PRInt32 aCount, PRBool aIsBlock); +class nsLineList; + +// don't use the following names outside of this file. Instead, use +// nsLineList::iterator, etc. These are just here to allow them to +// be specified as parameters to methods of nsLineBox. +class nsLineList_iterator; +class nsLineList_const_iterator; +class nsLineList_reverse_iterator; +class nsLineList_const_reverse_iterator; + +/** + * Users must have the class that is to be part of the list inherit + * from nsLineLink. If they want to be efficient, it should be the + * first base class. (This was originally nsCLink in a templatized + * nsCList, but it's still useful separately.) + */ + +class nsLineLink { + + public: + friend class nsLineList; + friend class nsLineList_iterator; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_iterator; + friend class nsLineList_const_reverse_iterator; + + private: + nsLineLink *_mNext; // or head + nsLineLink *_mPrev; // or tail + +}; + + /** * The nsLineBox class represents a horizontal line of frames. It contains * enough state to support incremental reflow of the frames, event handling * for the frames, and rendering of the frames. */ -class nsLineBox { +class nsLineBox : public nsLineLink { private: nsLineBox(nsIFrame* aFrame, PRInt32 aCount, PRBool aIsBlock); ~nsLineBox(); @@ -202,6 +237,17 @@ public: return mFlags.mDirty; } + // mPreviousMarginDirty bit + void MarkPreviousMarginDirty() { + mFlags.mPreviousMarginDirty = 1; + } + void ClearPreviousMarginDirty() { + mFlags.mPreviousMarginDirty = 0; + } + PRBool IsPreviousMarginDirty() const { + return mFlags.mPreviousMarginDirty; + } + // mImpactedByFloater bit void SetLineIsImpactedByFloater(PRBool aValue) { NS_ASSERTION((PR_FALSE==aValue || PR_TRUE==aValue), "somebody is playing fast and loose with bools and bits!"); @@ -211,15 +257,6 @@ public: return mFlags.mImpactedByFloater; } - // mTrimmed bit - void SetTrimmed(PRBool aOn) { - NS_ASSERTION((PR_FALSE==aOn || PR_TRUE==aOn), "somebody is playing fast and loose with bools and bits!"); - mFlags.mTrimmed = aOn; - } - PRBool IsTrimmed() const { - return mFlags.mTrimmed; - } - // mHasPercentageChild bit void SetHasPercentageChild(PRBool aOn) { NS_ASSERTION((PR_FALSE==aOn || PR_TRUE==aOn), "somebody is playing fast and loose with bools and bits!"); @@ -285,8 +322,8 @@ public: } // mCarriedOutBottomMargin value - nscoord GetCarriedOutBottomMargin() const; - void SetCarriedOutBottomMargin(nscoord aValue); + nsCollapsingMargin GetCarriedOutBottomMargin() const; + void SetCarriedOutBottomMargin(nsCollapsingMargin aValue); // mFloaters PRBool HasFloaters() const { @@ -320,14 +357,24 @@ public: return mBounds.height; } - static void DeleteLineList(nsIPresContext* aPresContext, nsLineBox* aLine); + static void DeleteLineList(nsIPresContext* aPresContext, nsLineList& aLines); - static nsLineBox* LastLine(nsLineBox* aLine); - - static nsLineBox* FindLineContaining(nsLineBox* aLine, nsIFrame* aFrame, + // search from beginning to end + // XXX Should switch to API below + static nsLineBox* FindLineContaining(nsLineList& aLines, nsIFrame* aFrame, PRInt32* aFrameIndexInLine); + // search from end to beginning of [aBegin, aEnd) + // Returns PR_TRUE if it found the line and PR_FALSE if not. + // Moves aEnd as it searches so that aEnd points to the resulting line. + static PRBool RFindLineContaining(nsIFrame* aFrame, + const nsLineList_iterator& aBegin, + nsLineList_iterator& aEnd, + PRInt32* aFrameIndexInLine); + #ifdef DEBUG + char* StateToString(char* aBuf, PRInt32 aBufSize) const; + void List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) const; #endif @@ -335,22 +382,22 @@ public: PRBool IsLastChild(nsIFrame* aFrame) const; - char* StateToString(char* aBuf, PRInt32 aBufSize) const; - PRInt32 IndexOf(nsIFrame* aFrame) const; PRBool Contains(nsIFrame* aFrame) const { return IndexOf(aFrame) >= 0; } + // whether the line box is "logically" empty (just like nsIFrame::IsEmpty) + nsresult IsEmpty(PRBool aIsQuirkMode, PRBool aParentIsPre, + PRBool *aResult) const; + #ifdef DEBUG nsIAtom* SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const; static PRInt32 GetCtorCount(); - static PRInt32 ListLength(nsLineBox* aLine); #endif nsIFrame* mFirstChild; - nsLineBox* mNext; nsRect mBounds; nscoord mMaxElementWidth; // width part of max-element-size @@ -358,13 +405,13 @@ public: struct FlagBits { PRUint32 mDirty : 1; + PRUint32 mPreviousMarginDirty : 1; PRUint32 mBlock : 1; PRUint32 mImpactedByFloater : 1; - PRUint32 mTrimmed : 1; PRUint32 mHasPercentageChild : 1; PRUint32 mLineWrapped: 1; PRUint32 mForceInvalidate: 1; // default 0 = means this line handles it's own invalidation. 1 = always invalidate this entire line - PRUint32 mResizeReflowOptimizationDisabled: 1; // default 0 = means that the opt potentially applies to this line. 1 = never skip reflowing this line for a resize reflow + PRUint32 mResizeReflowOptimizationDisabled: 1; // default 0 = means that the opt potentially applies to this line. 1 = never skip reflowing this line for a resize reflow PRUint32 mBreakType : 4; PRUint32 mChildCount : 20; @@ -377,10 +424,12 @@ public: }; struct ExtraBlockData : public ExtraData { - ExtraBlockData(const nsRect& aBounds) : ExtraData(aBounds) { - mCarriedOutBottomMargin = 0; + ExtraBlockData(const nsRect& aBounds) + : ExtraData(aBounds), + mCarriedOutBottomMargin() + { } - nscoord mCarriedOutBottomMargin; + nsCollapsingMargin mCarriedOutBottomMargin; }; struct ExtraInlineData : public ExtraData { @@ -405,6 +454,978 @@ protected: void MaybeFreeData(); }; +#ifdef DEBUG +#define NS_LINELIST_DEBUG_PASS_END +#else +#undef NS_LINELIST_DEBUG_PASS_END +#endif + +/** + * A linked list type where the items in the list must inherit from + * a link type to fuse allocations. + * + * API heavily based on the |list| class in the C++ standard. + */ + +class nsLineList_iterator { + public: + friend class nsLineList; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_iterator; + friend class nsLineList_const_reverse_iterator; + + typedef nsLineList_iterator iterator_self_type; + typedef nsLineList_reverse_iterator iterator_reverse_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef PRUint32 size_type; + typedef PRInt32 difference_type; + + typedef nsLineLink link_type; + + // Auto generated default constructor OK. + // Auto generated copy-constructor OK. + + inline iterator_self_type& + operator=(const iterator_self_type& aOther); + inline iterator_self_type& + operator=(const iterator_reverse_type& aOther); + + iterator_self_type& operator++() + { + mCurrent = mCurrent->_mNext; + return *this; + } + + iterator_self_type operator++(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mNext; + return rv; + } + + iterator_self_type& operator--() + { + mCurrent = mCurrent->_mPrev; + return *this; + } + + iterator_self_type operator--(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mPrev; + return rv; + } + + reference operator*() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return *NS_STATIC_CAST(pointer, mCurrent); + } + + pointer operator->() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(pointer, mCurrent); + } + + pointer get() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(pointer, mCurrent); + } + + operator pointer() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(pointer, mCurrent); + } + + const_reference operator*() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return *NS_STATIC_CAST(const_pointer, mCurrent); + } + + const_pointer operator->() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } + +#ifndef __MWERKS__ + operator const_pointer() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } +#endif /* !__MWERKS__ */ + + iterator_self_type next() + { + iterator_self_type copy(*this); + return ++copy; + } + + const iterator_self_type next() const + { + iterator_self_type copy(*this); + return ++copy; + } + + iterator_self_type prev() + { + iterator_self_type copy(*this); + return --copy; + } + + const iterator_self_type prev() const + { + iterator_self_type copy(*this); + return --copy; + } + + PRBool operator==(const iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(const iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + + private: + link_type *mCurrent; +#ifdef NS_LINELIST_DEBUG_PASS_END + link_type *mListLink; // the list's link, i.e., the end +#endif +}; + +class nsLineList_reverse_iterator { + + public: + + friend class nsLineList; + friend class nsLineList_iterator; + friend class nsLineList_const_iterator; + friend class nsLineList_const_reverse_iterator; + + typedef nsLineList_reverse_iterator iterator_self_type; + typedef nsLineList_iterator iterator_reverse_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef PRUint32 size_type; + typedef PRInt32 difference_type; + + typedef nsLineLink link_type; + + // Auto generated default constructor OK. + // Auto generated copy-constructor OK. + + inline iterator_self_type& + operator=(const iterator_reverse_type& aOther); + inline iterator_self_type& + operator=(const iterator_self_type& aOther); + + iterator_self_type& operator++() + { + mCurrent = mCurrent->_mPrev; + return *this; + } + + iterator_self_type operator++(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mPrev; + return rv; + } + + iterator_self_type& operator--() + { + mCurrent = mCurrent->_mNext; + return *this; + } + + iterator_self_type operator--(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mNext; + return rv; + } + + reference operator*() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return *NS_STATIC_CAST(pointer, mCurrent); + } + + pointer operator->() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(pointer, mCurrent); + } + + pointer get() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(pointer, mCurrent); + } + + operator pointer() + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(pointer, mCurrent); + } + + const_reference operator*() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return *NS_STATIC_CAST(const_pointer, mCurrent); + } + + const_pointer operator->() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } + +#ifndef __MWERKS__ + operator const_pointer() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } +#endif /* !__MWERKS__ */ + + PRBool operator==(const iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(const iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + + private: + link_type *mCurrent; +#ifdef NS_LINELIST_DEBUG_PASS_END + link_type *mListLink; // the list's link, i.e., the end +#endif +}; + +class nsLineList_const_iterator { + public: + + friend class nsLineList; + friend class nsLineList_iterator; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_reverse_iterator; + + typedef nsLineList_const_iterator iterator_self_type; + typedef nsLineList_const_reverse_iterator iterator_reverse_type; + typedef nsLineList_iterator iterator_nonconst_type; + typedef nsLineList_reverse_iterator iterator_nonconst_reverse_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef PRUint32 size_type; + typedef PRInt32 difference_type; + + typedef nsLineLink link_type; + + // Auto generated default constructor OK. + // Auto generated copy-constructor OK. + + inline iterator_self_type& + operator=(const iterator_nonconst_type& aOther); + inline iterator_self_type& + operator=(const iterator_nonconst_reverse_type& aOther); + inline iterator_self_type& + operator=(const iterator_self_type& aOther); + inline iterator_self_type& + operator=(const iterator_reverse_type& aOther); + + iterator_self_type& operator++() + { + mCurrent = mCurrent->_mNext; + return *this; + } + + iterator_self_type operator++(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mNext; + return rv; + } + + iterator_self_type& operator--() + { + mCurrent = mCurrent->_mPrev; + return *this; + } + + iterator_self_type operator--(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mPrev; + return rv; + } + + const_reference operator*() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return *NS_STATIC_CAST(const_pointer, mCurrent); + } + + const_pointer operator->() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } + + const_pointer get() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } + +#ifndef __MWERKS__ + operator const_pointer() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } +#endif /* !__MWERKS__ */ + + const iterator_self_type next() const + { + iterator_self_type copy(*this); + return ++copy; + } + + const iterator_self_type prev() const + { + iterator_self_type copy(*this); + return --copy; + } + + PRBool operator==(const iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(const iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + + private: + const link_type *mCurrent; +#ifdef NS_LINELIST_DEBUG_PASS_END + const link_type *mListLink; // the list's link, i.e., the end +#endif +}; + +class nsLineList_const_reverse_iterator { + public: + + friend class nsLineList; + friend class nsLineList_iterator; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_iterator; + + typedef nsLineList_const_reverse_iterator iterator_self_type; + typedef nsLineList_const_iterator iterator_reverse_type; + typedef nsLineList_iterator iterator_nonconst_reverse_type; + typedef nsLineList_reverse_iterator iterator_nonconst_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef PRUint32 size_type; + typedef PRInt32 difference_type; + + typedef nsLineLink link_type; + + // Auto generated default constructor OK. + // Auto generated copy-constructor OK. + + inline iterator_self_type& + operator=(const iterator_nonconst_type& aOther); + inline iterator_self_type& + operator=(const iterator_nonconst_reverse_type& aOther); + inline iterator_self_type& + operator=(const iterator_self_type& aOther); + inline iterator_self_type& + operator=(const iterator_reverse_type& aOther); + + iterator_self_type& operator++() + { + mCurrent = mCurrent->_mPrev; + return *this; + } + + iterator_self_type operator++(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mPrev; + return rv; + } + + iterator_self_type& operator--() + { + mCurrent = mCurrent->_mNext; + return *this; + } + + iterator_self_type operator--(int) + { + iterator_self_type rv(*this); + mCurrent = mCurrent->_mNext; + return rv; + } + + const_reference operator*() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return *NS_STATIC_CAST(const_pointer, mCurrent); + } + + const_pointer operator->() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } + + const_pointer get() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } + +#ifndef __MWERKS__ + operator const_pointer() const + { +#ifdef NS_LINELIST_DEBUG_PASS_END + NS_ASSERTION(mCurrent != mListLink, "running past end"); +#endif + return NS_STATIC_CAST(const_pointer, mCurrent); + } +#endif /* !__MWERKS__ */ + + PRBool operator==(const iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(const iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(const iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) const + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) const + { return mCurrent != aOther.mCurrent; } + PRBool operator==(iterator_self_type& aOther) + { return mCurrent == aOther.mCurrent; } + PRBool operator!=(iterator_self_type& aOther) + { return mCurrent != aOther.mCurrent; } + +//private: + const link_type *mCurrent; +#ifdef NS_LINELIST_DEBUG_PASS_END + const link_type *mListLink; // the list's link, i.e., the end +#endif +}; + +class nsLineList { + + public: + + friend class nsLineList_iterator; + friend class nsLineList_reverse_iterator; + friend class nsLineList_const_iterator; + friend class nsLineList_const_reverse_iterator; + + typedef PRUint32 size_type; + typedef PRInt32 difference_type; + + typedef nsLineLink link_type; + + private: + link_type mLink; + + public: + typedef nsLineList self_type; + + typedef nsLineBox& reference; + typedef const nsLineBox& const_reference; + + typedef nsLineBox* pointer; + typedef const nsLineBox* const_pointer; + + typedef nsLineList_iterator iterator; + typedef nsLineList_reverse_iterator reverse_iterator; + typedef nsLineList_const_iterator const_iterator; + typedef nsLineList_const_reverse_iterator const_reverse_iterator; + + nsLineList() + { + clear(); + } + + const_iterator begin() const + { + const_iterator rv; + rv.mCurrent = mLink._mNext; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + iterator begin() + { + iterator rv; + rv.mCurrent = mLink._mNext; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + const_iterator end() const + { + const_iterator rv; + rv.mCurrent = &mLink; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + iterator end() + { + iterator rv; + rv.mCurrent = &mLink; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + const_reverse_iterator rbegin() const + { + const_reverse_iterator rv; + rv.mCurrent = mLink._mPrev; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + reverse_iterator rbegin() + { + reverse_iterator rv; + rv.mCurrent = mLink._mPrev; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + const_reverse_iterator rend() const + { + const_reverse_iterator rv; + rv.mCurrent = &mLink; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + reverse_iterator rend() + { + reverse_iterator rv; + rv.mCurrent = &mLink; +#ifdef NS_LINELIST_DEBUG_PASS_END + rv.mListLink = &mLink; +#endif + return rv; + } + + PRBool empty() const + { + return mLink._mNext == &mLink; + } + + // NOTE: O(N). + size_type size() const + { + size_type count = 0; + for (const link_type *cur = mLink._mNext; + cur != &mLink; + cur = cur->_mNext) + { + ++count; + } + return count; + } + + pointer front() + { + NS_ASSERTION(!empty(), "no element to return"); + return NS_STATIC_CAST(pointer, mLink._mNext); + } + + const_pointer front() const + { + NS_ASSERTION(!empty(), "no element to return"); + return NS_STATIC_CAST(const_pointer, mLink._mNext); + } + + pointer back() + { + NS_ASSERTION(!empty(), "no element to return"); + return NS_STATIC_CAST(pointer, mLink._mPrev); + } + + const_pointer back() const + { + NS_ASSERTION(!empty(), "no element to return"); + return NS_STATIC_CAST(const_pointer, mLink._mPrev); + } + + void push_front(pointer aNew) + { + aNew->_mNext = mLink._mNext; + mLink._mNext->_mPrev = aNew; + aNew->_mPrev = &mLink; + mLink._mNext = aNew; + } + + void pop_front() + // NOTE: leaves dangling next/prev pointers + { + NS_ASSERTION(!empty(), "no element to pop"); + link_type *newFirst = mLink._mNext->_mNext; + newFirst->_mPrev = &mLink; + // mLink._mNext->_mNext = nsnull; + // mLink._mNext->_mPrev = nsnull; + mLink._mNext = newFirst; + } + + void push_back(pointer aNew) + { + aNew->_mPrev = mLink._mPrev; + mLink._mPrev->_mNext = aNew; + aNew->_mNext = &mLink; + mLink._mPrev = aNew; + } + + void pop_back() + // NOTE: leaves dangling next/prev pointers + { + NS_ASSERTION(!empty(), "no element to pop"); + link_type *newLast = mLink._mPrev->_mPrev; + newLast->_mNext = &mLink; + // mLink._mPrev->_mPrev = nsnull; + // mLink._mPrev->_mNext = nsnull; + mLink._mPrev = newLast; + } + + // inserts x before position + iterator before_insert(iterator position, pointer x) + { + // use |mCurrent| to prevent DEBUG_PASS_END assertions + x->_mPrev = position.mCurrent->_mPrev; + x->_mNext = position.mCurrent; + position.mCurrent->_mPrev->_mNext = x; + position.mCurrent->_mPrev = x; + return --position; + } + + // inserts x after position + iterator after_insert(iterator position, pointer x) + { + // use |mCurrent| to prevent DEBUG_PASS_END assertions + x->_mNext = position.mCurrent->_mNext; + x->_mPrev = position.mCurrent; + position.mCurrent->_mNext->_mPrev = x; + position.mCurrent->_mNext = x; + return ++position; + } + + // returns iterator pointing to after the element + iterator erase(iterator position) + // NOTE: leaves dangling next/prev pointers + { + position->_mPrev->_mNext = position->_mNext; + position->_mNext->_mPrev = position->_mPrev; + return ++position; + } + + void swap(self_type& y) + { + link_type tmp(y.mLink); + y.mLink = mLink; + mLink = tmp; + } + + void clear() + // NOTE: leaves dangling next/prev pointers + { + mLink._mNext = &mLink; + mLink._mPrev = &mLink; + } + + // inserts the conts of x before position and makes x empty + void splice(iterator position, self_type& x) + { + // use |mCurrent| to prevent DEBUG_PASS_END assertions + position.mCurrent->_mPrev->_mNext = x.mLink._mNext; + x.mLink._mNext->_mPrev = position.mCurrent->_mPrev; + x.mLink._mPrev->_mNext = position.mCurrent; + position.mCurrent->_mPrev = x.mLink._mPrev; + x.clear(); + } + + // Inserts element *i from list x before position and removes + // it from x. + void splice(iterator position, self_type& x, iterator i) + { + NS_ASSERTION(!x.empty(), "Can't insert from empty list."); + NS_ASSERTION(position != i && position != i->_mNext, + "We don't check for this case."); + + // remove from |x| + i->_mPrev->_mNext = i->_mNext; + i->_mNext->_mPrev = i->_mPrev; + + // use |mCurrent| to prevent DEBUG_PASS_END assertions + // link into |this|, before-side + i->_mPrev = position.mCurrent->_mPrev; + position.mCurrent->_mPrev->_mNext = i.get(); + + // link into |this|, after-side + i->_mNext = position.mCurrent; + position.mCurrent->_mPrev = i.get(); + } + + // Inserts elements in [|first|, |last|), which are in |x|, + // into |this| before |position| and removes them from |x|. + void splice(iterator position, self_type& x, iterator first, + iterator last) + { + NS_ASSERTION(!x.empty(), "Can't insert from empty list."); + + if (first == last) + return; + + --last; // so we now want to move [first, last] + // remove from |x| + first->_mPrev->_mNext = last->_mNext; + last->_mNext->_mPrev = first->_mPrev; + + // use |mCurrent| to prevent DEBUG_PASS_END assertions + // link into |this|, before-side + first->_mPrev = position.mCurrent->_mPrev; + position.mCurrent->_mPrev->_mNext = first.get(); + + // link into |this|, after-side + last->_mNext = position.mCurrent; + position.mCurrent->_mPrev = last.get(); + } + +}; + + +// Many of these implementations of operator= don't work yet. I don't +// know why. + +#ifdef NS_LINELIST_DEBUG_PASS_END + + // NOTE: ASSIGN_FROM is meant to be used *only* as the entire body + // of a function and therefore lacks PR_{BEGIN,END}_MACRO +#define ASSIGN_FROM(other_) \ + mCurrent = other_.mCurrent; \ + mListLink = other_.mListLink; \ + return *this; + +#else /* !NS_LINELIST_DEBUG_PASS_END */ + +#define ASSIGN_FROM(other_) \ + mCurrent = other_.mCurrent; \ + return *this; + +#endif /* !NS_LINELIST_DEBUG_PASS_END */ + +inline +nsLineList_iterator& +nsLineList_iterator::operator=(const nsLineList_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_iterator& +nsLineList_iterator::operator=(const nsLineList_reverse_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_reverse_iterator& +nsLineList_reverse_iterator::operator=(const nsLineList_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_reverse_iterator& +nsLineList_reverse_iterator::operator=(const nsLineList_reverse_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_iterator& +nsLineList_const_iterator::operator=(const nsLineList_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_iterator& +nsLineList_const_iterator::operator=(const nsLineList_reverse_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_iterator& +nsLineList_const_iterator::operator=(const nsLineList_const_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_iterator& +nsLineList_const_iterator::operator=(const nsLineList_const_reverse_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_reverse_iterator& +nsLineList_const_reverse_iterator::operator=(const nsLineList_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_reverse_iterator& +nsLineList_const_reverse_iterator::operator=(const nsLineList_reverse_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_reverse_iterator& +nsLineList_const_reverse_iterator::operator=(const nsLineList_const_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + +inline +nsLineList_const_reverse_iterator& +nsLineList_const_reverse_iterator::operator=(const nsLineList_const_reverse_iterator& aOther) +{ + ASSIGN_FROM(aOther) +} + + //---------------------------------------------------------------------- class nsLineIterator : public nsILineIteratorNavigator { @@ -441,7 +1462,7 @@ public: nsIFrame **aFirstVisual, nsIFrame **aLastVisual); #endif - nsresult Init(nsLineBox* aLines, PRBool aRightToLeft); + nsresult Init(nsLineList& aLines, PRBool aRightToLeft); protected: PRInt32 NumLines() const { diff --git a/mozilla/layout/html/base/src/nsLineLayout.cpp b/mozilla/layout/html/base/src/nsLineLayout.cpp index c9e13f4a266..7ffad424fc1 100644 --- a/mozilla/layout/html/base/src/nsLineLayout.cpp +++ b/mozilla/layout/html/base/src/nsLineLayout.cpp @@ -1647,8 +1647,6 @@ nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics) // Record ascent and update max-ascent and max-descent values pfd->mAscent = aMetrics.ascent; pfd->mDescent = aMetrics.descent; -//XXX mCarriedOutTopMargin = aMetrics.mCarriedOutTopMargin; - mCarriedOutBottomMargin = aMetrics.mCarriedOutBottomMargin; // If the band was updated during the reflow of that frame then we // need to adjust any prior frames that were reflowed. @@ -1710,8 +1708,8 @@ void nsLineLayout::DumpPerSpanData(PerSpanData* psd, PRInt32 aIndent) { nsFrame::IndentBy(stdout, aIndent); - printf("%p: left=%d x=%d right=%d\n", psd, psd->mLeftEdge, - psd->mX, psd->mRightEdge); + printf("%p: left=%d x=%d right=%d\n", NS_STATIC_CAST(void*, psd), + psd->mLeftEdge, psd->mX, psd->mRightEdge); PerFrameData* pfd = psd->mFirstFrame; while (nsnull != pfd) { nsFrame::IndentBy(stdout, aIndent+1); @@ -2129,7 +2127,6 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd) nsCOMPtr fm; rc->GetFontMetrics(*getter_AddRefs(fm)); - PRBool zeroEffectiveSpanBox = PR_FALSE; PRBool preMode = (mStyleText->mWhiteSpace == NS_STYLE_WHITESPACE_PRE) || (mStyleText->mWhiteSpace == NS_STYLE_WHITESPACE_MOZ_PRE_WRAP); @@ -2189,6 +2186,13 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd) // This code works correctly for preMode, because a blank line // in PRE mode is encoded as a text node with a LF in it, since // text nodes with only whitespace are considered in preMode. + // + // Much of this logic is shared with the various implementations of + // nsIFrame::IsEmpty since they need to duplicate the way it makes + // some lines empty. However, nsIFrame::IsEmpty can't be reused here + // since this code sets zeroEffectiveSpanBox even when there are + // non-empty children. + PRBool zeroEffectiveSpanBox = PR_FALSE; if ((emptyContinuation || !InStrictMode()) && ((psd == mRootSpan) || ((0 == spanFramePFD->mBorderPadding.top) && diff --git a/mozilla/layout/html/base/src/nsLineLayout.h b/mozilla/layout/html/base/src/nsLineLayout.h index 4b451253af4..bb4d4935099 100644 --- a/mozilla/layout/html/base/src/nsLineLayout.h +++ b/mozilla/layout/html/base/src/nsLineLayout.h @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:cindent:ts=2:et:sw=2: * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file @@ -105,10 +106,6 @@ public: nsHTMLReflowMetrics* aMetrics, PRBool& aPushedFrame); - nscoord GetCarriedOutBottomMargin() const { - return mCarriedOutBottomMargin; - } - nsresult AddBulletFrame(nsIFrame* aFrame, const nsHTMLReflowMetrics& aMetrics); @@ -269,7 +266,7 @@ public: protected: // This state is constant for a given block frame doing line layout nsISpaceManager* mSpaceManager; - const nsStyleText* mStyleText; + const nsStyleText* mStyleText; // for the block const nsHTMLReflowState* mBlockReflowState; nsBlockReflowState* mBlockRS;/* XXX hack! */ nscoord mMinLineHeight; @@ -294,7 +291,6 @@ protected: nscoord mBottomEdge; nscoord mMaxTopBoxHeight; nscoord mMaxBottomBoxHeight; - nscoord mCarriedOutBottomMargin; // Final computed line-height value after VerticalAlignFrames for // the block has been called. diff --git a/mozilla/layout/html/base/src/nsObjectFrame.cpp b/mozilla/layout/html/base/src/nsObjectFrame.cpp index fab6eff72e6..f5bc2a7f58d 100644 --- a/mozilla/layout/html/base/src/nsObjectFrame.cpp +++ b/mozilla/layout/html/base/src/nsObjectFrame.cpp @@ -120,7 +120,7 @@ static NS_DEFINE_CID(kPrintOptionsCID, NS_PRINTOPTIONS_CID); #include "nsIJVMManager.h" #include "nsContentCID.h" -static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID); +static NS_DEFINE_CID(kRangeCID, NS_RANGE_CID); // XXX temporary for Mac double buffering pref static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); @@ -362,10 +362,10 @@ NS_IMETHODIMP_(nsrefcnt) nsObjectFrame::Release(void) return 1; } -static NS_DEFINE_IID(kViewCID, NS_VIEW_CID); -static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID); -static NS_DEFINE_IID(kCAppShellCID, NS_APPSHELL_CID); -static NS_DEFINE_IID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); +static NS_DEFINE_CID(kViewCID, NS_VIEW_CID); +static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID); +static NS_DEFINE_CID(kCAppShellCID, NS_APPSHELL_CID); +static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); PRIntn nsObjectFrame::GetSkipSides() const diff --git a/mozilla/layout/html/base/src/nsPlaceholderFrame.cpp b/mozilla/layout/html/base/src/nsPlaceholderFrame.cpp index a90ecd11f42..754e6868a80 100644 --- a/mozilla/layout/html/base/src/nsPlaceholderFrame.cpp +++ b/mozilla/layout/html/base/src/nsPlaceholderFrame.cpp @@ -87,6 +87,13 @@ nsPlaceholderFrame::GetFrameType(nsIAtom** aType) const return NS_OK; } +NS_IMETHODIMP +nsPlaceholderFrame::IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool *aResult) +{ + *aResult = PR_TRUE; + return NS_OK; +} + #ifdef DEBUG NS_IMETHODIMP nsPlaceholderFrame::Paint(nsIPresContext* aPresContext, @@ -122,12 +129,12 @@ nsPlaceholderFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aInden IndentBy(out, aIndent); ListTag(out); #ifdef DEBUG_waterson - fprintf(out, " [parent=%p]", mParent); + fprintf(out, " [parent=%p]", NS_STATIC_CAST(void*, mParent)); #endif nsIView* view; GetView(aPresContext, &view); if (nsnull != view) { - fprintf(out, " [view=%p]", view); + fprintf(out, " [view=%p]", (void*)view); } fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height); if (0 != mState) { diff --git a/mozilla/layout/html/base/src/nsPlaceholderFrame.h b/mozilla/layout/html/base/src/nsPlaceholderFrame.h index a75851d77bd..ea55a15b0a7 100644 --- a/mozilla/layout/html/base/src/nsPlaceholderFrame.h +++ b/mozilla/layout/html/base/src/nsPlaceholderFrame.h @@ -82,6 +82,8 @@ public: NS_IMETHOD GetFrameName(nsString& aResult) const; #endif + NS_IMETHOD IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool *aResult); + protected: nsIFrame* mOutOfFlowFrame; }; diff --git a/mozilla/layout/html/base/src/nsScrollFrame.cpp b/mozilla/layout/html/base/src/nsScrollFrame.cpp index 48b1030286b..26366bd5d6a 100644 --- a/mozilla/layout/html/base/src/nsScrollFrame.cpp +++ b/mozilla/layout/html/base/src/nsScrollFrame.cpp @@ -63,9 +63,8 @@ #undef NOISY_SECOND_REFLOW -static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID); -static NS_DEFINE_IID(kScrollingViewCID, NS_SCROLLING_VIEW_CID); -static NS_DEFINE_IID(kViewCID, NS_VIEW_CID); +static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID); +static NS_DEFINE_CID(kScrollingViewCID, NS_SCROLLING_VIEW_CID); //---------------------------------------------------------------------- @@ -139,7 +138,7 @@ nsScrollFrame::GetClipSize( nsIPresContext* aPresContext, nsIScrollableView* scrollingView; nsIView* view; GetView(aPresContext, &view); - if (NS_SUCCEEDED(view->QueryInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollingView))) { + if (NS_SUCCEEDED(CallQueryInterface(view, &scrollingView))) { const nsIView* clip = nsnull; scrollingView->GetClipView(&clip); clip->GetDimensions(aWidth, aHeight); @@ -162,7 +161,7 @@ nsScrollFrame::GetScrollPreference(nsIPresContext* aPresContext, nsScrollPref* a nsIScrollableView* scrollingView; nsIView* view; GetView(aPresContext, &view); - if (NS_SUCCEEDED(view->QueryInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollingView))) { + if (NS_SUCCEEDED(CallQueryInterface(view, &scrollingView))) { nsScrollPreference pref; scrollingView->GetScrollPreference(pref); switch(pref) @@ -217,7 +216,7 @@ nsScrollFrame::GetScrollPosition(nsIPresContext* aPresContext, nscoord &aX, nsco nsIScrollableView* scrollingView; nsIView* view; GetView(aPresContext, &view); - nsresult rv = view->QueryInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollingView); + nsresult rv = CallQueryInterface(view, &scrollingView); NS_ASSERTION(NS_SUCCEEDED(rv), "No scrolling view"); return scrollingView->GetScrollPosition(aX, aY); } @@ -229,7 +228,7 @@ nsScrollFrame::ScrollTo(nsIPresContext* aPresContext, nscoord aX, nscoord aY, PR nsIView* view; GetView(aPresContext, &view); - nsresult rv = view->QueryInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollingView); + nsresult rv = CallQueryInterface(view, &scrollingView); NS_ASSERTION(NS_SUCCEEDED(rv), "No scrolling view"); return scrollingView->ScrollTo(aX, aY, aFlags); @@ -248,7 +247,7 @@ nsScrollFrame::GetScrollbarVisibility(nsIPresContext* aPresContext, nsIScrollableView* scrollingView; nsIView* view; GetView(aPresContext, &view); - if (NS_SUCCEEDED(view->QueryInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollingView))) { + if (NS_SUCCEEDED(CallQueryInterface(view, &scrollingView))) { scrollingView->GetScrollbarVisibility(aVerticalVisible, aHorizontalVisible); } else { aVerticalVisible = PR_FALSE; @@ -268,25 +267,21 @@ nsScrollFrame::SetScrollbarVisibility(nsIPresContext* aPresContext, } NS_IMETHODIMP -nsScrollFrame::QueryInterface(REFNSIID aIID, void** aInstancePtr) -{ - if (NULL == aInstancePtr) { - return NS_ERROR_NULL_POINTER; - } - - *aInstancePtr = NULL; - - if (aIID.Equals(NS_GET_IID(nsIScrollableFrame))) { - *aInstancePtr = (void*)(nsIScrollableFrame*) this; - return NS_OK; - } +nsScrollFrame::QueryInterface(REFNSIID aIID, void** aInstancePtr) +{ + NS_PRECONDITION(aInstancePtr, "null out param"); + if (aIID.Equals(NS_GET_IID(nsIScrollableFrame))) { + *aInstancePtr = + NS_STATIC_CAST(void*,NS_STATIC_CAST(nsIScrollableFrame*,this)); + return NS_OK; + } if (aIID.Equals(NS_GET_IID(nsIStatefulFrame))) { - *aInstancePtr = (void*)(nsIStatefulFrame*) this; + *aInstancePtr = + NS_STATIC_CAST(void*,NS_STATIC_CAST(nsIStatefulFrame*,this)); return NS_OK; } - - return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr); + return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr); } NS_IMETHODIMP @@ -368,7 +363,7 @@ nsScrollFrame::DidReflow(nsIPresContext* aPresContext, nsIScrollableView* scrollingView; nsIView* view; GetView(aPresContext, &view); - if (NS_SUCCEEDED(view->QueryInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollingView))) { + if (NS_SUCCEEDED(CallQueryInterface(view, &scrollingView))) { scrollingView->ComputeScrollOffsets(PR_TRUE); } } @@ -417,10 +412,7 @@ nsScrollFrame::CreateScrollingView(nsIPresContext* aPresContext) parentView->GetViewManager(viewManager); // Create the scrolling view - nsresult rv = nsComponentManager::CreateInstance(kScrollingViewCID, - nsnull, - NS_GET_IID(nsIView), - (void **)&view); + nsresult rv = CallCreateInstance(kScrollingViewCID, &view); if (NS_OK == rv) { const nsStylePosition* position = (const nsStylePosition*) @@ -604,7 +596,7 @@ nsScrollFrame::CalculateScrollAreaSize(nsIPresContext* aPresContext, nsIScrollableView* scrollingView; nsIView* view; GetView(aPresContext, &view); - if (NS_SUCCEEDED(view->QueryInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollingView))) { + if (NS_SUCCEEDED(CallQueryInterface(view, &scrollingView))) { PRBool unused; scrollingView->GetScrollbarVisibility(aRoomForVerticalScrollbar, &unused); } @@ -743,7 +735,7 @@ nsScrollFrame::Reflow(nsIPresContext* aPresContext, nsresult rv = NS_OK; nsIScrollableView *scrollableView = 0; - rv = view->QueryInterface(NS_GET_IID(nsIScrollableView), (void **)&scrollableView); + rv = CallQueryInterface(view, &scrollableView); if (NS_SUCCEEDED(rv) && scrollableView) { nsScrollPreference scrollPref = nsScrollPreference_kAuto; diff --git a/mozilla/layout/html/base/src/nsTextFrame.cpp b/mozilla/layout/html/base/src/nsTextFrame.cpp index 11b06826b2c..0de1df899be 100644 --- a/mozilla/layout/html/base/src/nsTextFrame.cpp +++ b/mozilla/layout/html/base/src/nsTextFrame.cpp @@ -474,6 +474,8 @@ public: PRBool aCheckVis, PRBool* aIsVisible); + NS_IMETHOD IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool* aResult); + #ifdef ACCESSIBILITY NS_IMETHOD GetAccessible(nsIAccessible** aAccessible); #endif @@ -5608,6 +5610,24 @@ nsTextFrame::GetFrameType(nsIAtom** aType) const return NS_OK; } +NS_IMETHODIMP +nsTextFrame::IsEmpty(PRBool aIsQuirkMode, PRBool aIsPre, PRBool* aResult) +{ + // XXXldb Should this check aIsQuirkMode as well??? + if (aIsPre) { + *aResult = PR_FALSE; + return NS_OK; + } + + nsCOMPtr textContent( do_QueryInterface(mContent) ); + if (! textContent) { + NS_NOTREACHED("text frame has no text content"); + *aResult = PR_TRUE; + return NS_ERROR_UNEXPECTED; + } + return textContent->IsOnlyWhitespace(aResult); +} + #ifdef DEBUG NS_IMETHODIMP nsTextFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const @@ -5637,7 +5657,7 @@ nsTextFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) cons nsIView* view; GetView(aPresContext, &view); if (nsnull != view) { - fprintf(out, " [view=%p]", view); + fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, view)); } PRInt32 totalContentLength; @@ -5651,15 +5671,15 @@ nsTextFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) cons isComplete ? 'T':'F'); if (nsnull != mNextSibling) { - fprintf(out, " next=%p", mNextSibling); + fprintf(out, " next=%p", NS_STATIC_CAST(void*, mNextSibling)); } nsIFrame* prevInFlow; GetPrevInFlow(&prevInFlow); if (nsnull != prevInFlow) { - fprintf(out, " prev-in-flow=%p", prevInFlow); + fprintf(out, " prev-in-flow=%p", NS_STATIC_CAST(void*, prevInFlow)); } if (nsnull != mNextInFlow) { - fprintf(out, " next-in-flow=%p", mNextInFlow); + fprintf(out, " next-in-flow=%p", NS_STATIC_CAST(void*, mNextInFlow)); } // Output the rect and state @@ -5671,7 +5691,7 @@ nsTextFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) cons fprintf(out, " [state=%08x]", mState); } } - fprintf(out, " sc=%p<\n", mStyleContext); + fprintf(out, " sc=%p<\n", NS_STATIC_CAST(void*, mStyleContext)); // Output the text aIndent++; diff --git a/mozilla/layout/html/base/src/nsTextTransformer.cpp b/mozilla/layout/html/base/src/nsTextTransformer.cpp index f314affa5fc..8fb2bdc0914 100644 --- a/mozilla/layout/html/base/src/nsTextTransformer.cpp +++ b/mozilla/layout/html/base/src/nsTextTransformer.cpp @@ -100,7 +100,7 @@ nsAutoTextBuffer::GrowTo(PRInt32 aNewSize, PRBool aCopyToHead) //---------------------------------------------------------------------- -static NS_DEFINE_IID(kUnicharUtilCID, NS_UNICHARUTIL_CID); +static NS_DEFINE_CID(kUnicharUtilCID, NS_UNICHARUTIL_CID); static nsICaseConversion* gCaseConv = nsnull; diff --git a/mozilla/layout/html/forms/src/nsListControlFrame.cpp b/mozilla/layout/html/forms/src/nsListControlFrame.cpp index 440292d4138..e2cb32db0a6 100644 --- a/mozilla/layout/html/forms/src/nsListControlFrame.cpp +++ b/mozilla/layout/html/forms/src/nsListControlFrame.cpp @@ -3271,6 +3271,14 @@ nsListControlFrame::GetFrameType(nsIAtom** aType) const return NS_OK; } +#ifdef DEBUG +NS_IMETHODIMP +nsListControlFrame::GetFrameName(nsString& aResult) const +{ + return MakeFrameName("ListControl", aResult); +} +#endif + //--------------------------------------------------------- NS_IMETHODIMP nsListControlFrame::GetMaximumSize(nsSize &aSize) diff --git a/mozilla/layout/html/forms/src/nsListControlFrame.h b/mozilla/layout/html/forms/src/nsListControlFrame.h index 5ff2aa6dae2..6fc6c4d9e92 100644 --- a/mozilla/layout/html/forms/src/nsListControlFrame.h +++ b/mozilla/layout/html/forms/src/nsListControlFrame.h @@ -222,6 +222,11 @@ public: */ NS_IMETHOD GetFrameType(nsIAtom** aType) const; +#ifdef DEBUG + // nsIFrameDebug + NS_IMETHOD GetFrameName(nsString& aResult) const; +#endif + // nsIFormControlFrame NS_IMETHOD GetType(PRInt32* aType) const; NS_IMETHOD GetName(nsString* aName); diff --git a/mozilla/layout/xul/base/src/nsBoxToBlockAdaptor.cpp b/mozilla/layout/xul/base/src/nsBoxToBlockAdaptor.cpp index 1f298ce8a0e..2eb9aa139bd 100644 --- a/mozilla/layout/xul/base/src/nsBoxToBlockAdaptor.cpp +++ b/mozilla/layout/xul/base/src/nsBoxToBlockAdaptor.cpp @@ -625,7 +625,9 @@ nsBoxToBlockAdaptor::Reflow(nsBoxLayoutState& aState, // printf("In debug\n"); if (mSpaceManager == nsnull) { - mSpaceManager = nsSpaceManager::Create(mFrame); + nsCOMPtr shell; + aPresContext->GetShell(getter_AddRefs(shell)); + mSpaceManager = nsSpaceManager::Create(shell, mFrame); NS_ADDREF(mSpaceManager); }