From 8b3489a026d4f95248ecac6b4795b68ef7091d69 Mon Sep 17 00:00:00 2001 From: "dbaron%fas.harvard.edu" Date: Thu, 25 Oct 2001 01:08:40 +0000 Subject: [PATCH] Bug 86947: Make the line list doubly linked and access it through a list class and iterators. Stop recomputing margins on all of the children of each block in the reflow chain (which causes O(N^2) state recovery during incremental reflow). Instead, add a second dirty bit to the lines and walk backwards through the line list to recompute vertical margins only when either dirty bit is set and the previous line was not reflowed. Add nsIFrame::IsEmpty to identify frames through which margins collapse. Fix O(N^2) propagation of float damage by maintaining a set of intervals damaged by floats (bug 61962) and be sure to damage the correct areas (bug 48138). Introduce nsCollapsingMargin to do correct collapsing of combinations of positive and negative margins (bug 50142). Clean up some odds and ends and fix another smaller O(N^2) problem in nsBlockFrame::AddFrames. r=attinasi, rbs sr=waterson git-svn-id: svn://10.0.0.236/trunk@106224 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/layout/base/Makefile.in | 3 + mozilla/layout/base/nsCaret.cpp | 2 +- mozilla/layout/base/nsFrameManager.cpp | 2 +- .../layout/base/public/nsHTMLReflowMetrics.h | 62 +- mozilla/layout/base/public/nsIFrame.h | 15 + mozilla/layout/base/public/nsISpaceManager.h | 14 + mozilla/layout/base/src/Makefile.in | 5 +- mozilla/layout/base/src/makefile.win | 36 +- mozilla/layout/base/src/nsCaret.cpp | 2 +- mozilla/layout/base/src/nsIntervalSet.cpp | 127 ++ mozilla/layout/base/src/nsIntervalSet.h | 125 ++ mozilla/layout/base/src/nsSpaceManager.cpp | 48 +- mozilla/layout/base/src/nsSpaceManager.h | 15 +- mozilla/layout/base/tests/Makefile.in | 28 +- .../layout/base/tests/TestSpaceManager.cpp | 5 +- mozilla/layout/forms/nsListControlFrame.cpp | 8 + mozilla/layout/forms/nsListControlFrame.h | 5 + mozilla/layout/generic/nsBlockFrame.cpp | 1273 ++++++++++------- mozilla/layout/generic/nsBlockFrame.h | 89 +- .../layout/generic/nsBlockReflowContext.cpp | 64 +- mozilla/layout/generic/nsBlockReflowContext.h | 31 +- mozilla/layout/generic/nsBlockReflowState.cpp | 227 +-- mozilla/layout/generic/nsBlockReflowState.h | 25 +- mozilla/layout/generic/nsContainerFrame.cpp | 14 +- mozilla/layout/generic/nsFrame.cpp | 30 +- mozilla/layout/generic/nsFrame.h | 6 +- mozilla/layout/generic/nsGfxScrollFrame.cpp | 6 +- .../layout/generic/nsHTMLContainerFrame.cpp | 4 +- mozilla/layout/generic/nsHTMLReflowMetrics.h | 62 +- mozilla/layout/generic/nsHTMLReflowState.cpp | 9 +- mozilla/layout/generic/nsIFrame.h | 15 + mozilla/layout/generic/nsILineIterator.h | 6 - mozilla/layout/generic/nsInlineFrame.cpp | 78 + mozilla/layout/generic/nsInlineFrame.h | 2 + mozilla/layout/generic/nsIntervalSet.cpp | 127 ++ mozilla/layout/generic/nsIntervalSet.h | 125 ++ mozilla/layout/generic/nsLineBox.cpp | 161 ++- mozilla/layout/generic/nsLineBox.h | 1073 +++++++++++++- mozilla/layout/generic/nsLineLayout.cpp | 14 +- mozilla/layout/generic/nsLineLayout.h | 8 +- mozilla/layout/generic/nsObjectFrame.cpp | 10 +- mozilla/layout/generic/nsPlaceholderFrame.cpp | 11 +- mozilla/layout/generic/nsPlaceholderFrame.h | 2 + mozilla/layout/generic/nsSpaceManager.cpp | 48 +- mozilla/layout/generic/nsSpaceManager.h | 15 +- mozilla/layout/generic/nsTextFrame.cpp | 30 +- mozilla/layout/generic/nsTextTransformer.cpp | 2 +- mozilla/layout/html/base/src/Makefile.in | 10 +- mozilla/layout/html/base/src/makefile.win | 161 +-- mozilla/layout/html/base/src/nsBlockFrame.cpp | 1273 ++++++++++------- mozilla/layout/html/base/src/nsBlockFrame.h | 89 +- .../html/base/src/nsBlockReflowContext.cpp | 64 +- .../html/base/src/nsBlockReflowContext.h | 31 +- .../html/base/src/nsBlockReflowState.cpp | 227 +-- .../layout/html/base/src/nsBlockReflowState.h | 25 +- .../layout/html/base/src/nsContainerFrame.cpp | 14 +- mozilla/layout/html/base/src/nsFrame.cpp | 30 +- mozilla/layout/html/base/src/nsFrame.h | 6 +- .../layout/html/base/src/nsFrameManager.cpp | 2 +- .../layout/html/base/src/nsGfxScrollFrame.cpp | 6 +- .../html/base/src/nsHTMLContainerFrame.cpp | 4 +- .../html/base/src/nsHTMLReflowState.cpp | 9 +- .../layout/html/base/src/nsILineIterator.h | 6 - .../layout/html/base/src/nsInlineFrame.cpp | 78 + mozilla/layout/html/base/src/nsInlineFrame.h | 2 + mozilla/layout/html/base/src/nsLineBox.cpp | 161 ++- mozilla/layout/html/base/src/nsLineBox.h | 1073 +++++++++++++- mozilla/layout/html/base/src/nsLineLayout.cpp | 14 +- mozilla/layout/html/base/src/nsLineLayout.h | 8 +- .../layout/html/base/src/nsObjectFrame.cpp | 10 +- .../html/base/src/nsPlaceholderFrame.cpp | 11 +- .../layout/html/base/src/nsPlaceholderFrame.h | 2 + .../layout/html/base/src/nsScrollFrame.cpp | 52 +- mozilla/layout/html/base/src/nsTextFrame.cpp | 30 +- .../html/base/src/nsTextTransformer.cpp | 2 +- .../html/forms/src/nsListControlFrame.cpp | 8 + .../html/forms/src/nsListControlFrame.h | 5 + .../xul/base/src/nsBoxToBlockAdaptor.cpp | 4 +- 78 files changed, 5366 insertions(+), 2120 deletions(-) create mode 100644 mozilla/layout/base/src/nsIntervalSet.cpp create mode 100644 mozilla/layout/base/src/nsIntervalSet.h create mode 100644 mozilla/layout/generic/nsIntervalSet.cpp create mode 100644 mozilla/layout/generic/nsIntervalSet.h 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); }