diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp
index 59d7407ec5b1..0228ca80d413 100644
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -16,66 +16,37 @@
* Reserved.
*/
#include "nsBlockFrame.h"
-#include "nsSize.h"
-#include "nsIAnchoredItems.h"
-#include "nsIContent.h"
-#include "nsIContentDelegate.h"
-#include "nsISpaceManager.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
-#include "nsIPresContext.h"
-#include "nsMargin.h"
-#include "nsHTMLIIDs.h"
-#include "nsCSSLayout.h"
-#include "nsCRT.h"
-#include "nsIPresShell.h"
-#include "nsReflowCommand.h"
-#include "nsPlaceholderFrame.h"
-#include "nsHTMLAtoms.h"
-#include "nsHTMLValue.h"
#include "nsIHTMLContent.h"
-#include "nsAbsoluteFrame.h"
+#include "nsIPresContext.h"
+#include "nsIPresShell.h"
+#include "nsIAnchoredItems.h"
+#include "nsPlaceholderFrame.h"
#include "nsIPtr.h"
+#include "nsHTMLAtoms.h"
+#include "nsHTMLIIDs.h"
+#include "nsHTMLValue.h"
-#ifdef NS_DEBUG
-#undef NOISY
-#undef NOISY_FLOW
-#else
-#undef NOISY
-#undef NOISY_FLOW
-#endif
+// XXX what do we do with catastrophic errors (rv < 0)? What is the
+// state of the reflow world after such an error?
-static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID);
-static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID);
-static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID);
+#undef NOISY_REFLOW
static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID);
-static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID);
-static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID);
-static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID);
static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID);
-NS_DEF_PTR(nsIStyleContext);
+static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID);
+static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID);
+static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID);
+
NS_DEF_PTR(nsIContent);
+NS_DEF_PTR(nsIStyleContext);
-struct BlockBandData : public nsBandData {
- nsBandTrapezoid data[12];
+//----------------------------------------------------------------------
- // Bounding rect of available space between any left and right floaters
- nsRect availSpace;
- // Constructor
- BlockBandData() {size = 12; trapezoids = data;}
-
- // Computes the bounding rect of the available space, i.e. space between
- // any left and right floaters
- //
- // Uses the current trapezoid data, see nsISpaceManager::GetBandData().
- // Updates member data "availSpace"
- void ComputeAvailSpaceRect();
-};
-
-void BlockBandData::ComputeAvailSpaceRect()
+void nsBlockBandData::ComputeAvailSpaceRect()
{
nsBandTrapezoid* trapezoid = data;
@@ -121,198 +92,60 @@ void BlockBandData::ComputeAvailSpaceRect()
}
}
-// XXX Bugs
-// 1. right to left reflow can generate negative x coordinates.
-
-// XXX Speedup idea (all containers)
-
-// If I reflow a child and it gives back not-complete status then
-// there is no sense in trying to pullup children. For blocks, it's a
-// little more complicated unless the child is a block - if the child
-// is a block, then we must be out of room hence we should stop. If
-// the child is not a block then our line should be flushed (see #2
-// below) if our line is already empty then we must be out of room.
-
-// For inline frames and column frames, if we reflow a child and get
-// back not-complete status then we should bail immediately because we
-// are out of room.
-
-// XXX Speedup ideas:
-// 1. change pullup code to use line information from next in flow
-// 2. we can advance to next line immediately after reflowing something
-// and noticing that it's not complete.
-// 3. pass down last child information in aState so that pullup, etc.,
-// don't need to recompute it
-
-// XXX TODO:
-// 0. Move justification into line flushing code
-
-// 1. To get ebina margins I need "auto" information from the style
-// system margin's. A bottom/top margin of auto will then be computed like
-// ebina computes it [however the heck that is].
-
-// 2. kicking out floaters and talking with floater container to adjust
-// left and right margins
+//----------------------------------------------------------------------
nsBlockReflowState::nsBlockReflowState()
{
}
-void nsBlockReflowState::Init(const nsSize& aMaxSize,
- nsSize* aMaxElementSize,
- nsIStyleContext* aBlockSC,
- nsISpaceManager* aSpaceManager)
+nsBlockReflowState::~nsBlockReflowState()
{
- firstLine = PR_TRUE;
- allowLeadingWhitespace = PR_FALSE;
- breakAfterChild = PR_FALSE;
- breakBeforeChild = PR_FALSE;
- firstChildIsInsideBullet = PR_FALSE;
- nextListOrdinal = -1;
- column = 0;
+}
- spaceManager = aSpaceManager;
- currentBand = new BlockBandData;
+nsresult
+nsBlockReflowState::Initialize(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockFrame* aBlock)
+{
+ nsresult rv = NS_OK;
- styleContext = aBlockSC;
- styleText = (nsStyleText*) aBlockSC->GetData(kStyleTextSID);
- styleFont = (nsStyleFont*) aBlockSC->GetData(kStyleFontSID);
- styleDisplay = (nsStyleDisplay*) aBlockSC->GetData(kStyleDisplaySID);
+ mPresContext = aPresContext;
+ mBlock = aBlock;
+ mSpaceManager = aSpaceManager;
+ mBlockIsPseudo = aBlock->IsPseudoFrame();
+ mCurrentLine = nsnull;
+ mPrevKidFrame = nsnull;
- justifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
- (NS_STYLE_WHITESPACE_PRE != styleText->mWhiteSpace);
-
- availSize.width = aMaxSize.width;
- availSize.height = aMaxSize.height;
- maxElementSize = aMaxElementSize;
+ mX = 0;
+ mY = 0;
+ mAvailSize = aMaxSize;
+ mUnconstrainedWidth = PRBool(mAvailSize.width == NS_UNCONSTRAINEDSIZE);
+ mUnconstrainedHeight = PRBool(mAvailSize.height == NS_UNCONSTRAINEDSIZE);
+ mMaxElementSizePointer = aMaxElementSize;
if (nsnull != aMaxElementSize) {
aMaxElementSize->width = 0;
aMaxElementSize->height = 0;
}
+ mKidXMost = 0;
- kidXMost = 0;
- x = 0;
- y = 0;
+ mPrevMaxPosBottomMargin = 0;
+ mPrevMaxNegBottomMargin = 0;
- isInline = PR_FALSE;
- currentLineNumber = 0;
- lineStart = nsnull;
- lineLength = 0;
- ascents = ascentBuf;
- maxAscent = 0;
- maxDescent = 0;
- lineWidth = 0;
- maxPosBottomMargin = 0;
- maxNegBottomMargin = 0;
- lineMaxElementSize.width = 0;
- lineMaxElementSize.height = 0;
- lastContentIsComplete = PR_TRUE;
+ mNextListOrdinal = -1;
+ mFirstChildIsInsideBullet = PR_FALSE;
- maxAscents = sizeof(ascentBuf) / sizeof(ascentBuf[0]);
- needRelativePos = PR_FALSE;
-
- prevLineLastFrame = nsnull;
- prevLineHeight = 0;
- topMargin = 0;
- prevMaxPosBottomMargin = 0;
- prevMaxNegBottomMargin = 0;
- prevLineLastContentIsComplete = PR_TRUE;
-
- unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE);
- unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE);
-
- reflowStatus = nsIFrame::frNotComplete;
+ return rv;
}
-nsBlockReflowState::~nsBlockReflowState()
-{
- if (ascents != ascentBuf) {
- delete ascents;
- }
- delete currentBand;
-}
-
-void nsBlockReflowState::AddAscent(nscoord aAscent)
-{
- NS_PRECONDITION(lineLength <= maxAscents, "bad line length");
- if (lineLength == maxAscents) {
- maxAscents *= 2;
- nscoord* newAscents = new nscoord[maxAscents];
- if (nsnull != newAscents) {
- nsCRT::memcpy(newAscents, ascents, sizeof(nscoord) * lineLength);
- if (ascents != ascentBuf) {
- delete ascents;
- }
- ascents = newAscents;
- } else {
- // Yikes! Out of memory!
- return;
- }
- }
- ascents[lineLength] = aAscent;
-}
-
-void nsBlockReflowState::AdvanceToNextLine(nsIFrame* aPrevLineLastFrame,
- nscoord aPrevLineHeight)
-{
- firstLine = PR_FALSE;
- allowLeadingWhitespace = PR_FALSE;
- column = 0;
- breakAfterChild = PR_FALSE;
- breakBeforeChild = PR_FALSE;
- lineStart = nsnull;
- lineLength = 0;
- currentLineNumber++;
- maxAscent = 0;
- maxDescent = 0;
- lineWidth = 0;
- needRelativePos = PR_FALSE;
-
- prevLineLastFrame = aPrevLineLastFrame;
- prevLineHeight = aPrevLineHeight;
- prevMaxPosBottomMargin = maxPosBottomMargin;
- prevMaxNegBottomMargin = maxNegBottomMargin;
-
- // Remember previous line's lastContentIsComplete
- prevLineLastContentIsComplete = lastContentIsComplete;
- lastContentIsComplete = PR_TRUE;
-
- topMargin = 0;
- maxPosBottomMargin = 0;
- maxNegBottomMargin = 0;
-}
-
-#ifdef NS_DEBUG
-void nsBlockReflowState::DumpLine()
-{
- nsIFrame* f = lineStart;
- PRInt32 ll = lineLength;
- while (--ll >= 0) {
- printf(" ");
- ((nsFrame*)f)->ListTag(stdout);/* XXX */
- printf("\n");
- f->GetNextSibling(f);
- }
-}
-
-void nsBlockReflowState::DumpList()
-{
- nsIFrame* f = lineStart;
- while (nsnull != f) {
- printf(" ");
- ((nsFrame*)f)->ListTag(stdout);/* XXX */
- printf("\n");
- f->GetNextSibling(f);
- }
-}
-#endif
-
//----------------------------------------------------------------------
-nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
- nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent)
+nsresult
+nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
+ nsIContent* aContent,
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
if (nsnull == aInstancePtrResult) {
@@ -327,963 +160,818 @@ nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
}
nsBlockFrame::nsBlockFrame(nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent)
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent)
: nsHTMLContainerFrame(aContent, aIndexInParent, aParent)
{
}
nsBlockFrame::~nsBlockFrame()
{
- if (nsnull != mLines) {
- delete mLines;
- }
+ DestroyLines();
}
-nsresult
+void nsBlockFrame::DestroyLines()
+{
+}
+
+NS_METHOD
nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
NS_PRECONDITION(0 != aInstancePtr, "null ptr");
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
}
- if (aIID.Equals(kIHTMLFrameTypeIID)) {
- *aInstancePtr = (void*) ((nsIHTMLFrameType*) this);
+ if (aIID.Equals(kBlockFrameCID)) {
+ *aInstancePtr = (void*) (this);
return NS_OK;
- } else if (aIID.Equals(kIRunaroundIID)) {
+ }
+ else if (aIID.Equals(kIRunaroundIID)) {
*aInstancePtr = (void*) ((nsIRunaround*) this);
return NS_OK;
- } else if (aIID.Equals(kIFloaterContainerIID)) {
+ }
+ else if (aIID.Equals(kIFloaterContainerIID)) {
*aInstancePtr = (void*) ((nsIFloaterContainer*) this);
return NS_OK;
}
return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
}
-// Computes the top margin to use for this child frames based on its display
-// type and the display type of the previous child frame.
-//
-// Adjacent vertical margins between block-level elements are collapsed.
-nscoord nsBlockFrame::GetTopMarginFor(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC,
- PRBool aIsInline)
+NS_METHOD
+nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const
{
- if (aIsInline) {
- // Just use whatever the previous bottom margin was
- return aState.prevMaxPosBottomMargin - aState.prevMaxNegBottomMargin;
+ aIsSplittable = frSplittableNonRectangular;
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX,
+ nsIFrame* aParent,
+ nsIFrame*& aContinuingFrame)
+{
+ nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent);
+ PrepareContinuingFrame(aCX, aParent, cf);
+ aContinuingFrame = cf;
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::ListTag(FILE* out) const
+{
+ if ((nsnull != mGeometricParent) && IsPseudoFrame()) {
+ fprintf(out, "*block<");
+ nsIAtom* atom = mContent->GetTag();
+ if (nsnull != atom) {
+ nsAutoString tmp;
+ atom->ToString(tmp);
+ fputs(tmp, out);
+ }
+ fprintf(out, ">(%d)@%p", mIndexInParent, this);
} else {
- // Does the frame have a prev-in-flow?
- nsIFrame* kidPrevInFlow;
+ nsHTMLContainerFrame::ListTag(out);
+ }
+ return NS_OK;
+}
- aKidFrame->GetPrevInFlow(kidPrevInFlow);
+NS_METHOD
+nsBlockFrame::List(FILE* out, PRInt32 aIndent) const
+{
+ // Indent
+ for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
- if (nsnull == kidPrevInFlow) {
+ // Output the tag
+ ListTag(out);
+
+ // Output the first/last content offset
+ fprintf(out, "[%d,%d,%c] pif=%p nif=%p",
+ mFirstContentOffset, mLastContentOffset,
+ (mLastContentIsComplete ? 'T' : 'F'),
+ mPrevInFlow, mNextInFlow);
+
+ // Output the rect
+ out << mRect;
+
+ // Output the children, one line at a time
+ if (nsnull != mLines) {
+ fputs("<\n", out);
+ aIndent++;
+
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ line->List(out, aIndent);
+ line = line->mNextLine;
+ }
+
+ aIndent--;
+ for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
+ fputs(">\n", out);
+ } else {
+ fputs("<>\n", out);
+ }
+
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::VerifyTree() const
+{
+ nsresult rv = nsHTMLContainerFrame::VerifyTree();
+ if (NS_OK != rv) {
+ return rv;
+ }
+ rv = VerifyLines(PR_TRUE);
+ return rv;
+}
+
+nsresult
+nsBlockFrame::VerifyLines(PRBool aFinalCheck) const
+{
+ nsresult rv = NS_OK;
+
+ // Make sure that the list of children agrees with our child count.
+ // If this is not the case then the child list and the line list are
+ // not properly arranged.
+ PRInt32 len = LengthOf(mFirstChild);
+ NS_ASSERTION(mChildCount == len, "bad child list");
+
+ // Verify that our lines are correctly setup
+ PRInt32 offset = mFirstContentOffset;
+ PRInt32 lineChildCount = 0;
+ nsLineData* line = mLines;
+ nsLineData* prevLine = nsnull;
+ while (nsnull != line) {
+ if (aFinalCheck) {
+ NS_ASSERTION(((offset == line->mFirstContentOffset) &&
+ (line->mFirstContentOffset <= line->mLastContentOffset)),
+ "bad line mFirstContentOffset");
+ NS_ASSERTION(line->mLastContentOffset <= mLastContentOffset,
+ "bad line mLastContentOffset");
+ offset = line->mLastContentOffset;
+ if (line->mLastContentIsComplete) {
+ offset++;
+ }
+ }
+ lineChildCount += line->mChildCount;
+ rv = line->Verify(aFinalCheck);
+ if (NS_OK != rv) {
+ return rv;
+ }
+ prevLine = line;
+ line = line->mNextLine;
+ }
+ if (aFinalCheck && (nsnull != prevLine)) {
+ NS_ASSERTION(prevLine->mLastContentOffset == mLastContentOffset,
+ "bad mLastContentOffset");
+ NS_ASSERTION(prevLine->mLastContentIsComplete == mLastContentIsComplete,
+ "bad mLastContentIsComplete");
+ }
+ NS_ASSERTION(lineChildCount == mChildCount, "bad line counts");
+
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+// Remove a next-in-flow from from this block's list of lines
+
+// XXX problems here:
+// 1. we always have to start from the first line: slow!
+// 2. we can avoid this when the child is not the last child in a line
+
+void
+nsBlockFrame::WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow)
+{
+ // When a reflow indicates completion it's possible that
+ // next-in-flows were just removed. We have to remove them from any
+ // nsLineData's that follow the current line.
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ if (line->mFirstChild == aNextInFlow) {
+ // Remove child from line.
+ if (0 == --line->mChildCount) {
+ line->mFirstChild = nsnull;
+ }
+ else {
+ // Fixup the line
+ nsIFrame* nextKid;
+ aNextInFlow->GetNextSibling(nextKid);
+ line->mFirstChild = nextKid;
+ nextKid->GetIndexInParent(line->mFirstContentOffset);
+ }
+ break;
+ }
+ line = line->mNextLine;
+ }
+}
+
+nsresult
+nsBlockFrame::ReflowInlineChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ aStatus = ReflowChild(aKidFrame, aPresContext, aDesiredSize, aMaxSize,
+ aMaxElementSize);
+ return NS_OK;
+}
+
+nsresult
+nsBlockFrame::ReflowBlockChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ aStatus = ReflowChild(aKidFrame, aPresContext, aSpaceManager,
+ aMaxSize, aDesiredRect, aMaxElementSize);
+ return NS_OK;
+}
+
+nsLineData*
+nsBlockFrame::CreateLineForOverflowList(nsIFrame* aOverflowList)
+{
+ nsLineData* newLine = new nsLineData();
+ if (nsnull != newLine) {
+ nsIFrame* kid = aOverflowList;
+ newLine->mFirstChild = kid;
+ kid->GetIndexInParent(newLine->mFirstContentOffset);
+ newLine->mLastContentOffset = -1;
+ newLine->mLastContentIsComplete = PRPackedBool(0x255);
+ PRInt32 kids = 0;
+ while (nsnull != kid) {
+ kids++;
+ kid->GetNextSibling(kid);
+ }
+ newLine->mChildCount = kids;
+ }
+ return newLine;
+}
+
+void
+nsBlockFrame::DrainOverflowList()
+{
+ nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow;
+ if (nsnull != prevBlock) {
+ nsIFrame* overflowList = prevBlock->mOverflowList;
+ if (nsnull != overflowList) {
+ NS_ASSERTION(nsnull == mFirstChild, "bad overflow list");
+ NS_ASSERTION(nsnull == mLines, "bad overflow list");
+
+ // Create a line to hold the entire overflow list
+ nsLineData* newLine = CreateLineForOverflowList(overflowList);
+
+ // Place the children on our child list; this also reassigns
+ // their geometric parent and updates our mChildCount.
+ AppendChildren(overflowList);
+ prevBlock->mOverflowList = nsnull;
+
+ // The new line is the first line
+ mLines = newLine;
+ }
+ }
+
+ if (nsnull != mOverflowList) {
+ NS_ASSERTION(nsnull != mFirstChild,
+ "overflow list but no mapped children");
+
+ // Create a line to hold the overflow list
+ nsLineData* newLine = CreateLineForOverflowList(mOverflowList);
+
+ // Place the children on our child list; this also reassigns
+ // their geometric parent and updates our mChildCount.
+ AppendChildren(mOverflowList, PR_FALSE);
+ mOverflowList = nsnull;
+
+ // The new line is appended after our other lines
+ nsLineData* prevLine = nsnull;
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ prevLine = line;
+ line = line->mNextLine;
+ }
+ if (nsnull == prevLine) {
+ mLines = newLine;
+ }
+ else {
+ prevLine->mNextLine = newLine;
+ newLine->mPrevLine = prevLine;
+ }
+ }
+#ifdef NS_DEBUG
+ VerifyLines(PR_FALSE);
+#endif
+}
+
+// XXX add in code here that notices if margin's were not provided by
+// the style system and when that is the case to apply the old layout
+// engines margin calculations.
+
+nsresult
+nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
+ nsLineLayout& aLineLayout,
+ nsLineData* aLine)
+{
+ nsresult rv = NS_LINE_LAYOUT_COMPLETE;
+
+ nscoord topMargin = 0;
+ nscoord bottomMargin = 0;
+ nscoord maxNegBottomMargin = 0;
+ nscoord maxPosBottomMargin = 0;
+
+ // See if block margins apply to this line or not
+ PRBool isBlockLine = PR_FALSE;
+ if (1 == aLine->mChildCount) {
+ nsIStyleContextPtr kidSC;
+ nsIFrame* kid = aLine->mFirstChild;
+ rv = kid->GetStyleContext(aState.mPresContext, kidSC.AssignRef());
+ if (NS_OK != rv) return rv;
+
+ nsStyleDisplay* display = (nsStyleDisplay*)
+ kidSC->GetData(kStyleDisplaySID);
+ switch (display->mDisplay) {
+ case NS_STYLE_DISPLAY_BLOCK:
+ case NS_STYLE_DISPLAY_LIST_ITEM:
+ isBlockLine = PR_TRUE;
+ nsStyleSpacing* spacing = (nsStyleSpacing*)
+ kidSC->GetData(kStyleSpacingSID);
+
+ // Calculate top margin by collapsing with previous bottom margin
+ // if any.
nscoord maxNegTopMargin = 0;
nscoord maxPosTopMargin = 0;
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- if (ss->mMargin.top < 0) {
- maxNegTopMargin = -ss->mMargin.top;
+ if (spacing->mMargin.top < 0) {
+ maxNegTopMargin = -spacing->mMargin.top;
} else {
- maxPosTopMargin = ss->mMargin.top;
+ maxPosTopMargin = spacing->mMargin.top;
}
-
- nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin);
- nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin);
- return maxPos - maxNeg;
- } else {
- return 0;
+ nscoord maxPos = PR_MAX(aState.mPrevMaxPosBottomMargin, maxPosTopMargin);
+ nscoord maxNeg = PR_MAX(aState.mPrevMaxNegBottomMargin, maxNegTopMargin);
+ topMargin = maxPos - maxNeg;
+
+ // Save away bottom information for later promotion into aState
+ if (spacing->mMargin.bottom < 0) {
+ maxNegBottomMargin = -spacing->mMargin.bottom;
+ } else {
+ maxPosBottomMargin = spacing->mMargin.bottom;
+ }
+ break;
}
}
+
+ // Before we move the line, make sure that it will fit in it's new
+ // location. It always fits if the height isn't constrained or it's
+ // the first line.
+ nscoord totalHeight = topMargin + aLine->mBounds.height;
+ if (!aState.mUnconstrainedHeight && (aLine != mLines)) {
+ if (aState.mY + totalHeight > aState.mAvailSize.height) {
+ // The line will not fit
+ rv = PushLines(aState, aLine);
+ goto done;
+ }
+ }
+ if (isBlockLine) {
+ if (0 != topMargin) {
+ // We have to move the line now that we know the top margin
+ // for it.
+ aLine->MoveLineBy(0, topMargin);
+ aState.mY += topMargin;
+ }
+ }
+ else {
+ // Apply previous line's bottom margin before the inline-line.
+ nscoord bottomMargin = aState.mPrevMaxPosBottomMargin -
+ aState.mPrevMaxNegBottomMargin;
+ if (0 != bottomMargin) {
+ aLine->MoveLineBy(0, bottomMargin);
+ aState.mY += topMargin;
+ }
+ }
+
+ // Consume space and advance running values
+ aState.mY += aLine->mBounds.height;
+ aState.mPrevMaxNegBottomMargin = maxNegBottomMargin;
+ aState.mPrevMaxPosBottomMargin = maxPosBottomMargin;
+ if (nsnull != aState.mMaxElementSizePointer) {
+ nsSize* maxSize = aState.mMaxElementSizePointer;
+ if (aLineLayout.mReflowData.mMaxElementSize.width > maxSize->width) {
+ maxSize->width = aLineLayout.mReflowData.mMaxElementSize.width;
+ }
+ if (aLineLayout.mReflowData.mMaxElementSize.height > maxSize->height) {
+ maxSize->height = aLineLayout.mReflowData.mMaxElementSize.height;
+ }
+ }
+ {
+ nscoord xmost = aLine->mBounds.XMost();
+ if (xmost > aState.mKidXMost) {
+ aState.mKidXMost = xmost;
+ }
+ }
+
+ // Any below current line floaters to place?
+ if (aState.mPendingFloaters.Count() > 0) {
+ PlaceBelowCurrentLineFloaters(aState, aState.mY);
+ // XXX Factor in the height of the floaters as well when considering
+ // whether the line fits.
+ // The default policy is that if there isn't room for the floaters then
+ // both the line and the floaters are pushed to the next-in-flow...
+ }
+
+ if (aState.mY >= aState.mCurrentBand.availSpace.YMost()) {
+ // The current y coordinate is now past our available space
+ // rectangle. Get a new band of space.
+ GetAvailableSpace(aState, aState.mY);
+ }
+
+done:
+ return rv;
}
-void nsBlockFrame::PlaceBelowCurrentLineFloaters(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nscoord aY)
+// aY has borderpadding.top already factored in
+// aResult is relative to left,aY
+nsresult
+nsBlockFrame::GetAvailableSpace(nsBlockReflowState& aState, nscoord aY)
{
- NS_PRECONDITION(aState.floaterToDo.Count() > 0, "no floaters");
+ nsresult rv = NS_OK;
- // XXX Factor this code with PlaceFloater()...
- PRInt32 numFloaters = aState.floaterToDo.Count();
-
- for (PRInt32 i = 0; i < numFloaters; i++) {
- nsIFrame* floater = (nsIFrame*)aState.floaterToDo[i];
- nsRect region;
+ nsISpaceManager* sm = aState.mSpaceManager;
- // Get the band of available space
- // XXX This is inefficient to do this inside the loop...
- GetAvailableSpaceBand(aState, aY);
+ // Fill in band data for the specific Y coordinate
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->GetBandData(aY, aState.mAvailSize, aState.mCurrentBand);
+ sm->Translate(-aState.mBorderPadding.left, 0);
- // Get the type of floater
- nsIStyleContextPtr styleContext;
-
- floater->GetStyleContext(aCX, styleContext.AssignRef());
- nsStyleDisplay* sd = (nsStyleDisplay*)
- styleContext->GetData(kStyleDisplaySID);
-
- floater->GetRect(region);
- region.y = mCurrentState->currentBand->availSpace.y;
+ // Compute the bounding rect of the available space, i.e. space
+ // between any left and right floaters
+ aState.mCurrentBand.ComputeAvailSpaceRect();
- if (NS_STYLE_FLOAT_LEFT == sd->mFloats) {
- region.x = mCurrentState->currentBand->availSpace.x;
- } else {
- NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type");
- region.x = mCurrentState->currentBand->availSpace.XMost() - region.width;
- }
-
- // XXX Don't forget the floater's margins...
- mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left,
- 0);
- mCurrentState->spaceManager->AddRectRegion(region, floater);
-
- // Set the origin of the floater in world coordinates
- nscoord worldX, worldY;
-
- mCurrentState->spaceManager->GetTranslation(worldX, worldY);
- floater->MoveTo(region.x + worldX, region.y + worldY);
- mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0);
- }
- aState.floaterToDo.Clear();
-}
-
-/**
- * Flush a line out. Return true if the line fits in our available
- * height. If the line does not fit then return false. When the line
- * fits we advance the y coordinate, reset the x coordinate and
- * prepare the nsBlockReflowState for the next line.
- */
-PRBool nsBlockFrame::AdvanceToNextLine(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
- NS_PRECONDITION(aState.lineLength > 0, "bad line");
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
-
- nscoord y = aState.y + aState.topMargin;
- nscoord lineHeight;
-
- if (aState.isInline) {
- // Vertically align the children on this line, returning the height of
- // the line upon completion.
- lineHeight = nsCSSLayout::VerticallyAlignChildren(aCX, this,
- aState.styleFont,
- y,
- aState.lineStart,
- aState.lineLength,
- aState.ascents,
- aState.maxAscent);
-
- // Any below current line floaters to place?
- if (aState.floaterToDo.Count() > 0) {
- PlaceBelowCurrentLineFloaters(aCX, aState, y + lineHeight);
- // XXX Factor in the height of the floaters as well when considering
- // whether the line fits.
- // The default policy is that if there isn't room for the floaters then
- // both the line and the floaters are pushed to the next-in-flow...
- }
- } else {
- nsSize size;
-
- aState.lineStart->GetSize(size);
- lineHeight = size.height;
- }
-
- // The first line always fits
- if (aState.currentLineNumber > 0) {
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- if (y + lineHeight > yb) {
- // After vertical alignment of the children and factoring in the
- // proper margin, the line doesn't fit.
- return PR_FALSE;
- }
- }
-
- if (aState.isInline) {
- // Check if the right-edge of the line exceeds our running x-most
- nscoord xMost = aState.borderPadding.left + aState.lineWidth;
- if (xMost > aState.kidXMost) {
- aState.kidXMost = xMost;
- }
- }
-
- // Advance the y coordinate to the new position where the next
- // line or block element will go.
- aState.y = y + lineHeight;
- aState.x = 0;
-
- // Now that the vertical alignment is done we can perform horizontal
- // alignment and relative positioning. Skip all of these if we are
- // doing an unconstrained (in x) reflow. There's no point in doing
- // the work if we *know* we are going to reflowed again.
- if (!aState.unconstrainedWidth) {
- nsCSSLayout::HorizontallyPlaceChildren(aCX, this, aState.styleText,
- aState.lineStart, aState.lineLength,
- aState.lineWidth,
- aState.availSize.width);
-
- // Finally, now that the in-flow positions of the line's frames are
- // known we can apply relative positioning if any of them need it.
- if (!aState.justifying) {
- nsCSSLayout::RelativePositionChildren(aCX, this,
- aState.lineStart,
- aState.lineLength);
- }
- }
-
- // Record line length
- aState.lineLengths.AppendElement((void*)aState.lineLength);
-
- // Find the last frame in the line
- // XXX keep this as running state in the nsBlockReflowState
- nsIFrame* lastFrame = aState.lineStart;
- PRInt32 lineLen = aState.lineLength - 1;
- while (--lineLen >= 0) {
- lastFrame->GetNextSibling(lastFrame);
- }
-
- // Update maxElementSize
- if (nsnull != aState.maxElementSize) {
- nsSize& lineMax = aState.lineMaxElementSize;
- nsSize* maxMax = aState.maxElementSize;
- if (lineMax.width > maxMax->width) {
- maxMax->width = lineMax.width;
- }
- if (lineMax.height > maxMax->height) {
- maxMax->height = lineMax.height;
- }
- aState.lineMaxElementSize.width = 0;
- aState.lineMaxElementSize.height = 0;
- }
-
- // Advance to the next line
- aState.AdvanceToNextLine(lastFrame, lineHeight);
-
- return PR_TRUE;
-}
-
-/**
- * Add an inline child to the current line. Advance various running
- * values after placement.
- */
-void nsBlockFrame::AddInlineChildToLine(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsReflowMetrics& aKidSize,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC)
-{
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
-
- nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID);
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID);
-
- if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) {
- aState.needRelativePos = PR_TRUE;
- }
-
- // Place and size the child
- // XXX add in left margin from kid
- nsRect r;
- r.y = aState.y;
- r.width = aKidSize.width;
- r.height = aKidSize.height;
- if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) {
- // Left to right positioning.
- r.x = aState.borderPadding.left + aState.x + ss->mMargin.left;
- aState.x += aKidSize.width + ss->mMargin.left + ss->mMargin.right;
- } else {
- // Right to left positioning
- // XXX what should we do when aState.x goes negative???
- r.x = aState.x - aState.borderPadding.right - ss->mMargin.right -
- aKidSize.width;
- aState.x -= aKidSize.width + ss->mMargin.right + ss->mMargin.left;
- }
- aKidFrame->SetRect(r);
- aState.AddAscent(aKidSize.ascent);
- aState.lineWidth += aKidSize.width;
- aState.lineLength++;
-
- // Update maximums for the line
- if (aKidSize.ascent > aState.maxAscent) {
- aState.maxAscent = aKidSize.ascent;
- }
- if (aKidSize.descent > aState.maxDescent) {
- aState.maxDescent = aKidSize.descent;
- }
- // Update running margin maximums
- if (aState.firstChildIsInsideBullet && (aKidFrame == mFirstChild)) {
- // XXX temporary code. Since the molecule for the bullet frame
- // is the same as the LI frame, we get bad style information.
- // ignore it.
- } else {
- nscoord margin;
#if 0
- // XXX CSS2 spec says that top/bottom margin don't affect line height
- // calculation. We're waiting for clarification on this issue...
- if ((margin = ss->mMargin.top) < 0) {
- margin = -margin;
- if (margin > aState.maxNegTopMargin) {
- aState.maxNegTopMargin = margin;
- }
- } else {
- if (margin > aState.maxPosTopMargin) {
- aState.maxPosTopMargin = margin;
- }
- }
+ // XXX For now we assume that there are no height restrictions
+ // (e.g. no "float to bottom of column/page")
+ nsRect& availSpace = aState.mCurrentBand.availSpace;
+ aResult.x = availSpace.x;
+ aResult.y = availSpace.y;
+ aResult.width = availSpace.width;
+ aResult.height = aState.mAvailSize.height;
+#else
+ aState.mCurrentBand.availSpace.MoveBy(aState.mBorderPadding.left,
+ aState.mY);
#endif
- if ((margin = ss->mMargin.bottom) < 0) {
- margin = -margin;
- if (margin > aState.maxNegBottomMargin) {
- aState.maxNegBottomMargin = margin;
- }
- } else {
- if (margin > aState.maxPosBottomMargin) {
- aState.maxPosBottomMargin = margin;
- }
- }
- }
- // Update line max element size
- nsSize& mes = aState.lineMaxElementSize;
- if (nsnull != aKidMaxElementSize) {
- if (aKidMaxElementSize->width > mes.width) {
- mes.width = aKidMaxElementSize->width;
- }
- if (aKidMaxElementSize->height > mes.height) {
- mes.height = aKidMaxElementSize->height;
- }
- }
+ return rv;
}
-// Places and sizes the block-level element, and advances the line.
-// The rect is in the local coordinate space of the kid frame.
-void nsBlockFrame::AddBlockChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsRect& aKidRect,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC)
+// Give aLine and any successive lines to the block's next-in-flow; if
+// we don't have a next-in-flow then push all the children onto our
+// overflow list.
+nsresult
+nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineData* aLine)
{
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
+ PRInt32 i;
- nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID);
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID);
-
- if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) {
- aState.needRelativePos = PR_TRUE;
+ // Split our child-list in two; revert our last content offset and
+ // completion status to the previous line.
+ nsLineData* prevLine = aLine->mPrevLine;
+ NS_PRECONDITION(nsnull != prevLine, "pushing first line");
+ nsIFrame* prevKidFrame = prevLine->mFirstChild;
+ for (i = prevLine->mChildCount - 1; --i >= 0; ) {
+ prevKidFrame->GetNextSibling(prevKidFrame);
}
-
- // Translate from the kid's coordinate space to our coordinate space
- aKidRect.x += aState.borderPadding.left + ss->mMargin.left;
- aKidRect.y += aState.y + aState.topMargin;
-
- // Place and size the child
- aKidFrame->SetRect(aKidRect);
-
- aState.AddAscent(aKidRect.height);
- aState.lineLength++;
-
- // Is this the widest child frame?
- nscoord xMost = aKidRect.XMost() + ss->mMargin.right;
- if (xMost > aState.kidXMost) {
- aState.kidXMost = xMost;
- }
-
- // Update the max element size
- if (nsnull != aKidMaxElementSize) {
- if (aKidMaxElementSize->width > aState.maxElementSize->width) {
- aState.maxElementSize->width = aKidMaxElementSize->width;
- }
- if (aKidMaxElementSize->height > aState.maxElementSize->height) {
- aState.maxElementSize->height = aKidMaxElementSize->height;
- }
- }
-
- // and the bottom line margin information which we'll use when placing
- // the next child
- if (ss->mMargin.bottom < 0) {
- aState.maxNegBottomMargin = -ss->mMargin.bottom;
- } else {
- aState.maxPosBottomMargin = ss->mMargin.bottom;
- }
-
- // Update the running y-offset
- aState.y += aKidRect.height + aState.topMargin;
-
- // Apply relative positioning if necessary
- nsCSSLayout::RelativePositionChildren(aCX, this, aKidFrame, 1);
-
- // Advance to the next line
- aState.AdvanceToNextLine(aKidFrame, aKidRect.height);
-}
-
-/**
- * Compute the available size for reflowing the given child at the
- * current x,y position in the state. Note that this may return
- * negative or zero width/height's if we are out of room.
- */
-void nsBlockFrame::GetAvailSize(nsSize& aResult,
- nsBlockReflowState& aState,
- nsIStyleContext* aKidSC,
- PRBool aIsInline)
-{
- // Determine the maximum available reflow height for the child
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- aResult.height = aState.unconstrainedHeight ? NS_UNCONSTRAINEDSIZE :
- yb - aState.y - aState.topMargin;
-
- // Determine the maximum available reflow width for the child
- if (aState.unconstrainedWidth) {
- aResult.width = NS_UNCONSTRAINEDSIZE;
- } else if (aIsInline) {
- if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) {
- aResult.width = aState.currentBand->availSpace.XMost() - aState.x;
- } else {
- aResult.width = aState.x - aState.currentBand->availSpace.x;
- }
- } else {
- // It's a block. Don't adjust for the left/right margin here. That happens
- // later on once we know the current left/right edge
- aResult.width = aState.availSize.width;
- }
-}
-
-/**
- * Push all of the kids that we have not reflowed, starting at
- * aState.lineStart. aPrevKid is the kid previous to aState.lineStart
- * and is also our last child. Note that line length is NOT a
- * reflection of the number of children we are actually pushing
- * (because we don't break the sibling list as we add children to the
- * line).
- */
-void nsBlockFrame::PushKids(nsBlockReflowState& aState)
-{
- nsIFrame* prevFrame = aState.prevLineLastFrame;
- NS_PRECONDITION(nsnull != prevFrame, "pushing all kids");
#ifdef NS_DEBUG
- nsIFrame* nextSibling;
-
- prevFrame->GetNextSibling(nextSibling);
- NS_PRECONDITION(nextSibling == aState.lineStart, "bad prev line");
+ nsIFrame* nextFrame;
+ prevKidFrame->GetNextSibling(nextFrame);
+ NS_ASSERTION(nextFrame == aLine->mFirstChild, "bad line list");
#endif
+ prevKidFrame->SetNextSibling(nsnull);
+ prevLine->mNextLine = nsnull;
+ mLastContentOffset = prevLine->mLastContentOffset;
+ mLastContentIsComplete = prevLine->mLastContentIsComplete;
+
+ // Push children to our next-in-flow if we have, or to our overflow list
+ nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
+ PRInt32 pushCount = 0;
+ if (nsnull == nextInFlow) {
+ // Place children on the overflow list
+ mOverflowList = aLine->mFirstChild;
+
+ // Destroy the lines
+ nsLineData* line = aLine;
+ while (nsnull != line) {
+ pushCount += line->mChildCount;
+ nsLineData* next = line->mNextLine;
+ delete line;
+ line = next;
+ }
+ }
+ else {
+ aLine->mPrevLine = nsnull;
+
+ // Pass on the children to our next-in-flow
+ nsLineData* line = aLine;
+ prevLine = line;
+ nsIFrame* lastKid = aLine->mFirstChild;
+ nsIFrame* kid = lastKid;
+ while (nsnull != line) {
+ i = line->mChildCount;
+ pushCount += i;
+ NS_ASSERTION(kid == line->mFirstChild, "bad line list");
+ while (--i >= 0) {
+ kid->SetGeometricParent(nextInFlow);
+ nsIFrame* contentParent;
+ kid->GetContentParent(contentParent);
+ if (this == contentParent) {
+ kid->SetContentParent(nextInFlow);
+ }
+ lastKid = kid;
+ kid->GetNextSibling(kid);
+ }
+ prevLine = line;
+ line = line->mNextLine;
+ }
+
+ // Join the two line lists
+ nsLineData* nextInFlowLine = nextInFlow->mLines;
+ if (nsnull != nextInFlowLine) {
+ lastKid->SetNextSibling(nextInFlowLine->mFirstChild);
+ nextInFlowLine->mPrevLine = prevLine;
+ }
+ nsIFrame* firstKid = aLine->mFirstChild;
+ prevLine->mNextLine = nextInFlowLine;
+ nextInFlow->mLines = aLine;
+ nextInFlow->mFirstChild = firstKid;
+ nextInFlow->mChildCount += pushCount;
+ firstKid->GetIndexInParent(nextInFlow->mFirstContentOffset);
#ifdef NS_DEBUG
- PRInt32 numKids = LengthOf(mFirstChild);
- NS_ASSERTION(numKids == mChildCount, "bad child count");
+ nextInFlow->VerifyLines(PR_FALSE);
+#endif
+ }
+ mChildCount -= pushCount;
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return NS_LINE_LAYOUT_NOT_COMPLETE;
+}
+
+nsresult
+nsBlockFrame::ReflowMapped(nsBlockReflowState& aState)
+{
+ nsresult rv = NS_OK;
+
+ // Get some space to start reflowing with
+ GetAvailableSpace(aState, aState.mY);
+
+ nsLineData* prevLine = nsnull;
+ nsLineData* line = mLines;
+ nsLineLayout lineLayout(aState);
+ aState.mCurrentLine = &lineLayout;
+ while (nsnull != line) {
+ // Initialize the line layout for this line
+ rv = lineLayout.Initialize(aState, line);
+ if (NS_OK != rv) {
+ goto done;
+ }
+ lineLayout.mPrevKidFrame = aState.mPrevKidFrame;
+
+ // Reflow the line
+ nsresult lineReflowStatus = lineLayout.ReflowLine();
+ if (lineReflowStatus < 0) {
+ // Some kind of hard error
+ rv = lineReflowStatus;
+ goto done;
+ }
+ mChildCount += lineLayout.mNewFrames;
+
+ // Now place it. It's possible it won't fit.
+ rv = PlaceLine(aState, lineLayout, line);
+ if (NS_LINE_LAYOUT_COMPLETE != rv) {
+ goto done;
+ }
+
+ mLastContentOffset = line->mLastContentOffset;
+ mLastContentIsComplete = PRBool(line->mLastContentIsComplete);
+ prevLine = line;
+ line = line->mNextLine;
+ aState.mPrevKidFrame = lineLayout.mPrevKidFrame;
+ }
+
+done:
+ aState.mCurrentLine = nsnull;
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return rv;
+}
+
+nsresult
+nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState)
+{
+ nsresult rv = NS_OK;
+
+ // If we have no children and we have a prev-in-flow then we need to
+ // pick up where it left off. If we have children, e.g. we're being
+ // resized, then our content offset will have already been set
+ // correctly.
+ nsIFrame* kidPrevInFlow = nsnull;
+ if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
+ nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow;
+ mFirstContentOffset = prev->NextChildOffset();// XXX Is this necessary?
+ if (PR_FALSE == prev->mLastContentIsComplete) {
+ // Our prev-in-flow's last child is not complete
+ prev->LastChild(kidPrevInFlow);
+ }
+ }
+
+ // Get to the last line where the new content may be added
+ nsLineData* line = nsnull;
+ nsLineData* prevLine = nsnull;
+ if (nsnull != mLines) {
+ line = mLines;
+ while (nsnull != line->mNextLine) {
+ line = line->mNextLine;
+ }
+ prevLine = line;
+
+ // If the last line is not complete then kidPrevInFlow should be
+ // set to the last-line's last child.
+ if (!prevLine->mLastContentIsComplete) {
+ kidPrevInFlow = prevLine->GetLastChild();
+ }
+ }
+
+ // Get some space to start reflowing with
+ GetAvailableSpace(aState, aState.mY);
+
+ // Now reflow the new content until we are out of new content or out
+ // of vertical space.
+ PRInt32 kidIndex = NextChildOffset();
+ nsLineLayout lineLayout(aState);
+ aState.mCurrentLine = &lineLayout;
+ lineLayout.mKidPrevInFlow = kidPrevInFlow;
+ PRInt32 contentChildCount = mContent->ChildCount();
+ while (kidIndex < contentChildCount) {
+ if (nsnull == line) {
+ if (!MoreToReflow(aState)) {
+ break;
+ }
+ line = new nsLineData();
+ if (nsnull == line) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+ line->mFirstContentOffset = kidIndex;
+ }
+
+ // Initialize the line layout for this line
+ rv = lineLayout.Initialize(aState, line);
+ if (NS_OK != rv) {
+ goto done;
+ }
+ lineLayout.mKidPrevInFlow = kidPrevInFlow;
+ lineLayout.mPrevKidFrame = aState.mPrevKidFrame;
+
+ // Reflow the line
+ nsresult lineReflowStatus = lineLayout.ReflowLine();
+ if (lineReflowStatus < 0) {
+ // Some kind of hard error
+ rv = lineReflowStatus;
+ goto done;
+ }
+ mChildCount += lineLayout.mNewFrames;
+
+ // Add line to the block; do this before placing the line in case
+ // PushLines is needed.
+ if (nsnull == prevLine) {
+ // For the first line, initialize mFirstContentOffset
+ mFirstContentOffset = line->mFirstContentOffset;
+ mFirstChild = line->mFirstChild;
+ mLines = line;
+ }
+ else {
+ prevLine->mNextLine = line;
+ line->mPrevLine = prevLine;
+ }
+
+ // Now place it. It's possible it won't fit.
+ rv = PlaceLine(aState, lineLayout, line);
+ if (NS_LINE_LAYOUT_COMPLETE != rv) {
+ goto done;
+ }
+ kidIndex = lineLayout.mKidIndex;
+ kidPrevInFlow = lineLayout.mKidPrevInFlow;
+
+ mLastContentOffset = line->mLastContentOffset;
+ mLastContentIsComplete = PRBool(line->mLastContentIsComplete);
+ prevLine = line;
+ line = line->mNextLine;
+ aState.mPrevKidFrame = lineLayout.mPrevKidFrame;
+ }
+
+done:
+ aState.mCurrentLine = nsnull;
+ if (aState.mBlockIsPseudo) {
+ PropagateContentOffsets();
+ }
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return rv;
+}
+
+nsresult
+nsBlockFrame::InitializeState(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockReflowState& aState)
+{
+ nsresult rv;
+ rv = aState.Initialize(aPresContext, aSpaceManager,
+ aMaxSize, aMaxElementSize, this);
+
+ nsStyleSpacing* mySpacing = (nsStyleSpacing*)
+ mStyleContext->GetData(kStyleSpacingSID);
+
+ // Apply border and padding adjustments for regular frames only
+ if (!aState.mBlockIsPseudo) {
+ aState.mY = mySpacing->mBorderPadding.top;
+ aState.mX = mySpacing->mBorderPadding.left;
+ aState.mAvailSize.width -=
+ (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right);
+ aState.mAvailSize.height -=
+ (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom);
+ aState.mBorderPadding = mySpacing->mBorderPadding;
+ }
+ else {
+ aState.mBorderPadding.SizeTo(0, 0, 0, 0);
+ }
+
+ // Setup initial list ordinal value
+ nsIAtom* tag = mContent->GetTag();
+ if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
+ (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
+ nsHTMLValue value;
+ if (eContentAttr_HasValue ==
+ ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) {
+ if (eHTMLUnit_Integer == value.GetUnit()) {
+ aState.mNextListOrdinal = value.GetIntValue();
+ }
+ }
+ }
+ NS_RELEASE(tag);
+
+ return rv;
+}
+
+#if XXX
+NS_METHOD
+nsBlockFrame::SizeTo(nscoord aWidth, nscoord aHeight)
+{
+ printf("size to %g,%g\n", NS_TWIPS_TO_POINTS_FLOAT(aWidth),
+ NS_TWIPS_TO_POINTS_FLOAT(aHeight));
+ return nsHTMLContainerFrame::SizeTo(aWidth, aHeight);
+}
+
+NS_METHOD
+nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ aStatus = frComplete;
+ nsBlockReflowState state;
+ rv = InitializeState(aPresContext, aMaxSize, aMaxElementSize, state);
+ if (NS_OK == rv) {
+ nsRect desiredRect;
+ rv = DoResizeReflow(state, desiredRect, aStatus);
+ aDesiredSize.width = desiredRect.width;
+ aDesiredSize.height = desiredRect.height;
+ aDesiredSize.ascent = aDesiredSize.height;
+ aDesiredSize.descent = 0;
+ }
+ return rv;
+}
#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": push kids (childCount=%d)\n", mChildCount);
- DumpFlow();
-#endif
-
- PushChildren(aState.lineStart, prevFrame, mLastContentIsComplete);
- SetLastContentOffset(prevFrame);
-
- // Set mLastContentIsComplete to the previous lines last content is
- // complete now that the previous line's last child is our last
- // child.
- mLastContentIsComplete = aState.prevLineLastContentIsComplete;
-
- // Fix up child count
- // XXX is there a better way? aState.lineLength doesn't work because
- // we might be pushing more than just the pending line.
- nsIFrame* kid = mFirstChild;
- PRInt32 kids = 0;
- while (nsnull != kid) {
- kids++;
- kid->GetNextSibling(kid);
- }
- mChildCount = kids;
-
- // Make sure we have no lingering line data
- aState.lineLength = 0;
- aState.lineStart = nsnull;
-
-#ifdef NOISY
- ListTag(stdout);
- printf(": push kids done (childCount=%d) [%c]\n", mChildCount,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-}
-
-/**
- * Gets a band of available space starting at the specified y-offset. Assumes
- * the local coordinate space is currently set to the upper-left origin of the
- * bounding rect
- *
- * Updates "currentBand" and "x" member data of the block reflow state
- */
-void nsBlockFrame::GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY)
-{
- // Gets a band of available space.
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
- aState.spaceManager->GetBandData(aY, aState.availSize, *aState.currentBand);
-
- // Compute the bounding rect of the available space, i.e. space between any
- // left and right floaters
- aState.currentBand->ComputeAvailSpaceRect();
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
- aState.x = aState.currentBand->availSpace.x;
-}
-
-void nsBlockFrame::ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear)
-{
- // Translate the coordinate space
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
-
-getBand:
- nscoord y = aState.y + aState.topMargin;
- PRBool isLeftFloater = PR_FALSE;
- PRBool isRightFloater = PR_FALSE;
-
- // Get a band of available space
- aState.spaceManager->GetBandData(y, aState.availSize, *aState.currentBand);
-
- // Scan the trapezoids looking for left and right floaters
- nsBandTrapezoid* trapezoid = aState.currentBand->trapezoids;
- for (PRInt32 i = 0; i < aState.currentBand->count; i++) {
- // XXX Handle multiply occupied
- if (nsBandTrapezoid::smOccupied == trapezoid->state) {
- nsStyleDisplay* display;
-
- trapezoid->frame->GetStyleData(kStylePositionSID, (nsStyleStruct*&)display);
- if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
- isLeftFloater = PR_TRUE;
- } else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) {
- isRightFloater = PR_TRUE;
- }
- }
-
- trapezoid++;
- }
-
- if (isLeftFloater) {
- if ((aClear == NS_STYLE_CLEAR_LEFT) ||
- (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
- aState.y += aState.currentBand->trapezoids[0].GetHeight();
- goto getBand;
- }
- }
- if (isRightFloater) {
- if ((aClear == NS_STYLE_CLEAR_RIGHT) ||
- (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
- aState.y += aState.currentBand->trapezoids[0].GetHeight();
- goto getBand;
- }
- }
-
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
-}
-
-// Bit's for PlaceAndReflowChild return value
-#define PLACE_FIT 0x1
-#define PLACE_FLOWED 0x2
-
-PRIntn
-nsBlockFrame::PlaceAndReflowChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC)
-{
- nsSize kidMaxElementSize;
- nsSize* pKidMaxElementSize =
- (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull;
-
- // Get line start setup if we are at the start of a new line
- if (nsnull == aState.lineStart) {
- NS_ASSERTION(0 == aState.lineLength, "bad line length");
- aState.lineStart = aKidFrame;
- }
-
- // Get kid and its style
- nsStyleDisplay* styleDisplay = (nsStyleDisplay*)
- aKidSC->GetData(kStyleDisplaySID);
-
- // Figure out if kid is a block element or not
- PRBool isInline = PR_TRUE;
- PRIntn display = styleDisplay->mDisplay;
- if (aState.firstChildIsInsideBullet && (mFirstChild == aKidFrame)) {
- // XXX Special hack for properly reflowing bullets that have the
- // inside value for list-style-position.
- display = NS_STYLE_DISPLAY_INLINE;
- }
- if ((NS_STYLE_DISPLAY_BLOCK == display) ||
- (NS_STYLE_DISPLAY_LIST_ITEM == display)) {
- // Block elements always end up on the next line (unless they are
- // already at the start of the line).
- isInline = PR_FALSE;
- if (aState.lineLength > 0) {
- aState.breakAfterChild = PR_TRUE;
- }
- }
-
- // Handle forced break first
- if (aState.breakAfterChild) {
- NS_ASSERTION(aState.lineStart != aKidFrame, "bad line");
-
- // Get the last child in the current line
- nsIFrame* lastFrame = aState.lineStart;
- PRInt32 lineLen = aState.lineLength - 1;
- while (--lineLen >= 0) {
- lastFrame->GetNextSibling(lastFrame);
- }
-
- if (!AdvanceToNextLine(aCX, aState)) {
- // The previous line didn't fit.
- return 0;
- }
- aState.lineStart = aKidFrame;
-
- // Get the style for the last child, and see if it wanted to clear
- // floaters. This handles the BR tag, which is the only inline
- // element for which clear applies
- nsIStyleContextPtr lastChildSC;
-
- lastFrame->GetStyleContext(aCX, lastChildSC.AssignRef());
- nsStyleDisplay* lastChildDisplay = (nsStyleDisplay*)
- lastChildSC->GetData(kStyleDisplaySID);
- switch (lastChildDisplay->mBreakType) {
- case NS_STYLE_CLEAR_LEFT:
- case NS_STYLE_CLEAR_RIGHT:
- case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
- ClearFloaters(aState, lastChildDisplay->mBreakType);
- break;
- }
- }
-
- // Now that we've handled force breaks (and maybe called AdvanceToNextLine()
- // which checks), remember whether it's an inline frame
- aState.isInline = isInline;
-
- // If we're at the beginning of a line then compute the top margin that we
- // should use
- if (aState.lineStart == aKidFrame) {
- // Compute the top margin to use for this line
- aState.topMargin = GetTopMarginFor(aCX, aState, aKidFrame, aKidSC,
- aState.isInline);
-
- // If it's an inline element then get a band of available space
- //
- // XXX If we have a current band and there's unused space in that band
- // then avoid this call to get a band...
- if (aState.isInline) {
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
- }
- }
-
- // Compute the available space to reflow the child into and then
- // reflow it into that space.
- nsSize kidAvailSize;
- GetAvailSize(kidAvailSize, aState, aKidSC, aState.isInline);
- if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) {
- // No more room
- return 0;
- }
-
- ReflowStatus status;
-
- if (aState.isInline) {
- nsReflowMetrics kidSize;
-
- // Inline elements are never passed the space manager
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
-
- // For first children, we skip all the fit checks because we must
- // fit at least one child for a parent to figure what to do with us.
- if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) {
- NS_ASSERTION(nsnull != aState.lineStart, "bad line start");
-
- if (aKidFrame == aState.lineStart) {
- // Width always fits when we are at the logical left margin.
- // Just check the height.
- //
- // XXX This height check isn't correct now that we have bands of
- // available space...
- if (kidSize.height > kidAvailSize.height) {
- // It's too tall
- return PLACE_FLOWED;
- }
- } else {
- // Examine state and if the breakBeforeChild is set and we
- // aren't already on the new line, do the forcing now.
- // XXX Why aren't we doing this check BEFORE we resize reflow the child?
- if (aState.breakBeforeChild) {
- aState.breakBeforeChild = PR_FALSE;
- if (aKidFrame != aState.lineStart) {
- if (!AdvanceToNextLine(aCX, aState)) {
- // Flushing out the line failed.
- return PLACE_FLOWED;
- }
- aState.lineStart = aKidFrame;
-
- // Get a band of available space
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
-
- // Reflow child now that it has the line to itself
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE);
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
- }
- }
-
- // When we are not at the logical left margin then we need
- // to check the width first. If we are too wide then advance
- // to the next line and try reflowing again.
- if (kidSize.width > kidAvailSize.width) {
- // Too wide. Try next line
- if (!AdvanceToNextLine(aCX, aState)) {
- // Flushing out the line failed.
- return PLACE_FLOWED;
- }
- aState.lineStart = aKidFrame;
-
- // Get a band of available space
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
-
- // Reflow splittable children
- SplittableType isSplittable;
-
- aKidFrame->IsSplittable(isSplittable);
- if (isSplittable != frNotSplittable) {
- // Update size info now that we are on the next line. Then
- // reflow the child into the new available space.
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE);
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
-
- // If we just reflowed our last child then update the
- // mLastContentIsComplete state.
- nsIFrame* nextSibling;
-
- aKidFrame->GetNextSibling(nextSibling);
- if (nsnull == nextSibling) {
- // Use state from the reflow we just did
- mLastContentIsComplete = PRBool(status == frComplete);
- }
- }
-
- // XXX This height check isn't correct now that we have bands of
- // available space...
- if (kidSize.height > kidAvailSize.height) {
- // It's too tall on the next line
- return PLACE_FLOWED;
- }
- // It's ok if it's too wide on the next line.
- }
- }
- }
-
- // Add child to the line
- AddInlineChildToLine(aCX, aState, aKidFrame, kidSize,
- pKidMaxElementSize, aKidSC);
- } else {
- nsRect kidRect;
-
- // Does the block-level element want to clear any floaters that impact
- // it? Note that the clear property only applies to block-level elements
- // and the BR tag
- nsStyleDisplay* styleDisplay = (nsStyleDisplay*)
- aKidSC->GetData(kStyleDisplaySID);
- switch (styleDisplay->mBreakType) {
- case NS_STYLE_CLEAR_LEFT:
- case NS_STYLE_CLEAR_RIGHT:
- case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
- ClearFloaters(aState, styleDisplay->mBreakType);
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_FALSE);
- if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) {
- // No more room
- return 0;
- }
- break;
- }
-
- // Give the block its own local coordinate space.. Note: ReflowChild()
- // will adjust for the child's left/right margin after determining the
- // current left/right edge
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
- // Give the block-level element the opportunity to use the space manager
- status = ReflowChild(aKidFrame, aCX, aState.spaceManager,
- kidAvailSize, kidRect, pKidMaxElementSize);
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
-
- // For first children, we skip all the fit checks because we must
- // fit at least one child for a parent to figure what to do with us.
- if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) {
- // Block elements always fit horizontally (because they are
- // always placed at the logical left margin). Check to see if
- // the block fits vertically
- if (kidRect.YMost() > kidAvailSize.height) {
- // Nope
- return PLACE_FLOWED;
- }
- }
-
- // Add block child
- // XXX We need to set lastContentIsComplete here, because AddBlockChild()
- // calls AdvaneceToNextLine(). We need to restructure the flow of control,
- // and use a state machine...
- aState.lastContentIsComplete = PRBool(status == frComplete);
- AddBlockChild(aCX, aState, aKidFrame, kidRect, pKidMaxElementSize, aKidSC);
- }
-
- // If we just reflowed our last child then update the
- // mLastContentIsComplete state.
- nsIFrame* nextSibling;
-
- aKidFrame->GetNextSibling(nextSibling);
- if (nsnull == nextSibling) {
- // Use state from the reflow we just did
- mLastContentIsComplete = PRBool(status == frComplete);
- }
-
- aState.lastContentIsComplete = PRBool(status == frComplete);
- if (aState.isInline && (frNotComplete == status)) {
- // Since the inline child didn't complete its reflow we *know*
- // that a continuation of it can't possibly fit on the current
- // line. Therefore, set a flag in the state that will cause the
- // a line break before the next frame is placed.
- aState.breakAfterChild = PR_TRUE;
- }
-
- aState.reflowStatus = status;
- return PLACE_FLOWED | PLACE_FIT;
-}
-
-/**
- * Reflow the existing frames.
- *
- * @param aCX presentation context to use
- * @param aState in out parameter which tracks the state of
- * reflow for the block frame.
- * @return true if we successfully reflowed all the mapped children and false
- * otherwise, e.g. we pushed children to the next in flow
- */
PRBool
-nsBlockFrame::ReflowMappedChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n",
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-
- PRBool result = PR_TRUE;
- nsIFrame* kidFrame;
-
- for (kidFrame = mFirstChild; nsnull != kidFrame; ) {
-
- /* we get the kid's style from kidFrame's cached style context */
- nsIStyleContextPtr kidSC;
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
-
- // Attempt to place and reflow the child
-
- // XXX if child is not splittable and it fits just place it where
- // it is, otherwise advance to the next line and place it there if
- // possible
-
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- ReflowStatus status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // The child doesn't fit. Push it and any remaining children.
- PushKids(aState);
- result = PR_FALSE;
- goto push_done;
- }
-
- // Is the child complete?
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- if (frComplete == status) {
- // Yes, the child is complete
- NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
- } else {
- // No the child isn't complete
- if (nsnull == kidNextInFlow) {
- // The child doesn't have a next-in-flow so create a continuing
- // frame. This hooks the child into the flow
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
- NS_ASSERTION(nsnull != continuingFrame, "frame creation failed");
-
- // Add the continuing frame to the sibling list
- nsIFrame* nextSib;
-
- kidFrame->GetNextSibling(nextSib);
- continuingFrame->SetNextSibling(nextSib);
- kidFrame->SetNextSibling(continuingFrame);
- mChildCount++;
- }
-
- // Unlike the inline frame code we can't assume that we used
- // up all of our space because the child's reflow status is
- // frNotComplete. Instead, the child is probably split and
- // we need to reflow the continuations as well.
- }
-
- // Get the next child frame
- kidFrame->GetNextSibling(kidFrame);
- }
-
- push_done:
-#ifdef NS_DEBUG
- nsIFrame* lastChild;
- PRInt32 lastIndexInParent;
-
- LastChild(lastChild);
- lastChild->GetIndexInParent(lastIndexInParent);
- NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset");
-
- PRInt32 len = LengthOf(mFirstChild);
- NS_POSTCONDITION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
-
-#ifdef NOISY
- ListTag(stdout);
- printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n",
- (result ? "" : "NOT "),
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
- return result;
-}
-
-/* XXX:
- * In this method, we seem to be getting the style for the next child,
- * and checking that child's style for the child's display type.
- * The problem is that it's very inefficient to ResolveStyleFor(kid) here
- * and then just throw it away, when we end up doing a ResolveStyleFor(kid)
- * again when we actually create a frame for the content.
- * It would be nice to cache the resolved style, maybe in the reflow state?
- */
-PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX)
+nsBlockFrame::MoreToReflow(nsBlockReflowState& aState)
{
PRBool rv = PR_FALSE;
- if (IsPseudoFrame()) {
+ if (aState.mBlockIsPseudo) {
// Get the next content object that we would like to reflow
PRInt32 kidIndex = NextChildOffset();
nsIContentPtr kid = mContent->ChildAt(kidIndex);
if (kid.IsNotNull()) {
// Resolve style for the kid
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
+ nsIStyleContextPtr kidSC =
+ aState.mPresContext->ResolveStyleContextFor(kid, this);
nsStyleDisplay* kidStyleDisplay = (nsStyleDisplay*)
kidSC->GetData(kStyleDisplaySID);
switch (kidStyleDisplay->mDisplay) {
@@ -1305,640 +993,288 @@ PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX)
return rv;
}
-/**
- * Create new frames for content we haven't yet mapped
- *
- * @param aCX presentation context to use
- * @return frComplete if all content has been mapped and frNotComplete
- * if we should be continued
- */
-nsIFrame::ReflowStatus
-nsBlockFrame::ReflowAppendedChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
+nsBlockReflowState*
+nsBlockFrame::FindBlockReflowState(nsIPresContext* aPresContext,
+ nsIFrame* aFrame)
{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
- nsIFrame* kidPrevInFlow = nsnull;
- ReflowStatus result = frNotComplete;
-
- // If we have no children and we have a prev-in-flow then we need to pick
- // up where it left off. If we have children, e.g. we're being resized, then
- // our content offset should already be set correctly...
- if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
- nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow;
- NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow");
- mFirstContentOffset = prev->NextChildOffset();
- if (PR_FALSE == prev->mLastContentIsComplete) {
- // Our prev-in-flow's last child is not complete
- prev->LastChild(kidPrevInFlow);
- }
- }
-
- // Place our children, one at a time until we are out of children
- PRInt32 kidIndex = NextChildOffset();
- nsIFrame* kidFrame = nsnull;
- nsIFrame* prevKidFrame;
-
- LastChild(prevKidFrame);
- for (;;) {
- // Get the next content object
- nsIContentPtr kid = mContent->ChildAt(kidIndex);
- if (kid.IsNull()) {
- result = frComplete;
- break;
- }
-
- // Resolve style for the kid
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
- nsStylePosition* kidPosition = (nsStylePosition*)
- kidSC->GetData(kStylePositionSID);
- nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
- kidSC->GetData(kStyleDisplaySID);
-
- // Check whether it wants to floated or absolutely positioned
- if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) {
- AbsoluteFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- kidFrame->SetStyleContext(aCX,kidSC);
- } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
- PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- kidFrame->SetStyleContext(aCX,kidSC);
- } else if (nsnull == kidPrevInFlow) {
- // Create initial frame for the child
- nsIContentDelegate* kidDel;
- switch (kidDisplay->mDisplay) {
- case NS_STYLE_DISPLAY_BLOCK:
- case NS_STYLE_DISPLAY_LIST_ITEM:
- // Pseudo block frames do not contain other block elements
- // unless the block element would be the first child.
- if (IsPseudoFrame()) {
- // If we're being used as a pseudo frame, i.e. we map the same
- // content as our parent then we want to indicate we're complete;
- // otherwise we'll be continued and go on mapping children...
-
- // It better be true that we are not being asked to flow a
- // block element as our first child. That means the body
- // decided it needed a pseudo-frame when it shouldn't have.
- NS_ASSERTION(nsnull != mFirstChild, "bad body");
-
- result = frComplete;
- goto done;
- }
- // FALL THROUGH (and create frame)
-
- case NS_STYLE_DISPLAY_INLINE:
- kidDel = kid->GetDelegate(aCX);
- kidFrame = kidDel->CreateFrame(aCX, kid, kidIndex, this);
- NS_RELEASE(kidDel);
- break;
-
- default:
- NS_ASSERTION(nsnull == kidPrevInFlow, "bad prev in flow");
- nsFrame::NewFrame(&kidFrame, kid, kidIndex, this);
+ nsBlockReflowState* state = nsnull;
+ if (nsnull != aFrame) {
+ nsIFrame* parent;
+ aFrame->GetGeometricParent(parent);
+ while (nsnull != parent) {
+ nsBlockFrame* block;
+ nsresult rv = parent->QueryInterface(kBlockFrameCID, (void**) &block);
+ if (NS_OK == rv) {
+ nsIPresShell* shell = aPresContext->GetShell();
+ state = (nsBlockReflowState*) shell->GetCachedData(block);
+ NS_RELEASE(shell);
break;
}
- kidFrame->SetStyleContext(aCX,kidSC);
- } else {
- // Since kid has a prev-in-flow, use that to create the next
- // frame.
- kidPrevInFlow->CreateContinuingFrame(aCX, this, kidFrame);
+ parent->GetGeometricParent(parent);
}
-
- // Link child frame into the list of children. If the frame ends
- // up not fitting and getting pushed, the PushKids code will fixup
- // the child count for us.
- if (nsnull != prevKidFrame) {
-#ifdef NS_DEBUG
- nsIFrame* nextSibling;
-
- prevKidFrame->GetNextSibling(nextSibling);
- NS_ASSERTION(nsnull == nextSibling, "bad append");
-#endif
- prevKidFrame->SetNextSibling(kidFrame);
- } else {
- NS_ASSERTION(nsnull == mFirstChild, "bad create");
- mFirstChild = kidFrame;
- SetFirstContentOffset(kidFrame);
- }
- prevKidFrame = kidFrame;
- mChildCount++;
-
- // Reflow child frame as many times as necessary until it is
- // complete.
- ReflowStatus status;
- do {
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // We ran out of room.
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- mLastContentIsComplete = PRBool(nsnull == kidNextInFlow);
- PushKids(aState);
-
- goto push_done;
- }
-
- // Did the child complete?
- prevKidFrame = kidFrame;
- if (frNotComplete == status) {
- // Child didn't complete so create a continuing frame
- kidPrevInFlow = kidFrame;
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
-
- // Add the continuing frame to the sibling list
- nsIFrame* kidNextSibling;
-
- kidFrame->GetNextSibling(kidNextSibling);
- continuingFrame->SetNextSibling(kidNextSibling);
- kidFrame->SetNextSibling(continuingFrame);
- kidFrame = continuingFrame;
- mChildCount++;
-
- // Switch to new kid style
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
- }
-#ifdef NS_DEBUG
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- NS_ASSERTION(nsnull == kidNextInFlow, "huh?");
-#endif
- } while (frNotComplete == status);
-
- // The child that we just reflowed is complete
-#ifdef NS_DEBUG
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
-#endif
- kidIndex++;
- kidPrevInFlow = nsnull;
}
-
- done:
- // To get here we either completely reflowed all our appended
- // children OR we are a pseudo-frame and we ran into a block
- // element. In either case our last content MUST be complete.
- NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state");
- NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
- SetLastContentOffset(prevKidFrame);
-
- push_done:
-#ifdef NS_DEBUG
- PRInt32 len = LengthOf(mFirstChild);
- NS_ASSERTION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
- return result;
+ return state;
}
-/**
- * Pullup frames from our next in flow and try to place them. Before
- * this is called our previously mapped children, if any have been
- * reflowed which means that the block reflow state's x and y
- * coordinates and other data are ready to go.
- *
- * Return true if we pulled everything up.
- */
-PRBool
-nsBlockFrame::PullUpChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": pullup (childCount=%d) [%d,%d,%c]\n",
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-
- PRBool result = PR_TRUE;
- nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
- nsIFrame* prevKidFrame;
-
- LastChild(prevKidFrame);
- while (nsnull != nextInFlow) {
- // Get first available frame from the next-in-flow
- nsIFrame* kidFrame = PullUpOneChild(nextInFlow, prevKidFrame);
- if (nsnull == kidFrame) {
- // We've pulled up all the children from that next-in-flow, so
- // move to the next next-in-flow.
- nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
- continue;
- }
-
- // Get style information for the pulled up kid
- nsIContentPtr kid;
-
- kidFrame->GetContent(kid.AssignRef());
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
-
- ReflowStatus status;
- do {
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // Push the kids that didn't fit back down to the next-in-flow
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- mLastContentIsComplete = PRBool(nsnull == kidNextInFlow);
- PushKids(aState);
-
- result = PR_FALSE;
- goto push_done;
- }
-
- if (frNotComplete == status) {
- // Child is not complete
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- if (nsnull == kidNextInFlow) {
- // Create a continuing frame for the incomplete child
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
-
- // Add the continuing frame to our sibling list.
- nsIFrame* nextSibling;
-
- kidFrame->GetNextSibling(nextSibling);
- continuingFrame->SetNextSibling(nextSibling);
- kidFrame->SetNextSibling(continuingFrame);
- prevKidFrame = kidFrame;
- kidFrame = continuingFrame;
- mChildCount++;
-
- // Switch to new kid style
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
- } else {
- // The child has a next-in-flow, but it's not one of ours.
- // It *must* be in one of our next-in-flows. Collect it
- // then.
- NS_ASSERTION(!IsChild(kidNextInFlow), "busted kid next-in-flow");
- break;
- }
- }
- } while (frNotComplete == status);
-
- prevKidFrame = kidFrame;
- }
-
- if (nsnull != prevKidFrame) {
- // The only way we can get here is by pulling up every last child
- // in our next-in-flows (and reflowing any continunations they
- // have). Therefore we KNOW that our last child is complete.
- NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state");
- NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
- SetLastContentOffset(prevKidFrame);
- }
-
- push_done:;
-
- if (result == PR_FALSE) {
- // If our next-in-flow is empty OR our next next-in-flow is empty
- // then adjust the offsets of all of the empty next-in-flows.
- nextInFlow = (nsBlockFrame*) mNextInFlow;
- if ((0 == nextInFlow->mChildCount) ||
- ((nsnull != nextInFlow->mNextInFlow) &&
- (0 == ((nsBlockFrame*)(nextInFlow->mNextInFlow))->mChildCount))) {
- // We didn't pullup everything and we need to fixup one of our
- // next-in-flows content offsets.
- AdjustOffsetOfEmptyNextInFlows();
- }
- }
-
-
-#ifdef NS_DEBUG
- PRInt32 len = LengthOf(mFirstChild);
- NS_ASSERTION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n",
- (result ? "" : "NOT "),
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
- return result;
-}
-
-void nsBlockFrame::SetupState(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- const nsSize& aMaxSize,
- nsSize* aMaxElementSize,
- nsISpaceManager* aSpaceManager)
-{
- // Setup reflow state
- aState.Init(aMaxSize, aMaxElementSize, mStyleContext, aSpaceManager);
-
- nsStyleSpacing* mySpacing = (nsStyleSpacing*)
- mStyleContext->GetData(kStyleSpacingSID);
-
- // Apply border and padding adjustments for regular frames only
- if (PR_FALSE == IsPseudoFrame()) {
- aState.borderPadding = mySpacing->mBorderPadding;
- aState.y = mySpacing->mBorderPadding.top;
- aState.availSize.width -=
- (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right);
- aState.availSize.height -=
- (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom);
- } else {
- aState.borderPadding.SizeTo(0, 0, 0, 0);
- }
-
- // Setup initial list ordinal value
- nsIAtom* tag = mContent->GetTag();
- if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
- (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
- nsHTMLValue value;
- if (eContentAttr_HasValue ==
- ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) {
- if (eHTMLUnit_Integer == value.GetUnit()) {
- aState.nextListOrdinal = value.GetIntValue();
- }
- }
- }
- NS_RELEASE(tag);
-
- mCurrentState = &aState;
-}
-
-#include "nsUnitConversion.h"/* XXX */
-NS_METHOD nsBlockFrame::ResizeReflow(nsIPresContext* aCX,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsSize* aMaxElementSize,
- ReflowStatus& aStatus)
-{
- nsBlockReflowState state;
- SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager);
- return DoResizeReflow(aCX, state, aDesiredRect, aStatus);
-}
-
-nsresult nsBlockFrame::DoResizeReflow(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsRect& aDesiredRect,
- ReflowStatus& aStatus)
+nsresult
+nsBlockFrame::DoResizeReflow(nsBlockReflowState& aState,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ ReflowStatus& aStatus)
{
#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
PreReflowCheck();
#endif
-#ifdef NOISY
+#ifdef NOISY_REFLOW
ListTag(stdout);
- printf(": resize reflow %g,%g\n",
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width),
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height));
- DumpFlow();
+ printf(": Before:\n");
+ List(stdout, 1);
#endif
- // Zap old line data
- if (nsnull != mLines) {
- delete mLines;
- mLines = nsnull;
- }
- mNumLines = 0;
+ nsresult rv = NS_OK;
- // Check for an overflow list
- MoveOverflowToChildList();
-
- // Before we start reflowing, cache a pointer to our state structure
- // so that inline frames can find it.
- nsIPresShell* shell = aCX->GetShell();
+ nsIPresShell* shell = aState.mPresContext->GetShell();
shell->PutCachedData(this, &aState);
- // First reflow any existing frames
- PRBool reflowMappedOK = PR_TRUE;
- aStatus = frComplete;
- if (nsnull != mFirstChild) {
- reflowMappedOK = ReflowMappedChildren(aCX, aState);
- if (!reflowMappedOK) {
- aStatus = frNotComplete;
- }
+ // Check for an overflow list
+ DrainOverflowList();
+
+ if (nsnull != mLines) {
+ rv = ReflowMapped(aState);
}
- if (reflowMappedOK) {
- // Any space left?
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- if ((nsnull != mFirstChild) && (aState.y >= yb)) {
- // No space left. Don't try to pull-up children or reflow
- // unmapped. We need to return the correct completion status,
- // so see if there is more to reflow.
- if (MoreToReflow(aCX)) {
- aStatus = frNotComplete;
- }
- } else if (MoreToReflow(aCX)) {
- // Try and pull-up some children from a next-in-flow
- if ((nsnull == mNextInFlow) || PullUpChildren(aCX, aState)) {
- // If we still have unmapped children then create some new frames
- if (MoreToReflow(aCX)) {
- aStatus = ReflowAppendedChildren(aCX, aState);
- }
- } else {
- // We were unable to pull-up all the existing frames from the
- // next in flow
- aStatus = frNotComplete;
- }
+ if (NS_OK == rv) {
+ if ((nsnull != mLines) && (aState.mAvailSize.height <= 0)) {
+ // We are out of space
}
- }
-
- // Deal with last line - make sure it gets vertically and
- // horizontally aligned. This also updates the state's y coordinate
- // which is good because that's how we size ourselves.
- if (0 != aState.lineLength) {
- if (!AdvanceToNextLine(aCX, aState)) {
- // The last line of output doesn't fit. Push all of the kids to
- // the next in flow and change our reflow status to not complete
- // so that we are continued.
-#ifdef NOISY
- ListTag(stdout);
- printf(": pushing kids since last line doesn't fit\n");
-#endif
-
- PushKids(aState);
- aStatus = frNotComplete;
+ if (MoreToReflow(aState)) {
+ rv = ReflowUnmapped(aState);
}
}
- if (frComplete == aStatus) {
- // Don't forget to add in the bottom margin from our last child.
- // Only add it in if there is room for it.
- nscoord margin = aState.prevMaxPosBottomMargin -
- aState.prevMaxNegBottomMargin;
- nscoord y = aState.y + margin;
- if (y <= aState.borderPadding.top + aState.availSize.height) {
- aState.y = y;
- }
- }
-
- // Now that reflow has finished, remove the cached pointer
- shell->RemoveCachedData(this);
- NS_RELEASE(shell);
-
- // Translate state.lineLengths into an integer array
- mNumLines = aState.lineLengths.Count();
- if (mNumLines > 0) {
- mLines = new PRInt32[mNumLines];
- for (PRInt32 i = 0; i < mNumLines; i++) {
- PRInt32 ll = (PRInt32) aState.lineLengths.ElementAt(i);
- mLines[i] = ll;
- }
- }
-
- if (!aState.unconstrainedWidth && aState.justifying) {
- // Perform justification now that we know how many lines we have.
- JustifyLines(aCX, aState);
- }
-
// Return our desired rect and our status
aDesiredRect.x = 0;
aDesiredRect.y = 0;
- aDesiredRect.width = aState.kidXMost + aState.borderPadding.right;
- if (!aState.unconstrainedWidth) {
+ aDesiredRect.width = aState.mKidXMost + aState.mBorderPadding.right;
+ if (!aState.mUnconstrainedWidth) {
// Make sure we're at least as wide as the max size we were given
- nscoord maxWidth = aState.availSize.width + aState.borderPadding.left +
- aState.borderPadding.right;
-
+ nscoord maxWidth = aState.mAvailSize.width + aState.mBorderPadding.left +
+ aState.mBorderPadding.right;
if (aDesiredRect.width < maxWidth) {
aDesiredRect.width = maxWidth;
}
}
- aState.y += aState.borderPadding.bottom;
- aDesiredRect.height = aState.y;
-
-#ifdef NS_DEBUG
- PostReflowCheck(aStatus);
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": resize reflow %g,%g %scomplete [%d,%d,%c]\n",
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width),
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height),
- ((status == frNotComplete) ? "NOT " : ""),
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F')
- );
- DumpFlow();
-#endif
- mCurrentState = nsnull;
- return NS_OK;
-}
-
-void nsBlockFrame::JustifyLines(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
- // XXX we don't justify the last line; what if we are continued,
- // should we do it then?
- nsIFrame* kid = mFirstChild;
- for (PRInt32 i = 0; i < mNumLines; i++) {
- nsIFrame* lineStart = kid;
- PRInt32 lineLength = mLines[i];
- if (i < mNumLines - 1) {
- if (1 == lineLength) {
- // For lines with one element on them we can take a shortcut and
- // let them do the justification. Note that we still call
- // JustifyReflow even if the available space is zero in case the
- // child is a hunk of text that ends in whitespace. The whitespace
- // will be distributed elsewhere causing a proper flush right look
- // for the last word.
- nsRect r;
- kid->GetRect(r);
- nscoord maxWidth = aState.availSize.width;
- nscoord availableSpace = maxWidth - r.width;
- nscoord maxAvailSpace = nscoord(maxWidth * 0.1f);
- if ((availableSpace >= 0) && (availableSpace < maxAvailSpace)) {
- kid->JustifyReflow(aCX, availableSpace);
- kid->SizeTo(r.width + availableSpace, r.height);
- }
- kid->GetNextSibling(kid);
- } else {
- // XXX Get justification of multiple elements working
- while (--lineLength >= 0) {
- kid->GetNextSibling(kid);
- }
+ aState.mY += aState.mBorderPadding.bottom;
+ nscoord lastBottomMargin = aState.mPrevMaxPosBottomMargin -
+ aState.mPrevMaxNegBottomMargin;
+ if (!aState.mUnconstrainedHeight && (lastBottomMargin > 0)) {
+ // It's possible that we don't have room for the last bottom
+ // margin (the last bottom margin is the margin following a block
+ // element that we contain; it isn't applied immediately because
+ // of the margin collapsing logic). This can happen when we are
+ // reflowed in a limited amount of space because we don't know in
+ // advance what the last bottom margin will be.
+ nscoord maxY = aMaxSize.height;
+ if (aState.mY + lastBottomMargin > maxY) {
+ lastBottomMargin = maxY - aState.mY;
+ if (lastBottomMargin < 0) {
+ lastBottomMargin = 0;
}
}
-
- // Finally, now that the in-flow positions of the line's frames are
- // known we can apply relative positioning if any of them need it.
- nsCSSLayout::RelativePositionChildren(aCX, this, lineStart, mLines[i]);
+ aState.mY += lastBottomMargin;
}
-}
+ aDesiredRect.height = aState.mY;
-NS_METHOD nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const
-{
- aIsSplittable = frSplittableNonRectangular;
- return NS_OK;
-}
-
-NS_METHOD nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX,
- nsIFrame* aParent,
- nsIFrame*& aContinuingFrame)
-{
- nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent);
- PrepareContinuingFrame(aCX, aParent, cf);
- aContinuingFrame = cf;
- return NS_OK;
-}
-
-NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aCX,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsReflowCommand& aReflowCommand,
- ReflowStatus& aStatus)
-{
+ // Set return status
aStatus = frComplete;
-
- if (aReflowCommand.GetTarget() == this) {
- // XXX for now, just do a complete reflow mapped (it'll kinda
- // work, but it's slow)
-
- nsBlockReflowState state;
- SetupState(aCX, state, aMaxSize, nsnull, aSpaceManager);
- PRBool reflowMappedOK = ReflowMappedChildren(aCX, state);
- if (!reflowMappedOK) {
- aStatus = frNotComplete;
- }
- } else {
- // XXX not yet implemented
- NS_ABORT();
- // XXX work to compute initial state goes *HERE*
- aDesiredRect.x = 0;
- aDesiredRect.y = 0;
- aDesiredRect.width = 0;
- aDesiredRect.height = 0;
+ if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) {
+ rv = NS_OK;
+ aStatus = frNotComplete;
}
+#ifdef NS_DEBUG
+ // Verify that the line layout code pulled everything up when it
+ // indicates a complete reflow.
+ if (frComplete == aStatus) {
+ nsBlockFrame* nextBlock = (nsBlockFrame*) mNextInFlow;
+ while (nsnull != nextBlock) {
+ NS_ASSERTION((nsnull == nextBlock->mLines) &&
+ (nextBlock->mChildCount == 0) &&
+ (nsnull == nextBlock->mFirstChild),
+ "bad completion status");
+ nextBlock = (nsBlockFrame*) nextBlock->mNextInFlow;
+ }
- mCurrentState = nsnull;
- return NS_OK;
+#if XXX
+ // We better not be in the same parent frame as our prev-in-flow.
+ // If we are it means that we were continued instead of pulling up
+ // children.
+ if (nsnull != mPrevInFlow) {
+ nsIFrame* ourParent = mGeometricParent;
+ nsIFrame* prevParent = ((nsBlockFrame*)mPrevInFlow)->mGeometricParent;
+ NS_ASSERTION(ourParent != prevParent, "bad continuation");
+ }
+#endif
+ }
+#endif
+
+ // Now that reflow has finished, remove the cached pointer
+ shell->RemoveCachedData(this);
+ NS_RELEASE(shell);
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+ PostReflowCheck(aStatus);
+#endif
+#ifdef NOISY_REFLOW
+ ListTag(stdout);
+ printf(": After:\n");
+ List(stdout, 1);
+#endif
+ return rv;
}
-PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
+//----------------------------------------------------------------------
+
+NS_METHOD
+nsBlockFrame::ContentAppended(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentInserted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentReplaced(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aOldChild,
+ nsIContent* aNewChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentDeleted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::GetReflowMetrics(nsIPresContext* aPresContext,
+ nsReflowMetrics& aMetrics)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+NS_METHOD
+nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ nsIFrame::ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ aStatus = frComplete;
+ nsBlockReflowState state;
+ rv = InitializeState(aPresContext, aSpaceManager, aMaxSize,
+ aMaxElementSize, state);
+ if (NS_OK == rv) {
+ nsRect desiredRect;
+ rv = DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus);
+ }
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsReflowCommand& aReflowCommand,
+ nsIFrame::ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+PRBool
+nsBlockFrame::AddFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder)
+{
+ nsIPresShell* shell = aPresContext->GetShell();
+ nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this);
+ NS_RELEASE(shell);
+
+ if (nsnull != state) {
+ // Get the frame associated with the space manager, and get its
+ // nsIAnchoredItems interface
+ nsIFrame* frame = state->mSpaceManager->GetFrame();
+ nsIAnchoredItems* anchoredItems = nsnull;
+
+ frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems);
+ NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface");
+ if (nsnull != anchoredItems) {
+ anchoredItems->AddAnchoredItem(aFloater,
+ nsIAnchoredItems::anHTMLFloater,
+ this);
+ PlaceFloater(aPresContext, aFloater, aPlaceholder, *state);
+ return PR_TRUE;
+ }
+ }
+
+ return PR_FALSE;
+}
+
+void
+nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder)
+{
+ nsIPresShell* shell = aPresContext->GetShell();
+ nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this);
+ NS_RELEASE(shell);
+ if (nsnull != state) {
+ PlaceFloater(aPresContext, aFloater, aPlaceholder, *state);
+ }
+}
+
+PRBool
+nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
{
do {
nsIFrame* parent;
-
aFrame->GetGeometricParent(parent);
- // See if there are any non-zero sized child frames that precede aFrame
- // in the child list
+ // See if there are any non-zero sized child frames that precede
+ // aFrame in the child list
nsIFrame* child;
-
parent->FirstChild(child);
while ((nsnull != child) && (aFrame != child)) {
nsSize size;
@@ -1949,7 +1285,6 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
// We found a non-zero sized child frame that precedes aFrame
return PR_FALSE;
}
-
child->GetNextSibling(child);
}
@@ -1957,184 +1292,127 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
// Walk up one level and check that its parent is left-most as well
aFrame = parent;
} while (aFrame != this);
-
return PR_TRUE;
}
-PRBool nsBlockFrame::AddFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
- PlaceholderFrame* aPlaceholder)
-{
- // Get the frame associated with the space manager, and get its nsIAnchoredItems
- // interface
- nsIFrame* frame = mCurrentState->spaceManager->GetFrame();
- nsIAnchoredItems* anchoredItems = nsnull;
-
- frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems);
- NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface");
-
- if (nsnull != anchoredItems) {
- anchoredItems->AddAnchoredItem(aFloater, nsIAnchoredItems::anHTMLFloater, this);
- PlaceFloater(aCX, aFloater, aPlaceholder);
- return PR_TRUE;
- }
-
- return PR_FALSE;
-}
-
-// XXX The size of the floater needs to be taken into consideration if we're
-// computing a maximum element size
-void nsBlockFrame::PlaceFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
- PlaceholderFrame* aPlaceholder)
+void
+nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder,
+ nsBlockReflowState& aState)
{
// If the floater is the left-most non-zero size child frame then insert
// it before the current line; otherwise add it to the below-current-line
// todo list and we'll handle it when we flush out the line
if (IsLeftMostChild(aPlaceholder)) {
- // Get the type of floater
- nsIStyleContextPtr styleContext;
+ nsISpaceManager* sm = aState.mSpaceManager;
- aFloater->GetStyleContext(aCX, styleContext.AssignRef());
+ // Get the type of floater
+ nsIStyleContextPtr styleContext;
+ aFloater->GetStyleContext(aPresContext, styleContext.AssignRef());
nsStyleDisplay* floaterDisplay = (nsStyleDisplay*)
styleContext->GetData(kStyleDisplaySID);
- if (!mCurrentState->isInline) {
- // Get the current band for this line
- GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin);
- }
-
- // Commit some space in the space manager
- nsRect region;
-
+ // Commit some space in the space manager and adjust our current
+ // band of available space.
+ nsRect region;
aFloater->GetRect(region);
- region.y = mCurrentState->currentBand->availSpace.y;
-
+ region.y = aState.mY;
if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) {
- region.x = mCurrentState->currentBand->availSpace.x;
+ region.x = aState.mX;
} else {
- NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, "bad float type");
- region.x = mCurrentState->currentBand->availSpace.XMost() - region.width;
+ NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats,
+ "bad float type");
+ region.x = aState.mCurrentBand.availSpace.XMost() - region.width;
}
// XXX Don't forget the floater's margins...
- mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, 0);
- mCurrentState->spaceManager->AddRectRegion(region, aFloater);
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->AddRectRegion(region, aFloater);
// Set the origin of the floater in world coordinates
nscoord worldX, worldY;
-
- mCurrentState->spaceManager->GetTranslation(worldX, worldY);
+ sm->GetTranslation(worldX, worldY);
aFloater->MoveTo(region.x + worldX, region.y + worldY);
- mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0);
+ sm->Translate(-aState.mBorderPadding.left, 0);
- // Update the band of available space to reflect space taken up by the floater
- GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin);
+ // Update the band of available space to reflect space taken up by
+ // the floater
+ GetAvailableSpace(aState, aState.mY);
+ if (nsnull != aState.mCurrentLine) {
+ nsLineLayout& lineLayout = *aState.mCurrentLine;
+ lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace);
+ }
} else {
// Add the floater to our to-do list
- mCurrentState->floaterToDo.AppendElement(aFloater);
+ aState.mPendingFloaters.AppendElement(aFloater);
}
}
-NS_METHOD nsBlockFrame::ContentAppended(nsIPresShell* aShell,
- nsIPresContext* aPresContext,
- nsIContent* aContainer)
+void
+nsBlockFrame::PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState,
+ nscoord aY)
{
- // Get the last-in-flow
- nsBlockFrame* flow = (nsBlockFrame*)GetLastInFlow();
- PRInt32 kidIndex = flow->NextChildOffset();
- PRInt32 startIndex = kidIndex;
+ NS_PRECONDITION(aState.mPendingFloaters.Count() > 0, "no floaters");
-#if 0
- nsIFrame* kidFrame = nsnull;
- nsIFrame* prevKidFrame;
-
- flow->LastChild(prevKidFrame);
- for (;;) {
- // Get the next content object
- nsIContentPtr kid = mContent->ChildAt(kidIndex);
- if (nsnull == kid) {
- break;
- }
+ nsISpaceManager* sm = aState.mSpaceManager;
+ nsBlockBandData* bd = &aState.mCurrentBand;
- // Resolve style for the kid
- nsIStyleContextPtr kidSC = aPresContext->ResolveStyleContextFor(kid, this);
- nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
- kidSC->GetData(kStyleDisplaySID);
+ // XXX Factor this code with PlaceFloater()...
+ PRInt32 numFloaters = aState.mPendingFloaters.Count();
+ for (PRInt32 i = 0; i < numFloaters; i++) {
+ nsIFrame* floater = (nsIFrame*) aState.mPendingFloaters[i];
+ nsRect region;
- // Is it a floater?
- if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
- PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this);
+ // Get the band of available space
+ // XXX This is inefficient to do this inside the loop...
+ GetAvailableSpace(aState, aY);
+
+ // Get the type of floater
+ nsIStyleContextPtr styleContext;
+ floater->GetStyleContext(aState.mPresContext, styleContext.AssignRef());
+ nsStyleDisplay* sd = (nsStyleDisplay*)
+ styleContext->GetData(kStyleDisplaySID);
+
+ floater->GetRect(region);
+ region.y = bd->availSpace.y;
+ if (NS_STYLE_FLOAT_LEFT == sd->mFloats) {
+ region.x = bd->availSpace.x;
} else {
- // Create initial frame for the child
- nsIContentDelegate* kidDel;
- nsresult fr;
- switch (kidDisplay->mDisplay) {
- case NS_STYLE_DISPLAY_BLOCK:
- case NS_STYLE_DISPLAY_LIST_ITEM:
- // Pseudo block frames do not contain other block elements
- // unless the block element would be the first child.
- if (IsPseudoFrame()) {
- // Update last content offset now that we are done drawing
- // children from our parent.
- SetLastContentOffset(prevKidFrame);
-
- // It better be true that we are not being asked to flow a
- // block element as our first child. That means the body
- // decided it needed a pseudo-frame when it shouldn't have.
- NS_ASSERTION(nsnull != mFirstChild, "bad body");
-
- return NS_OK;
- }
- // FALL THROUGH (and create frame)
-
- case NS_STYLE_DISPLAY_INLINE:
- kidDel = kid->GetDelegate(aPresContext);
- kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this);
- NS_RELEASE(kidDel);
- break;
-
- default:
- fr = nsFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- break;
- }
+ NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type");
+ region.x = bd->availSpace.XMost() - region.width;
}
- kidFrame->SetStyleContext(aCX,kidSC);
- // Link child frame into the list of children
- if (nsnull != prevKidFrame) {
-#ifdef NS_DEBUG
- nsIFrame* nextSibling;
+ // XXX Don't forget the floater's margins...
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->AddRectRegion(region, floater);
- prevKidFrame->GetNextSibling(nextSibling);
- NS_ASSERTION(nsnull == nextSibling, "bad append");
-#endif
- prevKidFrame->SetNextSibling(kidFrame);
- } else {
- NS_ASSERTION(nsnull == mFirstChild, "bad create");
- mFirstChild = kidFrame;
- SetFirstContentOffset(kidFrame);
- }
- prevKidFrame = kidFrame;
- kidIndex++;
- mChildCount++;
+ // Set the origin of the floater in world coordinates
+ nscoord worldX, worldY;
+ sm->GetTranslation(worldX, worldY);
+ floater->MoveTo(region.x + worldX, region.y + worldY);
+ sm->Translate(-aState.mBorderPadding.left, 0);
}
- SetLastContentOffset(prevKidFrame);
-#endif
- // If this is a pseudo-frame then our parent will generate the
- // reflow command. Otherwise, if the container is us then we should
- // generate the reflow command because we were directly called.
- if (!IsPseudoFrame() && (aContainer == mContent)) {
- nsReflowCommand* rc =
- new nsReflowCommand(aPresContext, flow, nsReflowCommand::FrameAppended,
- startIndex);
- aShell->AppendReflowCommand(rc);
+ aState.mPendingFloaters.Clear();
+
+ // Pass on updated available space to the current line
+ if (nsnull != aState.mCurrentLine) {
+ nsLineLayout& lineLayout = *aState.mCurrentLine;
+ lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace);
}
- return NS_OK;
}
-PRIntn nsBlockFrame::GetSkipSides() const
+//----------------------------------------------------------------------
+
+nsLineData*
+nsBlockFrame::GetFirstLine()
+{
+ return mLines;
+}
+
+PRIntn
+nsBlockFrame::GetSkipSides() const
{
PRIntn skip = 0;
if (nsnull != mPrevInFlow) {
@@ -2145,40 +1423,3 @@ PRIntn nsBlockFrame::GetSkipSides() const
}
return skip;
}
-
-nsHTMLFrameType nsBlockFrame::GetFrameType() const
-{
- return eHTMLFrame_Block;
-}
-
-NS_METHOD nsBlockFrame::ListTag(FILE* out) const
-{
- if ((nsnull != mGeometricParent) && IsPseudoFrame()) {
- fprintf(out, "*block<");
- nsIAtom* atom = mContent->GetTag();
- if (nsnull != atom) {
- nsAutoString tmp;
- atom->ToString(tmp);
- fputs(tmp, out);
- }
- fprintf(out, ">(%d)@%p", mIndexInParent, this);
- } else {
- nsHTMLContainerFrame::ListTag(out);
- }
- return NS_OK;
-}
-
-#ifdef NS_DEBUG
-void nsBlockFrame::DumpFlow() const
-{
-#ifdef NOISY_FLOW
- nsBlockFrame* flow = (nsBlockFrame*) mNextInFlow;
- while (flow != 0) {
- printf(" %p: [%d,%d,%c]\n",
- flow, flow->mFirstContentOffset, flow->mLastContentOffset,
- (flow->mLastContentIsComplete ? 'T' : 'F'));
- flow = (nsBlockFrame*) flow->mNextInFlow;
- }
-#endif
-}
-#endif
diff --git a/layout/generic/nsBlockFrame.h b/layout/generic/nsBlockFrame.h
index f11bba52ca10..f3fa810539b5 100644
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -20,158 +20,101 @@
#include "nsHTMLContainerFrame.h"
#include "nsIFloaterContainer.h"
-#include "nsIHTMLFrameType.h"
#include "nsIRunaround.h"
+#include "nsISpaceManager.h"
+#include "nsLineLayout.h"
#include "nsVoidArray.h"
-struct BlockBandData;
+
struct nsMargin;
struct nsStyleDisplay;
struct nsStyleFont;
struct nsStyleText;
+class nsBlockFrame;
+struct nsBandData;
+
+struct nsBlockBandData : public nsBandData {
+ // Trapezoid's used during band processing
+ nsBandTrapezoid data[12];
+
+ // Bounding rect of available space between any left and right floaters
+ nsRect availSpace;
+
+ nsBlockBandData() {
+ size = 12;
+ trapezoids = data;
+ }
+
+ /**
+ * Computes the bounding rect of the available space, i.e. space
+ * between any left and right floaters Uses the current trapezoid
+ * data, see nsISpaceManager::GetBandData(). Also updates member
+ * data "availSpace".
+ */
+ void ComputeAvailSpaceRect();
+};
-/**
- * Block frames have some state which needs to be made
- * available to child frames for proper reflow. This structure
- * describes that state.
- */
struct nsBlockReflowState {
- // True if this is the first line of text in a block container
- PRPackedBool firstLine;
-
- // True if leading whitespace is allowed to show
- PRPackedBool allowLeadingWhitespace;
-
- // This is set when some child frame needs a break after it's placed
- PRPackedBool breakAfterChild;
-
- // This is set when a child needs a break before it's placed. Note
- // that this value is set by a child AFTER we have called it's
- // ResizeReflow method.
- PRPackedBool breakBeforeChild;
-
- // This is set when the first child should be treated specially
- // because it's an inside list item bullet.
- // XXX this can go away once we have a way for the bullet's style
- // molecule to *not* be the same as it's parent's
- PRPackedBool firstChildIsInsideBullet;
-
- // For pre-formatted text, this is our current column
- PRIntn column;
-
- // The next list ordinal value
- PRInt32 nextListOrdinal;
-
- //----------------------------------------------------------------------
- // State from here on down is not to be used by block child frames!
-
- // Space manager to use
- nsISpaceManager* spaceManager;
-
- // Block's style context
- nsIStyleContext* styleContext;
- nsStyleText* styleText;
- nsStyleFont* styleFont;
- nsStyleDisplay* styleDisplay;
-
- // Block's available size (computed from the block's parent)
- nsSize availSize;
-
- // Current band of available space. Used for doing runaround
- BlockBandData* currentBand;
-
- // Pointer to a max-element-size (nsnull if none required)
- nsSize* maxElementSize;
-
- // The maximum x-most of our lines and block-level elements. This is used to
- // compute our desired size, and includes our left border/padding. For block-
- // level elements this also includes the block's right margin.
- nscoord kidXMost;
-
- // Current reflow position
- nscoord y;
-
- // Current line state
- nscoord x; // inline elements only. not include border/padding
- PRBool isInline; // whether the current is inline or block
- nsVoidArray lineLengths; // line length temporary storage
- PRIntn currentLineNumber; // index into mLines
- nsIFrame* lineStart; // frame starting the line
- PRInt32 lineLength; // length of line
- nscoord* ascents; // ascent information for each child
- nscoord maxAscent; // max ascent for this line
- nscoord maxDescent; // max descent for this line
- nscoord lineWidth; // current width of line
-#if 0
- nscoord maxPosTopMargin; // maximum positive top margin
- nscoord maxNegTopMargin; // maximum negative top margin
-#else
- nscoord topMargin; // current top margin
-#endif
- nscoord maxPosBottomMargin; // maximum positive bottom margin
- nscoord maxNegBottomMargin; // maximum negative bottom margin
- nsSize lineMaxElementSize; // max element size for current line
- PRBool lastContentIsComplete; // reflow status of last child on line
- nsVoidArray floaterToDo; // list of floaters to place below current line
-
- PRInt32 maxAscents; // size of ascent buffer
- nscoord ascentBuf[20];
-
- PRPackedBool needRelativePos; // some kid in line needs relative pos
-
- // Previous line state that we carry forward to the next line
- nsIFrame* prevLineLastFrame;
- nscoord prevLineHeight; // height of the previous line
- nscoord prevMaxPosBottomMargin; // maximum posative bottom margin
- nscoord prevMaxNegBottomMargin; // maximum negative bottom margin
- PRBool prevLineLastContentIsComplete;
-
- // Sanitized version of mol->borderPadding; if this block frame is a
- // pseudo-frame then the margin will be zero'd.
- nsMargin borderPadding;
-
- PRPackedBool justifying; // we are justifying
-
- // Status from last PlaceAndReflowChild
- nsIFrame::ReflowStatus reflowStatus;
-
- // Flags for whether the max size is unconstrained
- PRBool unconstrainedWidth;
- PRBool unconstrainedHeight;
-
nsBlockReflowState();
-
~nsBlockReflowState();
- void Init(const nsSize& aMaxSize, nsSize* aMaxElementSize,
- nsIStyleContext* aStyleContext, nsISpaceManager* aSpaceManager);
+ nsresult Initialize(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockFrame* aBlock);
- void AddAscent(nscoord aAscent);
- void AdvanceToNextLine(nsIFrame* aPrevLineLastFrame, nscoord aPrevLineHeight);
+ nsIPresContext* mPresContext;
-#ifdef NS_DEBUG
- void DumpLine();
- void DumpList();
-#endif
+ nsBlockFrame* mBlock;
+ PRBool mBlockIsPseudo;
+
+ // Current line being reflowed
+ nsLineLayout* mCurrentLine;
+
+ // Previous line's last child frame
+ nsIFrame* mPrevKidFrame;
+
+ // Layout position information
+ nscoord mX;
+ nscoord mY;
+ nsSize mAvailSize;
+ PRPackedBool mUnconstrainedWidth;
+ PRPackedBool mUnconstrainedHeight;
+ nsSize* mMaxElementSizePointer;
+ nscoord mKidXMost;
+
+ // Bottom margin information from the previous line
+ nscoord mPrevMaxNegBottomMargin;
+ nscoord mPrevMaxPosBottomMargin;
+
+ // Block frame border+padding information
+ nsMargin mBorderPadding;
+
+ // Space manager and current band information
+ nsISpaceManager* mSpaceManager;
+ nsBlockBandData mCurrentBand;
+
+ // Array of floaters to place below current line
+ nsVoidArray mPendingFloaters;
+
+ PRInt32 mNextListOrdinal;
+ PRPackedBool mFirstChildIsInsideBullet;
};
//----------------------------------------------------------------------
+/* 94e8e410-de21-11d1-89bf-006008911b81 */
+#define NS_BLOCKFRAME_CID \
+ {0x94e8e410, 0xde21, 0x11d1, {0x89, 0xbf, 0x00, 0x60, 0x08, 0x91, 0x1b, 0x81}}
+
/**
*
Block Reflow
*
* The block frame reflow machinery performs "2D" layout. Inline
* elements are flowed into logical lines (left to right or right to
- * left) and the lines are stacked vertically. Block elements are
- * flowed onto their own line after flushing out any preceeding line.
- *
- * After a line is ready to be flushed out, vertical alignment is
- * performed. Vertical alignment may require the line to consume more
- * vertical space than is available thus causing the entire line to
- * be pushed.
- *
- * After vertical alignment is done, horizontal alignment (including
- * justification) is performed. Finally, relative positioning is done
- * on any elements that require it.
+ * left) and the lines are stacked vertically. nsLineLayout is used
+ * for this part of the process. Block elements are flowed directly by
+ * the block reflow logic after flushing out any preceeding line.
*
* During reflow, the block frame will make available to child frames
* it's reflow state using the presentation shell's cached data
@@ -186,144 +129,155 @@ struct nsBlockReflowState {
*
*
Assertions
* mLastContentIsComplete always reflects the state of the last
- * child frame on our chlid list.
+ * child frame on our chlid list.
*/
-class nsBlockFrame : public nsHTMLContainerFrame, public nsIHTMLFrameType,
- public nsIRunaround, public nsIFloaterContainer
+
+// XXX we don't use nsContainerFrame mOverFlowList!!! wasted memory
+
+class nsBlockFrame : public nsHTMLContainerFrame,
+ public nsIRunaround,
+ public nsIFloaterContainer
{
public:
/**
* Create a new block frame that maps the given piece of content.
*/
- static nsresult NewFrame(nsIFrame** aInstancePtrResult,
+ static nsresult NewFrame(nsIFrame** aInstancePtrResult,
nsIContent* aContent,
PRInt32 aIndexInParent,
nsIFrame* aParent);
+ // nsISupports
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
- NS_IMETHOD ResizeReflow(nsIPresContext* aPresContext,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsSize* aMaxElementSize,
- ReflowStatus& aStatus);
-
- NS_IMETHOD IncrementalReflow(nsIPresContext* aPresContext,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsReflowCommand& aReflowCommand,
- ReflowStatus& aStatus);
-
- NS_IMETHOD ContentAppended(nsIPresShell* aShell,
+ // nsIFrame
+ NS_IMETHOD ContentAppended(nsIPresShell* aShell,
nsIPresContext* aPresContext,
- nsIContent* aContainer);
-
+ nsIContent* aContainer);
+ NS_IMETHOD ContentInserted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent);
+ NS_IMETHOD ContentReplaced(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aOldChild,
+ nsIContent* aNewChild,
+ PRInt32 aIndexInParent);
+ NS_IMETHOD ContentDeleted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent);
+ NS_IMETHOD GetReflowMetrics(nsIPresContext* aPresContext,
+ nsReflowMetrics& aMetrics);
NS_IMETHOD IsSplittable(SplittableType& aIsSplittable) const;
-
NS_IMETHOD CreateContinuingFrame(nsIPresContext* aPresContext,
- nsIFrame* aParent,
- nsIFrame*& aContinuingFrame);
+ nsIFrame* aParent,
+ nsIFrame*& aContinuingFrame);
+ NS_IMETHOD List(FILE* out = stdout, PRInt32 aIndent = 0) const;
+ NS_IMETHOD ListTag(FILE* out) const;
+ NS_IMETHOD VerifyTree() const;
- virtual PRBool AddFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
+ // nsIRunaround
+ NS_IMETHOD ResizeReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ nsIFrame::ReflowStatus& aStatus);
+ NS_IMETHOD IncrementalReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsReflowCommand& aReflowCommand,
+ nsIFrame::ReflowStatus& aStatus);
+
+ // nsIFloaterContainer
+ virtual PRBool AddFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
PlaceholderFrame* aPlaceholder);
- virtual void PlaceFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
+ virtual void PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
PlaceholderFrame* aPlaceholder);
- NS_IMETHOD ListTag(FILE* out = stdout) const;
+ // nsBlockFrame
+ nsresult ReflowInlineChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus);
- virtual nsHTMLFrameType GetFrameType() const;
+ nsresult ReflowBlockChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus);
+
+ nsLineData* GetFirstLine();
+
+ static nsBlockReflowState* FindBlockReflowState(nsIPresContext* aPresContext,
+ nsIFrame* aFrame);
protected:
nsBlockFrame(nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent);
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent);
virtual ~nsBlockFrame();
virtual PRIntn GetSkipSides() const;
- PRBool MoreToReflow(nsIPresContext* aCX);
+ virtual void WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow);
- nscoord GetTopMarginFor(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC,
- PRBool aIsInline);
-
- PRBool AdvanceToNextLine(nsIPresContext* aPresContext,
+ nsresult InitializeState(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
nsBlockReflowState& aState);
- void AddInlineChildToLine(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsReflowMetrics& aKidSize,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC);
+ nsresult DoResizeReflow(nsBlockReflowState& aState,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ ReflowStatus& aStatus);
- void AddBlockChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsRect& aKidRect,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC);
+ void DestroyLines();
- void GetAvailSize(nsSize& aResult,
- nsBlockReflowState& aState,
- nsIStyleContext* aKidSC,
- PRBool aIsInline);
+ void DrainOverflowList();
- PRIntn PlaceAndReflowChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* kidFrame,
- nsIStyleContext* aKidSC);
+ nsLineData* CreateLineForOverflowList(nsIFrame* aOverflowList);
- void PushKids(nsBlockReflowState& aState);
+ nsresult VerifyLines(PRBool aFinalCheck) const;
- void SetupState(nsIPresContext* aCX, nsBlockReflowState& aState,
- const nsSize& aMaxSize, nsSize* aMaxElementSize,
- nsISpaceManager* aSpaceManager);
-
- nsresult DoResizeReflow(nsIPresContext* aPresContext,
- nsBlockReflowState& aState,
- nsRect& aDesiredRect,
- ReflowStatus& aStatus);
-
- PRBool ReflowMappedChildren(nsIPresContext* aPresContext,
- nsBlockReflowState& aState);
-
- PRBool PullUpChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState);
-
- ReflowStatus ReflowAppendedChildren(nsIPresContext* aPresContext,
- nsBlockReflowState& aState);
-
- void JustifyLines(nsIPresContext* aPresContext, nsBlockReflowState& aState);
+ nsresult PlaceLine(nsBlockReflowState& aState,
+ nsLineLayout& aLineLayout,
+ nsLineData* aLine);
PRBool IsLeftMostChild(nsIFrame* aFrame);
- void GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY);
-
- void PlaceBelowCurrentLineFloaters(nsIPresContext* aCX,
- nsBlockReflowState& aState,
+ void PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder,
+ nsBlockReflowState& aState);
+ void PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState,
nscoord aY);
- void ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear);
+ nsresult GetAvailableSpace(nsBlockReflowState& aState, nscoord aY);
-#ifdef NS_DEBUG
- void DumpFlow() const;
-#endif
+ PRBool MoreToReflow(nsBlockReflowState& aState);
- /**
- * Array of lines lengths. For each logical line of children, this array
- * contains a count of the number of children on the line.
- */
- PRInt32* mLines;
- PRInt32 mNumLines;
- nsBlockReflowState* mCurrentState;
+ nsresult PushLines(nsBlockReflowState& aState,
+ nsLineData* aLine);
+
+ nsresult ReflowMapped(nsBlockReflowState& aState);
+
+ nsresult ReflowUnmapped(nsBlockReflowState& aState);
+
+ nsLineData* mLines;
};
#endif /* nsBlockFrame_h___ */
diff --git a/layout/generic/nsBlockReflowState.cpp b/layout/generic/nsBlockReflowState.cpp
index 59d7407ec5b1..0228ca80d413 100644
--- a/layout/generic/nsBlockReflowState.cpp
+++ b/layout/generic/nsBlockReflowState.cpp
@@ -16,66 +16,37 @@
* Reserved.
*/
#include "nsBlockFrame.h"
-#include "nsSize.h"
-#include "nsIAnchoredItems.h"
-#include "nsIContent.h"
-#include "nsIContentDelegate.h"
-#include "nsISpaceManager.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
-#include "nsIPresContext.h"
-#include "nsMargin.h"
-#include "nsHTMLIIDs.h"
-#include "nsCSSLayout.h"
-#include "nsCRT.h"
-#include "nsIPresShell.h"
-#include "nsReflowCommand.h"
-#include "nsPlaceholderFrame.h"
-#include "nsHTMLAtoms.h"
-#include "nsHTMLValue.h"
#include "nsIHTMLContent.h"
-#include "nsAbsoluteFrame.h"
+#include "nsIPresContext.h"
+#include "nsIPresShell.h"
+#include "nsIAnchoredItems.h"
+#include "nsPlaceholderFrame.h"
#include "nsIPtr.h"
+#include "nsHTMLAtoms.h"
+#include "nsHTMLIIDs.h"
+#include "nsHTMLValue.h"
-#ifdef NS_DEBUG
-#undef NOISY
-#undef NOISY_FLOW
-#else
-#undef NOISY
-#undef NOISY_FLOW
-#endif
+// XXX what do we do with catastrophic errors (rv < 0)? What is the
+// state of the reflow world after such an error?
-static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID);
-static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID);
-static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID);
+#undef NOISY_REFLOW
static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID);
-static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID);
-static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID);
-static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID);
static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID);
-NS_DEF_PTR(nsIStyleContext);
+static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID);
+static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID);
+static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID);
+
NS_DEF_PTR(nsIContent);
+NS_DEF_PTR(nsIStyleContext);
-struct BlockBandData : public nsBandData {
- nsBandTrapezoid data[12];
+//----------------------------------------------------------------------
- // Bounding rect of available space between any left and right floaters
- nsRect availSpace;
- // Constructor
- BlockBandData() {size = 12; trapezoids = data;}
-
- // Computes the bounding rect of the available space, i.e. space between
- // any left and right floaters
- //
- // Uses the current trapezoid data, see nsISpaceManager::GetBandData().
- // Updates member data "availSpace"
- void ComputeAvailSpaceRect();
-};
-
-void BlockBandData::ComputeAvailSpaceRect()
+void nsBlockBandData::ComputeAvailSpaceRect()
{
nsBandTrapezoid* trapezoid = data;
@@ -121,198 +92,60 @@ void BlockBandData::ComputeAvailSpaceRect()
}
}
-// XXX Bugs
-// 1. right to left reflow can generate negative x coordinates.
-
-// XXX Speedup idea (all containers)
-
-// If I reflow a child and it gives back not-complete status then
-// there is no sense in trying to pullup children. For blocks, it's a
-// little more complicated unless the child is a block - if the child
-// is a block, then we must be out of room hence we should stop. If
-// the child is not a block then our line should be flushed (see #2
-// below) if our line is already empty then we must be out of room.
-
-// For inline frames and column frames, if we reflow a child and get
-// back not-complete status then we should bail immediately because we
-// are out of room.
-
-// XXX Speedup ideas:
-// 1. change pullup code to use line information from next in flow
-// 2. we can advance to next line immediately after reflowing something
-// and noticing that it's not complete.
-// 3. pass down last child information in aState so that pullup, etc.,
-// don't need to recompute it
-
-// XXX TODO:
-// 0. Move justification into line flushing code
-
-// 1. To get ebina margins I need "auto" information from the style
-// system margin's. A bottom/top margin of auto will then be computed like
-// ebina computes it [however the heck that is].
-
-// 2. kicking out floaters and talking with floater container to adjust
-// left and right margins
+//----------------------------------------------------------------------
nsBlockReflowState::nsBlockReflowState()
{
}
-void nsBlockReflowState::Init(const nsSize& aMaxSize,
- nsSize* aMaxElementSize,
- nsIStyleContext* aBlockSC,
- nsISpaceManager* aSpaceManager)
+nsBlockReflowState::~nsBlockReflowState()
{
- firstLine = PR_TRUE;
- allowLeadingWhitespace = PR_FALSE;
- breakAfterChild = PR_FALSE;
- breakBeforeChild = PR_FALSE;
- firstChildIsInsideBullet = PR_FALSE;
- nextListOrdinal = -1;
- column = 0;
+}
- spaceManager = aSpaceManager;
- currentBand = new BlockBandData;
+nsresult
+nsBlockReflowState::Initialize(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockFrame* aBlock)
+{
+ nsresult rv = NS_OK;
- styleContext = aBlockSC;
- styleText = (nsStyleText*) aBlockSC->GetData(kStyleTextSID);
- styleFont = (nsStyleFont*) aBlockSC->GetData(kStyleFontSID);
- styleDisplay = (nsStyleDisplay*) aBlockSC->GetData(kStyleDisplaySID);
+ mPresContext = aPresContext;
+ mBlock = aBlock;
+ mSpaceManager = aSpaceManager;
+ mBlockIsPseudo = aBlock->IsPseudoFrame();
+ mCurrentLine = nsnull;
+ mPrevKidFrame = nsnull;
- justifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
- (NS_STYLE_WHITESPACE_PRE != styleText->mWhiteSpace);
-
- availSize.width = aMaxSize.width;
- availSize.height = aMaxSize.height;
- maxElementSize = aMaxElementSize;
+ mX = 0;
+ mY = 0;
+ mAvailSize = aMaxSize;
+ mUnconstrainedWidth = PRBool(mAvailSize.width == NS_UNCONSTRAINEDSIZE);
+ mUnconstrainedHeight = PRBool(mAvailSize.height == NS_UNCONSTRAINEDSIZE);
+ mMaxElementSizePointer = aMaxElementSize;
if (nsnull != aMaxElementSize) {
aMaxElementSize->width = 0;
aMaxElementSize->height = 0;
}
+ mKidXMost = 0;
- kidXMost = 0;
- x = 0;
- y = 0;
+ mPrevMaxPosBottomMargin = 0;
+ mPrevMaxNegBottomMargin = 0;
- isInline = PR_FALSE;
- currentLineNumber = 0;
- lineStart = nsnull;
- lineLength = 0;
- ascents = ascentBuf;
- maxAscent = 0;
- maxDescent = 0;
- lineWidth = 0;
- maxPosBottomMargin = 0;
- maxNegBottomMargin = 0;
- lineMaxElementSize.width = 0;
- lineMaxElementSize.height = 0;
- lastContentIsComplete = PR_TRUE;
+ mNextListOrdinal = -1;
+ mFirstChildIsInsideBullet = PR_FALSE;
- maxAscents = sizeof(ascentBuf) / sizeof(ascentBuf[0]);
- needRelativePos = PR_FALSE;
-
- prevLineLastFrame = nsnull;
- prevLineHeight = 0;
- topMargin = 0;
- prevMaxPosBottomMargin = 0;
- prevMaxNegBottomMargin = 0;
- prevLineLastContentIsComplete = PR_TRUE;
-
- unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE);
- unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE);
-
- reflowStatus = nsIFrame::frNotComplete;
+ return rv;
}
-nsBlockReflowState::~nsBlockReflowState()
-{
- if (ascents != ascentBuf) {
- delete ascents;
- }
- delete currentBand;
-}
-
-void nsBlockReflowState::AddAscent(nscoord aAscent)
-{
- NS_PRECONDITION(lineLength <= maxAscents, "bad line length");
- if (lineLength == maxAscents) {
- maxAscents *= 2;
- nscoord* newAscents = new nscoord[maxAscents];
- if (nsnull != newAscents) {
- nsCRT::memcpy(newAscents, ascents, sizeof(nscoord) * lineLength);
- if (ascents != ascentBuf) {
- delete ascents;
- }
- ascents = newAscents;
- } else {
- // Yikes! Out of memory!
- return;
- }
- }
- ascents[lineLength] = aAscent;
-}
-
-void nsBlockReflowState::AdvanceToNextLine(nsIFrame* aPrevLineLastFrame,
- nscoord aPrevLineHeight)
-{
- firstLine = PR_FALSE;
- allowLeadingWhitespace = PR_FALSE;
- column = 0;
- breakAfterChild = PR_FALSE;
- breakBeforeChild = PR_FALSE;
- lineStart = nsnull;
- lineLength = 0;
- currentLineNumber++;
- maxAscent = 0;
- maxDescent = 0;
- lineWidth = 0;
- needRelativePos = PR_FALSE;
-
- prevLineLastFrame = aPrevLineLastFrame;
- prevLineHeight = aPrevLineHeight;
- prevMaxPosBottomMargin = maxPosBottomMargin;
- prevMaxNegBottomMargin = maxNegBottomMargin;
-
- // Remember previous line's lastContentIsComplete
- prevLineLastContentIsComplete = lastContentIsComplete;
- lastContentIsComplete = PR_TRUE;
-
- topMargin = 0;
- maxPosBottomMargin = 0;
- maxNegBottomMargin = 0;
-}
-
-#ifdef NS_DEBUG
-void nsBlockReflowState::DumpLine()
-{
- nsIFrame* f = lineStart;
- PRInt32 ll = lineLength;
- while (--ll >= 0) {
- printf(" ");
- ((nsFrame*)f)->ListTag(stdout);/* XXX */
- printf("\n");
- f->GetNextSibling(f);
- }
-}
-
-void nsBlockReflowState::DumpList()
-{
- nsIFrame* f = lineStart;
- while (nsnull != f) {
- printf(" ");
- ((nsFrame*)f)->ListTag(stdout);/* XXX */
- printf("\n");
- f->GetNextSibling(f);
- }
-}
-#endif
-
//----------------------------------------------------------------------
-nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
- nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent)
+nsresult
+nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
+ nsIContent* aContent,
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
if (nsnull == aInstancePtrResult) {
@@ -327,963 +160,818 @@ nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
}
nsBlockFrame::nsBlockFrame(nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent)
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent)
: nsHTMLContainerFrame(aContent, aIndexInParent, aParent)
{
}
nsBlockFrame::~nsBlockFrame()
{
- if (nsnull != mLines) {
- delete mLines;
- }
+ DestroyLines();
}
-nsresult
+void nsBlockFrame::DestroyLines()
+{
+}
+
+NS_METHOD
nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
NS_PRECONDITION(0 != aInstancePtr, "null ptr");
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
}
- if (aIID.Equals(kIHTMLFrameTypeIID)) {
- *aInstancePtr = (void*) ((nsIHTMLFrameType*) this);
+ if (aIID.Equals(kBlockFrameCID)) {
+ *aInstancePtr = (void*) (this);
return NS_OK;
- } else if (aIID.Equals(kIRunaroundIID)) {
+ }
+ else if (aIID.Equals(kIRunaroundIID)) {
*aInstancePtr = (void*) ((nsIRunaround*) this);
return NS_OK;
- } else if (aIID.Equals(kIFloaterContainerIID)) {
+ }
+ else if (aIID.Equals(kIFloaterContainerIID)) {
*aInstancePtr = (void*) ((nsIFloaterContainer*) this);
return NS_OK;
}
return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
}
-// Computes the top margin to use for this child frames based on its display
-// type and the display type of the previous child frame.
-//
-// Adjacent vertical margins between block-level elements are collapsed.
-nscoord nsBlockFrame::GetTopMarginFor(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC,
- PRBool aIsInline)
+NS_METHOD
+nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const
{
- if (aIsInline) {
- // Just use whatever the previous bottom margin was
- return aState.prevMaxPosBottomMargin - aState.prevMaxNegBottomMargin;
+ aIsSplittable = frSplittableNonRectangular;
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX,
+ nsIFrame* aParent,
+ nsIFrame*& aContinuingFrame)
+{
+ nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent);
+ PrepareContinuingFrame(aCX, aParent, cf);
+ aContinuingFrame = cf;
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::ListTag(FILE* out) const
+{
+ if ((nsnull != mGeometricParent) && IsPseudoFrame()) {
+ fprintf(out, "*block<");
+ nsIAtom* atom = mContent->GetTag();
+ if (nsnull != atom) {
+ nsAutoString tmp;
+ atom->ToString(tmp);
+ fputs(tmp, out);
+ }
+ fprintf(out, ">(%d)@%p", mIndexInParent, this);
} else {
- // Does the frame have a prev-in-flow?
- nsIFrame* kidPrevInFlow;
+ nsHTMLContainerFrame::ListTag(out);
+ }
+ return NS_OK;
+}
- aKidFrame->GetPrevInFlow(kidPrevInFlow);
+NS_METHOD
+nsBlockFrame::List(FILE* out, PRInt32 aIndent) const
+{
+ // Indent
+ for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
- if (nsnull == kidPrevInFlow) {
+ // Output the tag
+ ListTag(out);
+
+ // Output the first/last content offset
+ fprintf(out, "[%d,%d,%c] pif=%p nif=%p",
+ mFirstContentOffset, mLastContentOffset,
+ (mLastContentIsComplete ? 'T' : 'F'),
+ mPrevInFlow, mNextInFlow);
+
+ // Output the rect
+ out << mRect;
+
+ // Output the children, one line at a time
+ if (nsnull != mLines) {
+ fputs("<\n", out);
+ aIndent++;
+
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ line->List(out, aIndent);
+ line = line->mNextLine;
+ }
+
+ aIndent--;
+ for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
+ fputs(">\n", out);
+ } else {
+ fputs("<>\n", out);
+ }
+
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::VerifyTree() const
+{
+ nsresult rv = nsHTMLContainerFrame::VerifyTree();
+ if (NS_OK != rv) {
+ return rv;
+ }
+ rv = VerifyLines(PR_TRUE);
+ return rv;
+}
+
+nsresult
+nsBlockFrame::VerifyLines(PRBool aFinalCheck) const
+{
+ nsresult rv = NS_OK;
+
+ // Make sure that the list of children agrees with our child count.
+ // If this is not the case then the child list and the line list are
+ // not properly arranged.
+ PRInt32 len = LengthOf(mFirstChild);
+ NS_ASSERTION(mChildCount == len, "bad child list");
+
+ // Verify that our lines are correctly setup
+ PRInt32 offset = mFirstContentOffset;
+ PRInt32 lineChildCount = 0;
+ nsLineData* line = mLines;
+ nsLineData* prevLine = nsnull;
+ while (nsnull != line) {
+ if (aFinalCheck) {
+ NS_ASSERTION(((offset == line->mFirstContentOffset) &&
+ (line->mFirstContentOffset <= line->mLastContentOffset)),
+ "bad line mFirstContentOffset");
+ NS_ASSERTION(line->mLastContentOffset <= mLastContentOffset,
+ "bad line mLastContentOffset");
+ offset = line->mLastContentOffset;
+ if (line->mLastContentIsComplete) {
+ offset++;
+ }
+ }
+ lineChildCount += line->mChildCount;
+ rv = line->Verify(aFinalCheck);
+ if (NS_OK != rv) {
+ return rv;
+ }
+ prevLine = line;
+ line = line->mNextLine;
+ }
+ if (aFinalCheck && (nsnull != prevLine)) {
+ NS_ASSERTION(prevLine->mLastContentOffset == mLastContentOffset,
+ "bad mLastContentOffset");
+ NS_ASSERTION(prevLine->mLastContentIsComplete == mLastContentIsComplete,
+ "bad mLastContentIsComplete");
+ }
+ NS_ASSERTION(lineChildCount == mChildCount, "bad line counts");
+
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+// Remove a next-in-flow from from this block's list of lines
+
+// XXX problems here:
+// 1. we always have to start from the first line: slow!
+// 2. we can avoid this when the child is not the last child in a line
+
+void
+nsBlockFrame::WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow)
+{
+ // When a reflow indicates completion it's possible that
+ // next-in-flows were just removed. We have to remove them from any
+ // nsLineData's that follow the current line.
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ if (line->mFirstChild == aNextInFlow) {
+ // Remove child from line.
+ if (0 == --line->mChildCount) {
+ line->mFirstChild = nsnull;
+ }
+ else {
+ // Fixup the line
+ nsIFrame* nextKid;
+ aNextInFlow->GetNextSibling(nextKid);
+ line->mFirstChild = nextKid;
+ nextKid->GetIndexInParent(line->mFirstContentOffset);
+ }
+ break;
+ }
+ line = line->mNextLine;
+ }
+}
+
+nsresult
+nsBlockFrame::ReflowInlineChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ aStatus = ReflowChild(aKidFrame, aPresContext, aDesiredSize, aMaxSize,
+ aMaxElementSize);
+ return NS_OK;
+}
+
+nsresult
+nsBlockFrame::ReflowBlockChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ aStatus = ReflowChild(aKidFrame, aPresContext, aSpaceManager,
+ aMaxSize, aDesiredRect, aMaxElementSize);
+ return NS_OK;
+}
+
+nsLineData*
+nsBlockFrame::CreateLineForOverflowList(nsIFrame* aOverflowList)
+{
+ nsLineData* newLine = new nsLineData();
+ if (nsnull != newLine) {
+ nsIFrame* kid = aOverflowList;
+ newLine->mFirstChild = kid;
+ kid->GetIndexInParent(newLine->mFirstContentOffset);
+ newLine->mLastContentOffset = -1;
+ newLine->mLastContentIsComplete = PRPackedBool(0x255);
+ PRInt32 kids = 0;
+ while (nsnull != kid) {
+ kids++;
+ kid->GetNextSibling(kid);
+ }
+ newLine->mChildCount = kids;
+ }
+ return newLine;
+}
+
+void
+nsBlockFrame::DrainOverflowList()
+{
+ nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow;
+ if (nsnull != prevBlock) {
+ nsIFrame* overflowList = prevBlock->mOverflowList;
+ if (nsnull != overflowList) {
+ NS_ASSERTION(nsnull == mFirstChild, "bad overflow list");
+ NS_ASSERTION(nsnull == mLines, "bad overflow list");
+
+ // Create a line to hold the entire overflow list
+ nsLineData* newLine = CreateLineForOverflowList(overflowList);
+
+ // Place the children on our child list; this also reassigns
+ // their geometric parent and updates our mChildCount.
+ AppendChildren(overflowList);
+ prevBlock->mOverflowList = nsnull;
+
+ // The new line is the first line
+ mLines = newLine;
+ }
+ }
+
+ if (nsnull != mOverflowList) {
+ NS_ASSERTION(nsnull != mFirstChild,
+ "overflow list but no mapped children");
+
+ // Create a line to hold the overflow list
+ nsLineData* newLine = CreateLineForOverflowList(mOverflowList);
+
+ // Place the children on our child list; this also reassigns
+ // their geometric parent and updates our mChildCount.
+ AppendChildren(mOverflowList, PR_FALSE);
+ mOverflowList = nsnull;
+
+ // The new line is appended after our other lines
+ nsLineData* prevLine = nsnull;
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ prevLine = line;
+ line = line->mNextLine;
+ }
+ if (nsnull == prevLine) {
+ mLines = newLine;
+ }
+ else {
+ prevLine->mNextLine = newLine;
+ newLine->mPrevLine = prevLine;
+ }
+ }
+#ifdef NS_DEBUG
+ VerifyLines(PR_FALSE);
+#endif
+}
+
+// XXX add in code here that notices if margin's were not provided by
+// the style system and when that is the case to apply the old layout
+// engines margin calculations.
+
+nsresult
+nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
+ nsLineLayout& aLineLayout,
+ nsLineData* aLine)
+{
+ nsresult rv = NS_LINE_LAYOUT_COMPLETE;
+
+ nscoord topMargin = 0;
+ nscoord bottomMargin = 0;
+ nscoord maxNegBottomMargin = 0;
+ nscoord maxPosBottomMargin = 0;
+
+ // See if block margins apply to this line or not
+ PRBool isBlockLine = PR_FALSE;
+ if (1 == aLine->mChildCount) {
+ nsIStyleContextPtr kidSC;
+ nsIFrame* kid = aLine->mFirstChild;
+ rv = kid->GetStyleContext(aState.mPresContext, kidSC.AssignRef());
+ if (NS_OK != rv) return rv;
+
+ nsStyleDisplay* display = (nsStyleDisplay*)
+ kidSC->GetData(kStyleDisplaySID);
+ switch (display->mDisplay) {
+ case NS_STYLE_DISPLAY_BLOCK:
+ case NS_STYLE_DISPLAY_LIST_ITEM:
+ isBlockLine = PR_TRUE;
+ nsStyleSpacing* spacing = (nsStyleSpacing*)
+ kidSC->GetData(kStyleSpacingSID);
+
+ // Calculate top margin by collapsing with previous bottom margin
+ // if any.
nscoord maxNegTopMargin = 0;
nscoord maxPosTopMargin = 0;
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- if (ss->mMargin.top < 0) {
- maxNegTopMargin = -ss->mMargin.top;
+ if (spacing->mMargin.top < 0) {
+ maxNegTopMargin = -spacing->mMargin.top;
} else {
- maxPosTopMargin = ss->mMargin.top;
+ maxPosTopMargin = spacing->mMargin.top;
}
-
- nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin);
- nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin);
- return maxPos - maxNeg;
- } else {
- return 0;
+ nscoord maxPos = PR_MAX(aState.mPrevMaxPosBottomMargin, maxPosTopMargin);
+ nscoord maxNeg = PR_MAX(aState.mPrevMaxNegBottomMargin, maxNegTopMargin);
+ topMargin = maxPos - maxNeg;
+
+ // Save away bottom information for later promotion into aState
+ if (spacing->mMargin.bottom < 0) {
+ maxNegBottomMargin = -spacing->mMargin.bottom;
+ } else {
+ maxPosBottomMargin = spacing->mMargin.bottom;
+ }
+ break;
}
}
+
+ // Before we move the line, make sure that it will fit in it's new
+ // location. It always fits if the height isn't constrained or it's
+ // the first line.
+ nscoord totalHeight = topMargin + aLine->mBounds.height;
+ if (!aState.mUnconstrainedHeight && (aLine != mLines)) {
+ if (aState.mY + totalHeight > aState.mAvailSize.height) {
+ // The line will not fit
+ rv = PushLines(aState, aLine);
+ goto done;
+ }
+ }
+ if (isBlockLine) {
+ if (0 != topMargin) {
+ // We have to move the line now that we know the top margin
+ // for it.
+ aLine->MoveLineBy(0, topMargin);
+ aState.mY += topMargin;
+ }
+ }
+ else {
+ // Apply previous line's bottom margin before the inline-line.
+ nscoord bottomMargin = aState.mPrevMaxPosBottomMargin -
+ aState.mPrevMaxNegBottomMargin;
+ if (0 != bottomMargin) {
+ aLine->MoveLineBy(0, bottomMargin);
+ aState.mY += topMargin;
+ }
+ }
+
+ // Consume space and advance running values
+ aState.mY += aLine->mBounds.height;
+ aState.mPrevMaxNegBottomMargin = maxNegBottomMargin;
+ aState.mPrevMaxPosBottomMargin = maxPosBottomMargin;
+ if (nsnull != aState.mMaxElementSizePointer) {
+ nsSize* maxSize = aState.mMaxElementSizePointer;
+ if (aLineLayout.mReflowData.mMaxElementSize.width > maxSize->width) {
+ maxSize->width = aLineLayout.mReflowData.mMaxElementSize.width;
+ }
+ if (aLineLayout.mReflowData.mMaxElementSize.height > maxSize->height) {
+ maxSize->height = aLineLayout.mReflowData.mMaxElementSize.height;
+ }
+ }
+ {
+ nscoord xmost = aLine->mBounds.XMost();
+ if (xmost > aState.mKidXMost) {
+ aState.mKidXMost = xmost;
+ }
+ }
+
+ // Any below current line floaters to place?
+ if (aState.mPendingFloaters.Count() > 0) {
+ PlaceBelowCurrentLineFloaters(aState, aState.mY);
+ // XXX Factor in the height of the floaters as well when considering
+ // whether the line fits.
+ // The default policy is that if there isn't room for the floaters then
+ // both the line and the floaters are pushed to the next-in-flow...
+ }
+
+ if (aState.mY >= aState.mCurrentBand.availSpace.YMost()) {
+ // The current y coordinate is now past our available space
+ // rectangle. Get a new band of space.
+ GetAvailableSpace(aState, aState.mY);
+ }
+
+done:
+ return rv;
}
-void nsBlockFrame::PlaceBelowCurrentLineFloaters(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nscoord aY)
+// aY has borderpadding.top already factored in
+// aResult is relative to left,aY
+nsresult
+nsBlockFrame::GetAvailableSpace(nsBlockReflowState& aState, nscoord aY)
{
- NS_PRECONDITION(aState.floaterToDo.Count() > 0, "no floaters");
+ nsresult rv = NS_OK;
- // XXX Factor this code with PlaceFloater()...
- PRInt32 numFloaters = aState.floaterToDo.Count();
-
- for (PRInt32 i = 0; i < numFloaters; i++) {
- nsIFrame* floater = (nsIFrame*)aState.floaterToDo[i];
- nsRect region;
+ nsISpaceManager* sm = aState.mSpaceManager;
- // Get the band of available space
- // XXX This is inefficient to do this inside the loop...
- GetAvailableSpaceBand(aState, aY);
+ // Fill in band data for the specific Y coordinate
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->GetBandData(aY, aState.mAvailSize, aState.mCurrentBand);
+ sm->Translate(-aState.mBorderPadding.left, 0);
- // Get the type of floater
- nsIStyleContextPtr styleContext;
-
- floater->GetStyleContext(aCX, styleContext.AssignRef());
- nsStyleDisplay* sd = (nsStyleDisplay*)
- styleContext->GetData(kStyleDisplaySID);
-
- floater->GetRect(region);
- region.y = mCurrentState->currentBand->availSpace.y;
+ // Compute the bounding rect of the available space, i.e. space
+ // between any left and right floaters
+ aState.mCurrentBand.ComputeAvailSpaceRect();
- if (NS_STYLE_FLOAT_LEFT == sd->mFloats) {
- region.x = mCurrentState->currentBand->availSpace.x;
- } else {
- NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type");
- region.x = mCurrentState->currentBand->availSpace.XMost() - region.width;
- }
-
- // XXX Don't forget the floater's margins...
- mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left,
- 0);
- mCurrentState->spaceManager->AddRectRegion(region, floater);
-
- // Set the origin of the floater in world coordinates
- nscoord worldX, worldY;
-
- mCurrentState->spaceManager->GetTranslation(worldX, worldY);
- floater->MoveTo(region.x + worldX, region.y + worldY);
- mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0);
- }
- aState.floaterToDo.Clear();
-}
-
-/**
- * Flush a line out. Return true if the line fits in our available
- * height. If the line does not fit then return false. When the line
- * fits we advance the y coordinate, reset the x coordinate and
- * prepare the nsBlockReflowState for the next line.
- */
-PRBool nsBlockFrame::AdvanceToNextLine(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
- NS_PRECONDITION(aState.lineLength > 0, "bad line");
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
-
- nscoord y = aState.y + aState.topMargin;
- nscoord lineHeight;
-
- if (aState.isInline) {
- // Vertically align the children on this line, returning the height of
- // the line upon completion.
- lineHeight = nsCSSLayout::VerticallyAlignChildren(aCX, this,
- aState.styleFont,
- y,
- aState.lineStart,
- aState.lineLength,
- aState.ascents,
- aState.maxAscent);
-
- // Any below current line floaters to place?
- if (aState.floaterToDo.Count() > 0) {
- PlaceBelowCurrentLineFloaters(aCX, aState, y + lineHeight);
- // XXX Factor in the height of the floaters as well when considering
- // whether the line fits.
- // The default policy is that if there isn't room for the floaters then
- // both the line and the floaters are pushed to the next-in-flow...
- }
- } else {
- nsSize size;
-
- aState.lineStart->GetSize(size);
- lineHeight = size.height;
- }
-
- // The first line always fits
- if (aState.currentLineNumber > 0) {
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- if (y + lineHeight > yb) {
- // After vertical alignment of the children and factoring in the
- // proper margin, the line doesn't fit.
- return PR_FALSE;
- }
- }
-
- if (aState.isInline) {
- // Check if the right-edge of the line exceeds our running x-most
- nscoord xMost = aState.borderPadding.left + aState.lineWidth;
- if (xMost > aState.kidXMost) {
- aState.kidXMost = xMost;
- }
- }
-
- // Advance the y coordinate to the new position where the next
- // line or block element will go.
- aState.y = y + lineHeight;
- aState.x = 0;
-
- // Now that the vertical alignment is done we can perform horizontal
- // alignment and relative positioning. Skip all of these if we are
- // doing an unconstrained (in x) reflow. There's no point in doing
- // the work if we *know* we are going to reflowed again.
- if (!aState.unconstrainedWidth) {
- nsCSSLayout::HorizontallyPlaceChildren(aCX, this, aState.styleText,
- aState.lineStart, aState.lineLength,
- aState.lineWidth,
- aState.availSize.width);
-
- // Finally, now that the in-flow positions of the line's frames are
- // known we can apply relative positioning if any of them need it.
- if (!aState.justifying) {
- nsCSSLayout::RelativePositionChildren(aCX, this,
- aState.lineStart,
- aState.lineLength);
- }
- }
-
- // Record line length
- aState.lineLengths.AppendElement((void*)aState.lineLength);
-
- // Find the last frame in the line
- // XXX keep this as running state in the nsBlockReflowState
- nsIFrame* lastFrame = aState.lineStart;
- PRInt32 lineLen = aState.lineLength - 1;
- while (--lineLen >= 0) {
- lastFrame->GetNextSibling(lastFrame);
- }
-
- // Update maxElementSize
- if (nsnull != aState.maxElementSize) {
- nsSize& lineMax = aState.lineMaxElementSize;
- nsSize* maxMax = aState.maxElementSize;
- if (lineMax.width > maxMax->width) {
- maxMax->width = lineMax.width;
- }
- if (lineMax.height > maxMax->height) {
- maxMax->height = lineMax.height;
- }
- aState.lineMaxElementSize.width = 0;
- aState.lineMaxElementSize.height = 0;
- }
-
- // Advance to the next line
- aState.AdvanceToNextLine(lastFrame, lineHeight);
-
- return PR_TRUE;
-}
-
-/**
- * Add an inline child to the current line. Advance various running
- * values after placement.
- */
-void nsBlockFrame::AddInlineChildToLine(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsReflowMetrics& aKidSize,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC)
-{
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
-
- nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID);
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID);
-
- if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) {
- aState.needRelativePos = PR_TRUE;
- }
-
- // Place and size the child
- // XXX add in left margin from kid
- nsRect r;
- r.y = aState.y;
- r.width = aKidSize.width;
- r.height = aKidSize.height;
- if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) {
- // Left to right positioning.
- r.x = aState.borderPadding.left + aState.x + ss->mMargin.left;
- aState.x += aKidSize.width + ss->mMargin.left + ss->mMargin.right;
- } else {
- // Right to left positioning
- // XXX what should we do when aState.x goes negative???
- r.x = aState.x - aState.borderPadding.right - ss->mMargin.right -
- aKidSize.width;
- aState.x -= aKidSize.width + ss->mMargin.right + ss->mMargin.left;
- }
- aKidFrame->SetRect(r);
- aState.AddAscent(aKidSize.ascent);
- aState.lineWidth += aKidSize.width;
- aState.lineLength++;
-
- // Update maximums for the line
- if (aKidSize.ascent > aState.maxAscent) {
- aState.maxAscent = aKidSize.ascent;
- }
- if (aKidSize.descent > aState.maxDescent) {
- aState.maxDescent = aKidSize.descent;
- }
- // Update running margin maximums
- if (aState.firstChildIsInsideBullet && (aKidFrame == mFirstChild)) {
- // XXX temporary code. Since the molecule for the bullet frame
- // is the same as the LI frame, we get bad style information.
- // ignore it.
- } else {
- nscoord margin;
#if 0
- // XXX CSS2 spec says that top/bottom margin don't affect line height
- // calculation. We're waiting for clarification on this issue...
- if ((margin = ss->mMargin.top) < 0) {
- margin = -margin;
- if (margin > aState.maxNegTopMargin) {
- aState.maxNegTopMargin = margin;
- }
- } else {
- if (margin > aState.maxPosTopMargin) {
- aState.maxPosTopMargin = margin;
- }
- }
+ // XXX For now we assume that there are no height restrictions
+ // (e.g. no "float to bottom of column/page")
+ nsRect& availSpace = aState.mCurrentBand.availSpace;
+ aResult.x = availSpace.x;
+ aResult.y = availSpace.y;
+ aResult.width = availSpace.width;
+ aResult.height = aState.mAvailSize.height;
+#else
+ aState.mCurrentBand.availSpace.MoveBy(aState.mBorderPadding.left,
+ aState.mY);
#endif
- if ((margin = ss->mMargin.bottom) < 0) {
- margin = -margin;
- if (margin > aState.maxNegBottomMargin) {
- aState.maxNegBottomMargin = margin;
- }
- } else {
- if (margin > aState.maxPosBottomMargin) {
- aState.maxPosBottomMargin = margin;
- }
- }
- }
- // Update line max element size
- nsSize& mes = aState.lineMaxElementSize;
- if (nsnull != aKidMaxElementSize) {
- if (aKidMaxElementSize->width > mes.width) {
- mes.width = aKidMaxElementSize->width;
- }
- if (aKidMaxElementSize->height > mes.height) {
- mes.height = aKidMaxElementSize->height;
- }
- }
+ return rv;
}
-// Places and sizes the block-level element, and advances the line.
-// The rect is in the local coordinate space of the kid frame.
-void nsBlockFrame::AddBlockChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsRect& aKidRect,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC)
+// Give aLine and any successive lines to the block's next-in-flow; if
+// we don't have a next-in-flow then push all the children onto our
+// overflow list.
+nsresult
+nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineData* aLine)
{
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
+ PRInt32 i;
- nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID);
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID);
-
- if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) {
- aState.needRelativePos = PR_TRUE;
+ // Split our child-list in two; revert our last content offset and
+ // completion status to the previous line.
+ nsLineData* prevLine = aLine->mPrevLine;
+ NS_PRECONDITION(nsnull != prevLine, "pushing first line");
+ nsIFrame* prevKidFrame = prevLine->mFirstChild;
+ for (i = prevLine->mChildCount - 1; --i >= 0; ) {
+ prevKidFrame->GetNextSibling(prevKidFrame);
}
-
- // Translate from the kid's coordinate space to our coordinate space
- aKidRect.x += aState.borderPadding.left + ss->mMargin.left;
- aKidRect.y += aState.y + aState.topMargin;
-
- // Place and size the child
- aKidFrame->SetRect(aKidRect);
-
- aState.AddAscent(aKidRect.height);
- aState.lineLength++;
-
- // Is this the widest child frame?
- nscoord xMost = aKidRect.XMost() + ss->mMargin.right;
- if (xMost > aState.kidXMost) {
- aState.kidXMost = xMost;
- }
-
- // Update the max element size
- if (nsnull != aKidMaxElementSize) {
- if (aKidMaxElementSize->width > aState.maxElementSize->width) {
- aState.maxElementSize->width = aKidMaxElementSize->width;
- }
- if (aKidMaxElementSize->height > aState.maxElementSize->height) {
- aState.maxElementSize->height = aKidMaxElementSize->height;
- }
- }
-
- // and the bottom line margin information which we'll use when placing
- // the next child
- if (ss->mMargin.bottom < 0) {
- aState.maxNegBottomMargin = -ss->mMargin.bottom;
- } else {
- aState.maxPosBottomMargin = ss->mMargin.bottom;
- }
-
- // Update the running y-offset
- aState.y += aKidRect.height + aState.topMargin;
-
- // Apply relative positioning if necessary
- nsCSSLayout::RelativePositionChildren(aCX, this, aKidFrame, 1);
-
- // Advance to the next line
- aState.AdvanceToNextLine(aKidFrame, aKidRect.height);
-}
-
-/**
- * Compute the available size for reflowing the given child at the
- * current x,y position in the state. Note that this may return
- * negative or zero width/height's if we are out of room.
- */
-void nsBlockFrame::GetAvailSize(nsSize& aResult,
- nsBlockReflowState& aState,
- nsIStyleContext* aKidSC,
- PRBool aIsInline)
-{
- // Determine the maximum available reflow height for the child
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- aResult.height = aState.unconstrainedHeight ? NS_UNCONSTRAINEDSIZE :
- yb - aState.y - aState.topMargin;
-
- // Determine the maximum available reflow width for the child
- if (aState.unconstrainedWidth) {
- aResult.width = NS_UNCONSTRAINEDSIZE;
- } else if (aIsInline) {
- if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) {
- aResult.width = aState.currentBand->availSpace.XMost() - aState.x;
- } else {
- aResult.width = aState.x - aState.currentBand->availSpace.x;
- }
- } else {
- // It's a block. Don't adjust for the left/right margin here. That happens
- // later on once we know the current left/right edge
- aResult.width = aState.availSize.width;
- }
-}
-
-/**
- * Push all of the kids that we have not reflowed, starting at
- * aState.lineStart. aPrevKid is the kid previous to aState.lineStart
- * and is also our last child. Note that line length is NOT a
- * reflection of the number of children we are actually pushing
- * (because we don't break the sibling list as we add children to the
- * line).
- */
-void nsBlockFrame::PushKids(nsBlockReflowState& aState)
-{
- nsIFrame* prevFrame = aState.prevLineLastFrame;
- NS_PRECONDITION(nsnull != prevFrame, "pushing all kids");
#ifdef NS_DEBUG
- nsIFrame* nextSibling;
-
- prevFrame->GetNextSibling(nextSibling);
- NS_PRECONDITION(nextSibling == aState.lineStart, "bad prev line");
+ nsIFrame* nextFrame;
+ prevKidFrame->GetNextSibling(nextFrame);
+ NS_ASSERTION(nextFrame == aLine->mFirstChild, "bad line list");
#endif
+ prevKidFrame->SetNextSibling(nsnull);
+ prevLine->mNextLine = nsnull;
+ mLastContentOffset = prevLine->mLastContentOffset;
+ mLastContentIsComplete = prevLine->mLastContentIsComplete;
+
+ // Push children to our next-in-flow if we have, or to our overflow list
+ nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
+ PRInt32 pushCount = 0;
+ if (nsnull == nextInFlow) {
+ // Place children on the overflow list
+ mOverflowList = aLine->mFirstChild;
+
+ // Destroy the lines
+ nsLineData* line = aLine;
+ while (nsnull != line) {
+ pushCount += line->mChildCount;
+ nsLineData* next = line->mNextLine;
+ delete line;
+ line = next;
+ }
+ }
+ else {
+ aLine->mPrevLine = nsnull;
+
+ // Pass on the children to our next-in-flow
+ nsLineData* line = aLine;
+ prevLine = line;
+ nsIFrame* lastKid = aLine->mFirstChild;
+ nsIFrame* kid = lastKid;
+ while (nsnull != line) {
+ i = line->mChildCount;
+ pushCount += i;
+ NS_ASSERTION(kid == line->mFirstChild, "bad line list");
+ while (--i >= 0) {
+ kid->SetGeometricParent(nextInFlow);
+ nsIFrame* contentParent;
+ kid->GetContentParent(contentParent);
+ if (this == contentParent) {
+ kid->SetContentParent(nextInFlow);
+ }
+ lastKid = kid;
+ kid->GetNextSibling(kid);
+ }
+ prevLine = line;
+ line = line->mNextLine;
+ }
+
+ // Join the two line lists
+ nsLineData* nextInFlowLine = nextInFlow->mLines;
+ if (nsnull != nextInFlowLine) {
+ lastKid->SetNextSibling(nextInFlowLine->mFirstChild);
+ nextInFlowLine->mPrevLine = prevLine;
+ }
+ nsIFrame* firstKid = aLine->mFirstChild;
+ prevLine->mNextLine = nextInFlowLine;
+ nextInFlow->mLines = aLine;
+ nextInFlow->mFirstChild = firstKid;
+ nextInFlow->mChildCount += pushCount;
+ firstKid->GetIndexInParent(nextInFlow->mFirstContentOffset);
#ifdef NS_DEBUG
- PRInt32 numKids = LengthOf(mFirstChild);
- NS_ASSERTION(numKids == mChildCount, "bad child count");
+ nextInFlow->VerifyLines(PR_FALSE);
+#endif
+ }
+ mChildCount -= pushCount;
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return NS_LINE_LAYOUT_NOT_COMPLETE;
+}
+
+nsresult
+nsBlockFrame::ReflowMapped(nsBlockReflowState& aState)
+{
+ nsresult rv = NS_OK;
+
+ // Get some space to start reflowing with
+ GetAvailableSpace(aState, aState.mY);
+
+ nsLineData* prevLine = nsnull;
+ nsLineData* line = mLines;
+ nsLineLayout lineLayout(aState);
+ aState.mCurrentLine = &lineLayout;
+ while (nsnull != line) {
+ // Initialize the line layout for this line
+ rv = lineLayout.Initialize(aState, line);
+ if (NS_OK != rv) {
+ goto done;
+ }
+ lineLayout.mPrevKidFrame = aState.mPrevKidFrame;
+
+ // Reflow the line
+ nsresult lineReflowStatus = lineLayout.ReflowLine();
+ if (lineReflowStatus < 0) {
+ // Some kind of hard error
+ rv = lineReflowStatus;
+ goto done;
+ }
+ mChildCount += lineLayout.mNewFrames;
+
+ // Now place it. It's possible it won't fit.
+ rv = PlaceLine(aState, lineLayout, line);
+ if (NS_LINE_LAYOUT_COMPLETE != rv) {
+ goto done;
+ }
+
+ mLastContentOffset = line->mLastContentOffset;
+ mLastContentIsComplete = PRBool(line->mLastContentIsComplete);
+ prevLine = line;
+ line = line->mNextLine;
+ aState.mPrevKidFrame = lineLayout.mPrevKidFrame;
+ }
+
+done:
+ aState.mCurrentLine = nsnull;
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return rv;
+}
+
+nsresult
+nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState)
+{
+ nsresult rv = NS_OK;
+
+ // If we have no children and we have a prev-in-flow then we need to
+ // pick up where it left off. If we have children, e.g. we're being
+ // resized, then our content offset will have already been set
+ // correctly.
+ nsIFrame* kidPrevInFlow = nsnull;
+ if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
+ nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow;
+ mFirstContentOffset = prev->NextChildOffset();// XXX Is this necessary?
+ if (PR_FALSE == prev->mLastContentIsComplete) {
+ // Our prev-in-flow's last child is not complete
+ prev->LastChild(kidPrevInFlow);
+ }
+ }
+
+ // Get to the last line where the new content may be added
+ nsLineData* line = nsnull;
+ nsLineData* prevLine = nsnull;
+ if (nsnull != mLines) {
+ line = mLines;
+ while (nsnull != line->mNextLine) {
+ line = line->mNextLine;
+ }
+ prevLine = line;
+
+ // If the last line is not complete then kidPrevInFlow should be
+ // set to the last-line's last child.
+ if (!prevLine->mLastContentIsComplete) {
+ kidPrevInFlow = prevLine->GetLastChild();
+ }
+ }
+
+ // Get some space to start reflowing with
+ GetAvailableSpace(aState, aState.mY);
+
+ // Now reflow the new content until we are out of new content or out
+ // of vertical space.
+ PRInt32 kidIndex = NextChildOffset();
+ nsLineLayout lineLayout(aState);
+ aState.mCurrentLine = &lineLayout;
+ lineLayout.mKidPrevInFlow = kidPrevInFlow;
+ PRInt32 contentChildCount = mContent->ChildCount();
+ while (kidIndex < contentChildCount) {
+ if (nsnull == line) {
+ if (!MoreToReflow(aState)) {
+ break;
+ }
+ line = new nsLineData();
+ if (nsnull == line) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+ line->mFirstContentOffset = kidIndex;
+ }
+
+ // Initialize the line layout for this line
+ rv = lineLayout.Initialize(aState, line);
+ if (NS_OK != rv) {
+ goto done;
+ }
+ lineLayout.mKidPrevInFlow = kidPrevInFlow;
+ lineLayout.mPrevKidFrame = aState.mPrevKidFrame;
+
+ // Reflow the line
+ nsresult lineReflowStatus = lineLayout.ReflowLine();
+ if (lineReflowStatus < 0) {
+ // Some kind of hard error
+ rv = lineReflowStatus;
+ goto done;
+ }
+ mChildCount += lineLayout.mNewFrames;
+
+ // Add line to the block; do this before placing the line in case
+ // PushLines is needed.
+ if (nsnull == prevLine) {
+ // For the first line, initialize mFirstContentOffset
+ mFirstContentOffset = line->mFirstContentOffset;
+ mFirstChild = line->mFirstChild;
+ mLines = line;
+ }
+ else {
+ prevLine->mNextLine = line;
+ line->mPrevLine = prevLine;
+ }
+
+ // Now place it. It's possible it won't fit.
+ rv = PlaceLine(aState, lineLayout, line);
+ if (NS_LINE_LAYOUT_COMPLETE != rv) {
+ goto done;
+ }
+ kidIndex = lineLayout.mKidIndex;
+ kidPrevInFlow = lineLayout.mKidPrevInFlow;
+
+ mLastContentOffset = line->mLastContentOffset;
+ mLastContentIsComplete = PRBool(line->mLastContentIsComplete);
+ prevLine = line;
+ line = line->mNextLine;
+ aState.mPrevKidFrame = lineLayout.mPrevKidFrame;
+ }
+
+done:
+ aState.mCurrentLine = nsnull;
+ if (aState.mBlockIsPseudo) {
+ PropagateContentOffsets();
+ }
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return rv;
+}
+
+nsresult
+nsBlockFrame::InitializeState(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockReflowState& aState)
+{
+ nsresult rv;
+ rv = aState.Initialize(aPresContext, aSpaceManager,
+ aMaxSize, aMaxElementSize, this);
+
+ nsStyleSpacing* mySpacing = (nsStyleSpacing*)
+ mStyleContext->GetData(kStyleSpacingSID);
+
+ // Apply border and padding adjustments for regular frames only
+ if (!aState.mBlockIsPseudo) {
+ aState.mY = mySpacing->mBorderPadding.top;
+ aState.mX = mySpacing->mBorderPadding.left;
+ aState.mAvailSize.width -=
+ (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right);
+ aState.mAvailSize.height -=
+ (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom);
+ aState.mBorderPadding = mySpacing->mBorderPadding;
+ }
+ else {
+ aState.mBorderPadding.SizeTo(0, 0, 0, 0);
+ }
+
+ // Setup initial list ordinal value
+ nsIAtom* tag = mContent->GetTag();
+ if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
+ (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
+ nsHTMLValue value;
+ if (eContentAttr_HasValue ==
+ ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) {
+ if (eHTMLUnit_Integer == value.GetUnit()) {
+ aState.mNextListOrdinal = value.GetIntValue();
+ }
+ }
+ }
+ NS_RELEASE(tag);
+
+ return rv;
+}
+
+#if XXX
+NS_METHOD
+nsBlockFrame::SizeTo(nscoord aWidth, nscoord aHeight)
+{
+ printf("size to %g,%g\n", NS_TWIPS_TO_POINTS_FLOAT(aWidth),
+ NS_TWIPS_TO_POINTS_FLOAT(aHeight));
+ return nsHTMLContainerFrame::SizeTo(aWidth, aHeight);
+}
+
+NS_METHOD
+nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ aStatus = frComplete;
+ nsBlockReflowState state;
+ rv = InitializeState(aPresContext, aMaxSize, aMaxElementSize, state);
+ if (NS_OK == rv) {
+ nsRect desiredRect;
+ rv = DoResizeReflow(state, desiredRect, aStatus);
+ aDesiredSize.width = desiredRect.width;
+ aDesiredSize.height = desiredRect.height;
+ aDesiredSize.ascent = aDesiredSize.height;
+ aDesiredSize.descent = 0;
+ }
+ return rv;
+}
#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": push kids (childCount=%d)\n", mChildCount);
- DumpFlow();
-#endif
-
- PushChildren(aState.lineStart, prevFrame, mLastContentIsComplete);
- SetLastContentOffset(prevFrame);
-
- // Set mLastContentIsComplete to the previous lines last content is
- // complete now that the previous line's last child is our last
- // child.
- mLastContentIsComplete = aState.prevLineLastContentIsComplete;
-
- // Fix up child count
- // XXX is there a better way? aState.lineLength doesn't work because
- // we might be pushing more than just the pending line.
- nsIFrame* kid = mFirstChild;
- PRInt32 kids = 0;
- while (nsnull != kid) {
- kids++;
- kid->GetNextSibling(kid);
- }
- mChildCount = kids;
-
- // Make sure we have no lingering line data
- aState.lineLength = 0;
- aState.lineStart = nsnull;
-
-#ifdef NOISY
- ListTag(stdout);
- printf(": push kids done (childCount=%d) [%c]\n", mChildCount,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-}
-
-/**
- * Gets a band of available space starting at the specified y-offset. Assumes
- * the local coordinate space is currently set to the upper-left origin of the
- * bounding rect
- *
- * Updates "currentBand" and "x" member data of the block reflow state
- */
-void nsBlockFrame::GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY)
-{
- // Gets a band of available space.
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
- aState.spaceManager->GetBandData(aY, aState.availSize, *aState.currentBand);
-
- // Compute the bounding rect of the available space, i.e. space between any
- // left and right floaters
- aState.currentBand->ComputeAvailSpaceRect();
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
- aState.x = aState.currentBand->availSpace.x;
-}
-
-void nsBlockFrame::ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear)
-{
- // Translate the coordinate space
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
-
-getBand:
- nscoord y = aState.y + aState.topMargin;
- PRBool isLeftFloater = PR_FALSE;
- PRBool isRightFloater = PR_FALSE;
-
- // Get a band of available space
- aState.spaceManager->GetBandData(y, aState.availSize, *aState.currentBand);
-
- // Scan the trapezoids looking for left and right floaters
- nsBandTrapezoid* trapezoid = aState.currentBand->trapezoids;
- for (PRInt32 i = 0; i < aState.currentBand->count; i++) {
- // XXX Handle multiply occupied
- if (nsBandTrapezoid::smOccupied == trapezoid->state) {
- nsStyleDisplay* display;
-
- trapezoid->frame->GetStyleData(kStylePositionSID, (nsStyleStruct*&)display);
- if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
- isLeftFloater = PR_TRUE;
- } else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) {
- isRightFloater = PR_TRUE;
- }
- }
-
- trapezoid++;
- }
-
- if (isLeftFloater) {
- if ((aClear == NS_STYLE_CLEAR_LEFT) ||
- (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
- aState.y += aState.currentBand->trapezoids[0].GetHeight();
- goto getBand;
- }
- }
- if (isRightFloater) {
- if ((aClear == NS_STYLE_CLEAR_RIGHT) ||
- (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
- aState.y += aState.currentBand->trapezoids[0].GetHeight();
- goto getBand;
- }
- }
-
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
-}
-
-// Bit's for PlaceAndReflowChild return value
-#define PLACE_FIT 0x1
-#define PLACE_FLOWED 0x2
-
-PRIntn
-nsBlockFrame::PlaceAndReflowChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC)
-{
- nsSize kidMaxElementSize;
- nsSize* pKidMaxElementSize =
- (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull;
-
- // Get line start setup if we are at the start of a new line
- if (nsnull == aState.lineStart) {
- NS_ASSERTION(0 == aState.lineLength, "bad line length");
- aState.lineStart = aKidFrame;
- }
-
- // Get kid and its style
- nsStyleDisplay* styleDisplay = (nsStyleDisplay*)
- aKidSC->GetData(kStyleDisplaySID);
-
- // Figure out if kid is a block element or not
- PRBool isInline = PR_TRUE;
- PRIntn display = styleDisplay->mDisplay;
- if (aState.firstChildIsInsideBullet && (mFirstChild == aKidFrame)) {
- // XXX Special hack for properly reflowing bullets that have the
- // inside value for list-style-position.
- display = NS_STYLE_DISPLAY_INLINE;
- }
- if ((NS_STYLE_DISPLAY_BLOCK == display) ||
- (NS_STYLE_DISPLAY_LIST_ITEM == display)) {
- // Block elements always end up on the next line (unless they are
- // already at the start of the line).
- isInline = PR_FALSE;
- if (aState.lineLength > 0) {
- aState.breakAfterChild = PR_TRUE;
- }
- }
-
- // Handle forced break first
- if (aState.breakAfterChild) {
- NS_ASSERTION(aState.lineStart != aKidFrame, "bad line");
-
- // Get the last child in the current line
- nsIFrame* lastFrame = aState.lineStart;
- PRInt32 lineLen = aState.lineLength - 1;
- while (--lineLen >= 0) {
- lastFrame->GetNextSibling(lastFrame);
- }
-
- if (!AdvanceToNextLine(aCX, aState)) {
- // The previous line didn't fit.
- return 0;
- }
- aState.lineStart = aKidFrame;
-
- // Get the style for the last child, and see if it wanted to clear
- // floaters. This handles the BR tag, which is the only inline
- // element for which clear applies
- nsIStyleContextPtr lastChildSC;
-
- lastFrame->GetStyleContext(aCX, lastChildSC.AssignRef());
- nsStyleDisplay* lastChildDisplay = (nsStyleDisplay*)
- lastChildSC->GetData(kStyleDisplaySID);
- switch (lastChildDisplay->mBreakType) {
- case NS_STYLE_CLEAR_LEFT:
- case NS_STYLE_CLEAR_RIGHT:
- case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
- ClearFloaters(aState, lastChildDisplay->mBreakType);
- break;
- }
- }
-
- // Now that we've handled force breaks (and maybe called AdvanceToNextLine()
- // which checks), remember whether it's an inline frame
- aState.isInline = isInline;
-
- // If we're at the beginning of a line then compute the top margin that we
- // should use
- if (aState.lineStart == aKidFrame) {
- // Compute the top margin to use for this line
- aState.topMargin = GetTopMarginFor(aCX, aState, aKidFrame, aKidSC,
- aState.isInline);
-
- // If it's an inline element then get a band of available space
- //
- // XXX If we have a current band and there's unused space in that band
- // then avoid this call to get a band...
- if (aState.isInline) {
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
- }
- }
-
- // Compute the available space to reflow the child into and then
- // reflow it into that space.
- nsSize kidAvailSize;
- GetAvailSize(kidAvailSize, aState, aKidSC, aState.isInline);
- if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) {
- // No more room
- return 0;
- }
-
- ReflowStatus status;
-
- if (aState.isInline) {
- nsReflowMetrics kidSize;
-
- // Inline elements are never passed the space manager
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
-
- // For first children, we skip all the fit checks because we must
- // fit at least one child for a parent to figure what to do with us.
- if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) {
- NS_ASSERTION(nsnull != aState.lineStart, "bad line start");
-
- if (aKidFrame == aState.lineStart) {
- // Width always fits when we are at the logical left margin.
- // Just check the height.
- //
- // XXX This height check isn't correct now that we have bands of
- // available space...
- if (kidSize.height > kidAvailSize.height) {
- // It's too tall
- return PLACE_FLOWED;
- }
- } else {
- // Examine state and if the breakBeforeChild is set and we
- // aren't already on the new line, do the forcing now.
- // XXX Why aren't we doing this check BEFORE we resize reflow the child?
- if (aState.breakBeforeChild) {
- aState.breakBeforeChild = PR_FALSE;
- if (aKidFrame != aState.lineStart) {
- if (!AdvanceToNextLine(aCX, aState)) {
- // Flushing out the line failed.
- return PLACE_FLOWED;
- }
- aState.lineStart = aKidFrame;
-
- // Get a band of available space
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
-
- // Reflow child now that it has the line to itself
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE);
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
- }
- }
-
- // When we are not at the logical left margin then we need
- // to check the width first. If we are too wide then advance
- // to the next line and try reflowing again.
- if (kidSize.width > kidAvailSize.width) {
- // Too wide. Try next line
- if (!AdvanceToNextLine(aCX, aState)) {
- // Flushing out the line failed.
- return PLACE_FLOWED;
- }
- aState.lineStart = aKidFrame;
-
- // Get a band of available space
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
-
- // Reflow splittable children
- SplittableType isSplittable;
-
- aKidFrame->IsSplittable(isSplittable);
- if (isSplittable != frNotSplittable) {
- // Update size info now that we are on the next line. Then
- // reflow the child into the new available space.
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE);
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
-
- // If we just reflowed our last child then update the
- // mLastContentIsComplete state.
- nsIFrame* nextSibling;
-
- aKidFrame->GetNextSibling(nextSibling);
- if (nsnull == nextSibling) {
- // Use state from the reflow we just did
- mLastContentIsComplete = PRBool(status == frComplete);
- }
- }
-
- // XXX This height check isn't correct now that we have bands of
- // available space...
- if (kidSize.height > kidAvailSize.height) {
- // It's too tall on the next line
- return PLACE_FLOWED;
- }
- // It's ok if it's too wide on the next line.
- }
- }
- }
-
- // Add child to the line
- AddInlineChildToLine(aCX, aState, aKidFrame, kidSize,
- pKidMaxElementSize, aKidSC);
- } else {
- nsRect kidRect;
-
- // Does the block-level element want to clear any floaters that impact
- // it? Note that the clear property only applies to block-level elements
- // and the BR tag
- nsStyleDisplay* styleDisplay = (nsStyleDisplay*)
- aKidSC->GetData(kStyleDisplaySID);
- switch (styleDisplay->mBreakType) {
- case NS_STYLE_CLEAR_LEFT:
- case NS_STYLE_CLEAR_RIGHT:
- case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
- ClearFloaters(aState, styleDisplay->mBreakType);
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_FALSE);
- if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) {
- // No more room
- return 0;
- }
- break;
- }
-
- // Give the block its own local coordinate space.. Note: ReflowChild()
- // will adjust for the child's left/right margin after determining the
- // current left/right edge
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
- // Give the block-level element the opportunity to use the space manager
- status = ReflowChild(aKidFrame, aCX, aState.spaceManager,
- kidAvailSize, kidRect, pKidMaxElementSize);
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
-
- // For first children, we skip all the fit checks because we must
- // fit at least one child for a parent to figure what to do with us.
- if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) {
- // Block elements always fit horizontally (because they are
- // always placed at the logical left margin). Check to see if
- // the block fits vertically
- if (kidRect.YMost() > kidAvailSize.height) {
- // Nope
- return PLACE_FLOWED;
- }
- }
-
- // Add block child
- // XXX We need to set lastContentIsComplete here, because AddBlockChild()
- // calls AdvaneceToNextLine(). We need to restructure the flow of control,
- // and use a state machine...
- aState.lastContentIsComplete = PRBool(status == frComplete);
- AddBlockChild(aCX, aState, aKidFrame, kidRect, pKidMaxElementSize, aKidSC);
- }
-
- // If we just reflowed our last child then update the
- // mLastContentIsComplete state.
- nsIFrame* nextSibling;
-
- aKidFrame->GetNextSibling(nextSibling);
- if (nsnull == nextSibling) {
- // Use state from the reflow we just did
- mLastContentIsComplete = PRBool(status == frComplete);
- }
-
- aState.lastContentIsComplete = PRBool(status == frComplete);
- if (aState.isInline && (frNotComplete == status)) {
- // Since the inline child didn't complete its reflow we *know*
- // that a continuation of it can't possibly fit on the current
- // line. Therefore, set a flag in the state that will cause the
- // a line break before the next frame is placed.
- aState.breakAfterChild = PR_TRUE;
- }
-
- aState.reflowStatus = status;
- return PLACE_FLOWED | PLACE_FIT;
-}
-
-/**
- * Reflow the existing frames.
- *
- * @param aCX presentation context to use
- * @param aState in out parameter which tracks the state of
- * reflow for the block frame.
- * @return true if we successfully reflowed all the mapped children and false
- * otherwise, e.g. we pushed children to the next in flow
- */
PRBool
-nsBlockFrame::ReflowMappedChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n",
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-
- PRBool result = PR_TRUE;
- nsIFrame* kidFrame;
-
- for (kidFrame = mFirstChild; nsnull != kidFrame; ) {
-
- /* we get the kid's style from kidFrame's cached style context */
- nsIStyleContextPtr kidSC;
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
-
- // Attempt to place and reflow the child
-
- // XXX if child is not splittable and it fits just place it where
- // it is, otherwise advance to the next line and place it there if
- // possible
-
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- ReflowStatus status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // The child doesn't fit. Push it and any remaining children.
- PushKids(aState);
- result = PR_FALSE;
- goto push_done;
- }
-
- // Is the child complete?
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- if (frComplete == status) {
- // Yes, the child is complete
- NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
- } else {
- // No the child isn't complete
- if (nsnull == kidNextInFlow) {
- // The child doesn't have a next-in-flow so create a continuing
- // frame. This hooks the child into the flow
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
- NS_ASSERTION(nsnull != continuingFrame, "frame creation failed");
-
- // Add the continuing frame to the sibling list
- nsIFrame* nextSib;
-
- kidFrame->GetNextSibling(nextSib);
- continuingFrame->SetNextSibling(nextSib);
- kidFrame->SetNextSibling(continuingFrame);
- mChildCount++;
- }
-
- // Unlike the inline frame code we can't assume that we used
- // up all of our space because the child's reflow status is
- // frNotComplete. Instead, the child is probably split and
- // we need to reflow the continuations as well.
- }
-
- // Get the next child frame
- kidFrame->GetNextSibling(kidFrame);
- }
-
- push_done:
-#ifdef NS_DEBUG
- nsIFrame* lastChild;
- PRInt32 lastIndexInParent;
-
- LastChild(lastChild);
- lastChild->GetIndexInParent(lastIndexInParent);
- NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset");
-
- PRInt32 len = LengthOf(mFirstChild);
- NS_POSTCONDITION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
-
-#ifdef NOISY
- ListTag(stdout);
- printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n",
- (result ? "" : "NOT "),
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
- return result;
-}
-
-/* XXX:
- * In this method, we seem to be getting the style for the next child,
- * and checking that child's style for the child's display type.
- * The problem is that it's very inefficient to ResolveStyleFor(kid) here
- * and then just throw it away, when we end up doing a ResolveStyleFor(kid)
- * again when we actually create a frame for the content.
- * It would be nice to cache the resolved style, maybe in the reflow state?
- */
-PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX)
+nsBlockFrame::MoreToReflow(nsBlockReflowState& aState)
{
PRBool rv = PR_FALSE;
- if (IsPseudoFrame()) {
+ if (aState.mBlockIsPseudo) {
// Get the next content object that we would like to reflow
PRInt32 kidIndex = NextChildOffset();
nsIContentPtr kid = mContent->ChildAt(kidIndex);
if (kid.IsNotNull()) {
// Resolve style for the kid
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
+ nsIStyleContextPtr kidSC =
+ aState.mPresContext->ResolveStyleContextFor(kid, this);
nsStyleDisplay* kidStyleDisplay = (nsStyleDisplay*)
kidSC->GetData(kStyleDisplaySID);
switch (kidStyleDisplay->mDisplay) {
@@ -1305,640 +993,288 @@ PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX)
return rv;
}
-/**
- * Create new frames for content we haven't yet mapped
- *
- * @param aCX presentation context to use
- * @return frComplete if all content has been mapped and frNotComplete
- * if we should be continued
- */
-nsIFrame::ReflowStatus
-nsBlockFrame::ReflowAppendedChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
+nsBlockReflowState*
+nsBlockFrame::FindBlockReflowState(nsIPresContext* aPresContext,
+ nsIFrame* aFrame)
{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
- nsIFrame* kidPrevInFlow = nsnull;
- ReflowStatus result = frNotComplete;
-
- // If we have no children and we have a prev-in-flow then we need to pick
- // up where it left off. If we have children, e.g. we're being resized, then
- // our content offset should already be set correctly...
- if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
- nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow;
- NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow");
- mFirstContentOffset = prev->NextChildOffset();
- if (PR_FALSE == prev->mLastContentIsComplete) {
- // Our prev-in-flow's last child is not complete
- prev->LastChild(kidPrevInFlow);
- }
- }
-
- // Place our children, one at a time until we are out of children
- PRInt32 kidIndex = NextChildOffset();
- nsIFrame* kidFrame = nsnull;
- nsIFrame* prevKidFrame;
-
- LastChild(prevKidFrame);
- for (;;) {
- // Get the next content object
- nsIContentPtr kid = mContent->ChildAt(kidIndex);
- if (kid.IsNull()) {
- result = frComplete;
- break;
- }
-
- // Resolve style for the kid
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
- nsStylePosition* kidPosition = (nsStylePosition*)
- kidSC->GetData(kStylePositionSID);
- nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
- kidSC->GetData(kStyleDisplaySID);
-
- // Check whether it wants to floated or absolutely positioned
- if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) {
- AbsoluteFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- kidFrame->SetStyleContext(aCX,kidSC);
- } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
- PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- kidFrame->SetStyleContext(aCX,kidSC);
- } else if (nsnull == kidPrevInFlow) {
- // Create initial frame for the child
- nsIContentDelegate* kidDel;
- switch (kidDisplay->mDisplay) {
- case NS_STYLE_DISPLAY_BLOCK:
- case NS_STYLE_DISPLAY_LIST_ITEM:
- // Pseudo block frames do not contain other block elements
- // unless the block element would be the first child.
- if (IsPseudoFrame()) {
- // If we're being used as a pseudo frame, i.e. we map the same
- // content as our parent then we want to indicate we're complete;
- // otherwise we'll be continued and go on mapping children...
-
- // It better be true that we are not being asked to flow a
- // block element as our first child. That means the body
- // decided it needed a pseudo-frame when it shouldn't have.
- NS_ASSERTION(nsnull != mFirstChild, "bad body");
-
- result = frComplete;
- goto done;
- }
- // FALL THROUGH (and create frame)
-
- case NS_STYLE_DISPLAY_INLINE:
- kidDel = kid->GetDelegate(aCX);
- kidFrame = kidDel->CreateFrame(aCX, kid, kidIndex, this);
- NS_RELEASE(kidDel);
- break;
-
- default:
- NS_ASSERTION(nsnull == kidPrevInFlow, "bad prev in flow");
- nsFrame::NewFrame(&kidFrame, kid, kidIndex, this);
+ nsBlockReflowState* state = nsnull;
+ if (nsnull != aFrame) {
+ nsIFrame* parent;
+ aFrame->GetGeometricParent(parent);
+ while (nsnull != parent) {
+ nsBlockFrame* block;
+ nsresult rv = parent->QueryInterface(kBlockFrameCID, (void**) &block);
+ if (NS_OK == rv) {
+ nsIPresShell* shell = aPresContext->GetShell();
+ state = (nsBlockReflowState*) shell->GetCachedData(block);
+ NS_RELEASE(shell);
break;
}
- kidFrame->SetStyleContext(aCX,kidSC);
- } else {
- // Since kid has a prev-in-flow, use that to create the next
- // frame.
- kidPrevInFlow->CreateContinuingFrame(aCX, this, kidFrame);
+ parent->GetGeometricParent(parent);
}
-
- // Link child frame into the list of children. If the frame ends
- // up not fitting and getting pushed, the PushKids code will fixup
- // the child count for us.
- if (nsnull != prevKidFrame) {
-#ifdef NS_DEBUG
- nsIFrame* nextSibling;
-
- prevKidFrame->GetNextSibling(nextSibling);
- NS_ASSERTION(nsnull == nextSibling, "bad append");
-#endif
- prevKidFrame->SetNextSibling(kidFrame);
- } else {
- NS_ASSERTION(nsnull == mFirstChild, "bad create");
- mFirstChild = kidFrame;
- SetFirstContentOffset(kidFrame);
- }
- prevKidFrame = kidFrame;
- mChildCount++;
-
- // Reflow child frame as many times as necessary until it is
- // complete.
- ReflowStatus status;
- do {
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // We ran out of room.
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- mLastContentIsComplete = PRBool(nsnull == kidNextInFlow);
- PushKids(aState);
-
- goto push_done;
- }
-
- // Did the child complete?
- prevKidFrame = kidFrame;
- if (frNotComplete == status) {
- // Child didn't complete so create a continuing frame
- kidPrevInFlow = kidFrame;
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
-
- // Add the continuing frame to the sibling list
- nsIFrame* kidNextSibling;
-
- kidFrame->GetNextSibling(kidNextSibling);
- continuingFrame->SetNextSibling(kidNextSibling);
- kidFrame->SetNextSibling(continuingFrame);
- kidFrame = continuingFrame;
- mChildCount++;
-
- // Switch to new kid style
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
- }
-#ifdef NS_DEBUG
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- NS_ASSERTION(nsnull == kidNextInFlow, "huh?");
-#endif
- } while (frNotComplete == status);
-
- // The child that we just reflowed is complete
-#ifdef NS_DEBUG
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
-#endif
- kidIndex++;
- kidPrevInFlow = nsnull;
}
-
- done:
- // To get here we either completely reflowed all our appended
- // children OR we are a pseudo-frame and we ran into a block
- // element. In either case our last content MUST be complete.
- NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state");
- NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
- SetLastContentOffset(prevKidFrame);
-
- push_done:
-#ifdef NS_DEBUG
- PRInt32 len = LengthOf(mFirstChild);
- NS_ASSERTION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
- return result;
+ return state;
}
-/**
- * Pullup frames from our next in flow and try to place them. Before
- * this is called our previously mapped children, if any have been
- * reflowed which means that the block reflow state's x and y
- * coordinates and other data are ready to go.
- *
- * Return true if we pulled everything up.
- */
-PRBool
-nsBlockFrame::PullUpChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": pullup (childCount=%d) [%d,%d,%c]\n",
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-
- PRBool result = PR_TRUE;
- nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
- nsIFrame* prevKidFrame;
-
- LastChild(prevKidFrame);
- while (nsnull != nextInFlow) {
- // Get first available frame from the next-in-flow
- nsIFrame* kidFrame = PullUpOneChild(nextInFlow, prevKidFrame);
- if (nsnull == kidFrame) {
- // We've pulled up all the children from that next-in-flow, so
- // move to the next next-in-flow.
- nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
- continue;
- }
-
- // Get style information for the pulled up kid
- nsIContentPtr kid;
-
- kidFrame->GetContent(kid.AssignRef());
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
-
- ReflowStatus status;
- do {
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // Push the kids that didn't fit back down to the next-in-flow
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- mLastContentIsComplete = PRBool(nsnull == kidNextInFlow);
- PushKids(aState);
-
- result = PR_FALSE;
- goto push_done;
- }
-
- if (frNotComplete == status) {
- // Child is not complete
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- if (nsnull == kidNextInFlow) {
- // Create a continuing frame for the incomplete child
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
-
- // Add the continuing frame to our sibling list.
- nsIFrame* nextSibling;
-
- kidFrame->GetNextSibling(nextSibling);
- continuingFrame->SetNextSibling(nextSibling);
- kidFrame->SetNextSibling(continuingFrame);
- prevKidFrame = kidFrame;
- kidFrame = continuingFrame;
- mChildCount++;
-
- // Switch to new kid style
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
- } else {
- // The child has a next-in-flow, but it's not one of ours.
- // It *must* be in one of our next-in-flows. Collect it
- // then.
- NS_ASSERTION(!IsChild(kidNextInFlow), "busted kid next-in-flow");
- break;
- }
- }
- } while (frNotComplete == status);
-
- prevKidFrame = kidFrame;
- }
-
- if (nsnull != prevKidFrame) {
- // The only way we can get here is by pulling up every last child
- // in our next-in-flows (and reflowing any continunations they
- // have). Therefore we KNOW that our last child is complete.
- NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state");
- NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
- SetLastContentOffset(prevKidFrame);
- }
-
- push_done:;
-
- if (result == PR_FALSE) {
- // If our next-in-flow is empty OR our next next-in-flow is empty
- // then adjust the offsets of all of the empty next-in-flows.
- nextInFlow = (nsBlockFrame*) mNextInFlow;
- if ((0 == nextInFlow->mChildCount) ||
- ((nsnull != nextInFlow->mNextInFlow) &&
- (0 == ((nsBlockFrame*)(nextInFlow->mNextInFlow))->mChildCount))) {
- // We didn't pullup everything and we need to fixup one of our
- // next-in-flows content offsets.
- AdjustOffsetOfEmptyNextInFlows();
- }
- }
-
-
-#ifdef NS_DEBUG
- PRInt32 len = LengthOf(mFirstChild);
- NS_ASSERTION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n",
- (result ? "" : "NOT "),
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
- return result;
-}
-
-void nsBlockFrame::SetupState(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- const nsSize& aMaxSize,
- nsSize* aMaxElementSize,
- nsISpaceManager* aSpaceManager)
-{
- // Setup reflow state
- aState.Init(aMaxSize, aMaxElementSize, mStyleContext, aSpaceManager);
-
- nsStyleSpacing* mySpacing = (nsStyleSpacing*)
- mStyleContext->GetData(kStyleSpacingSID);
-
- // Apply border and padding adjustments for regular frames only
- if (PR_FALSE == IsPseudoFrame()) {
- aState.borderPadding = mySpacing->mBorderPadding;
- aState.y = mySpacing->mBorderPadding.top;
- aState.availSize.width -=
- (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right);
- aState.availSize.height -=
- (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom);
- } else {
- aState.borderPadding.SizeTo(0, 0, 0, 0);
- }
-
- // Setup initial list ordinal value
- nsIAtom* tag = mContent->GetTag();
- if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
- (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
- nsHTMLValue value;
- if (eContentAttr_HasValue ==
- ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) {
- if (eHTMLUnit_Integer == value.GetUnit()) {
- aState.nextListOrdinal = value.GetIntValue();
- }
- }
- }
- NS_RELEASE(tag);
-
- mCurrentState = &aState;
-}
-
-#include "nsUnitConversion.h"/* XXX */
-NS_METHOD nsBlockFrame::ResizeReflow(nsIPresContext* aCX,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsSize* aMaxElementSize,
- ReflowStatus& aStatus)
-{
- nsBlockReflowState state;
- SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager);
- return DoResizeReflow(aCX, state, aDesiredRect, aStatus);
-}
-
-nsresult nsBlockFrame::DoResizeReflow(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsRect& aDesiredRect,
- ReflowStatus& aStatus)
+nsresult
+nsBlockFrame::DoResizeReflow(nsBlockReflowState& aState,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ ReflowStatus& aStatus)
{
#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
PreReflowCheck();
#endif
-#ifdef NOISY
+#ifdef NOISY_REFLOW
ListTag(stdout);
- printf(": resize reflow %g,%g\n",
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width),
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height));
- DumpFlow();
+ printf(": Before:\n");
+ List(stdout, 1);
#endif
- // Zap old line data
- if (nsnull != mLines) {
- delete mLines;
- mLines = nsnull;
- }
- mNumLines = 0;
+ nsresult rv = NS_OK;
- // Check for an overflow list
- MoveOverflowToChildList();
-
- // Before we start reflowing, cache a pointer to our state structure
- // so that inline frames can find it.
- nsIPresShell* shell = aCX->GetShell();
+ nsIPresShell* shell = aState.mPresContext->GetShell();
shell->PutCachedData(this, &aState);
- // First reflow any existing frames
- PRBool reflowMappedOK = PR_TRUE;
- aStatus = frComplete;
- if (nsnull != mFirstChild) {
- reflowMappedOK = ReflowMappedChildren(aCX, aState);
- if (!reflowMappedOK) {
- aStatus = frNotComplete;
- }
+ // Check for an overflow list
+ DrainOverflowList();
+
+ if (nsnull != mLines) {
+ rv = ReflowMapped(aState);
}
- if (reflowMappedOK) {
- // Any space left?
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- if ((nsnull != mFirstChild) && (aState.y >= yb)) {
- // No space left. Don't try to pull-up children or reflow
- // unmapped. We need to return the correct completion status,
- // so see if there is more to reflow.
- if (MoreToReflow(aCX)) {
- aStatus = frNotComplete;
- }
- } else if (MoreToReflow(aCX)) {
- // Try and pull-up some children from a next-in-flow
- if ((nsnull == mNextInFlow) || PullUpChildren(aCX, aState)) {
- // If we still have unmapped children then create some new frames
- if (MoreToReflow(aCX)) {
- aStatus = ReflowAppendedChildren(aCX, aState);
- }
- } else {
- // We were unable to pull-up all the existing frames from the
- // next in flow
- aStatus = frNotComplete;
- }
+ if (NS_OK == rv) {
+ if ((nsnull != mLines) && (aState.mAvailSize.height <= 0)) {
+ // We are out of space
}
- }
-
- // Deal with last line - make sure it gets vertically and
- // horizontally aligned. This also updates the state's y coordinate
- // which is good because that's how we size ourselves.
- if (0 != aState.lineLength) {
- if (!AdvanceToNextLine(aCX, aState)) {
- // The last line of output doesn't fit. Push all of the kids to
- // the next in flow and change our reflow status to not complete
- // so that we are continued.
-#ifdef NOISY
- ListTag(stdout);
- printf(": pushing kids since last line doesn't fit\n");
-#endif
-
- PushKids(aState);
- aStatus = frNotComplete;
+ if (MoreToReflow(aState)) {
+ rv = ReflowUnmapped(aState);
}
}
- if (frComplete == aStatus) {
- // Don't forget to add in the bottom margin from our last child.
- // Only add it in if there is room for it.
- nscoord margin = aState.prevMaxPosBottomMargin -
- aState.prevMaxNegBottomMargin;
- nscoord y = aState.y + margin;
- if (y <= aState.borderPadding.top + aState.availSize.height) {
- aState.y = y;
- }
- }
-
- // Now that reflow has finished, remove the cached pointer
- shell->RemoveCachedData(this);
- NS_RELEASE(shell);
-
- // Translate state.lineLengths into an integer array
- mNumLines = aState.lineLengths.Count();
- if (mNumLines > 0) {
- mLines = new PRInt32[mNumLines];
- for (PRInt32 i = 0; i < mNumLines; i++) {
- PRInt32 ll = (PRInt32) aState.lineLengths.ElementAt(i);
- mLines[i] = ll;
- }
- }
-
- if (!aState.unconstrainedWidth && aState.justifying) {
- // Perform justification now that we know how many lines we have.
- JustifyLines(aCX, aState);
- }
-
// Return our desired rect and our status
aDesiredRect.x = 0;
aDesiredRect.y = 0;
- aDesiredRect.width = aState.kidXMost + aState.borderPadding.right;
- if (!aState.unconstrainedWidth) {
+ aDesiredRect.width = aState.mKidXMost + aState.mBorderPadding.right;
+ if (!aState.mUnconstrainedWidth) {
// Make sure we're at least as wide as the max size we were given
- nscoord maxWidth = aState.availSize.width + aState.borderPadding.left +
- aState.borderPadding.right;
-
+ nscoord maxWidth = aState.mAvailSize.width + aState.mBorderPadding.left +
+ aState.mBorderPadding.right;
if (aDesiredRect.width < maxWidth) {
aDesiredRect.width = maxWidth;
}
}
- aState.y += aState.borderPadding.bottom;
- aDesiredRect.height = aState.y;
-
-#ifdef NS_DEBUG
- PostReflowCheck(aStatus);
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": resize reflow %g,%g %scomplete [%d,%d,%c]\n",
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width),
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height),
- ((status == frNotComplete) ? "NOT " : ""),
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F')
- );
- DumpFlow();
-#endif
- mCurrentState = nsnull;
- return NS_OK;
-}
-
-void nsBlockFrame::JustifyLines(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
- // XXX we don't justify the last line; what if we are continued,
- // should we do it then?
- nsIFrame* kid = mFirstChild;
- for (PRInt32 i = 0; i < mNumLines; i++) {
- nsIFrame* lineStart = kid;
- PRInt32 lineLength = mLines[i];
- if (i < mNumLines - 1) {
- if (1 == lineLength) {
- // For lines with one element on them we can take a shortcut and
- // let them do the justification. Note that we still call
- // JustifyReflow even if the available space is zero in case the
- // child is a hunk of text that ends in whitespace. The whitespace
- // will be distributed elsewhere causing a proper flush right look
- // for the last word.
- nsRect r;
- kid->GetRect(r);
- nscoord maxWidth = aState.availSize.width;
- nscoord availableSpace = maxWidth - r.width;
- nscoord maxAvailSpace = nscoord(maxWidth * 0.1f);
- if ((availableSpace >= 0) && (availableSpace < maxAvailSpace)) {
- kid->JustifyReflow(aCX, availableSpace);
- kid->SizeTo(r.width + availableSpace, r.height);
- }
- kid->GetNextSibling(kid);
- } else {
- // XXX Get justification of multiple elements working
- while (--lineLength >= 0) {
- kid->GetNextSibling(kid);
- }
+ aState.mY += aState.mBorderPadding.bottom;
+ nscoord lastBottomMargin = aState.mPrevMaxPosBottomMargin -
+ aState.mPrevMaxNegBottomMargin;
+ if (!aState.mUnconstrainedHeight && (lastBottomMargin > 0)) {
+ // It's possible that we don't have room for the last bottom
+ // margin (the last bottom margin is the margin following a block
+ // element that we contain; it isn't applied immediately because
+ // of the margin collapsing logic). This can happen when we are
+ // reflowed in a limited amount of space because we don't know in
+ // advance what the last bottom margin will be.
+ nscoord maxY = aMaxSize.height;
+ if (aState.mY + lastBottomMargin > maxY) {
+ lastBottomMargin = maxY - aState.mY;
+ if (lastBottomMargin < 0) {
+ lastBottomMargin = 0;
}
}
-
- // Finally, now that the in-flow positions of the line's frames are
- // known we can apply relative positioning if any of them need it.
- nsCSSLayout::RelativePositionChildren(aCX, this, lineStart, mLines[i]);
+ aState.mY += lastBottomMargin;
}
-}
+ aDesiredRect.height = aState.mY;
-NS_METHOD nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const
-{
- aIsSplittable = frSplittableNonRectangular;
- return NS_OK;
-}
-
-NS_METHOD nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX,
- nsIFrame* aParent,
- nsIFrame*& aContinuingFrame)
-{
- nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent);
- PrepareContinuingFrame(aCX, aParent, cf);
- aContinuingFrame = cf;
- return NS_OK;
-}
-
-NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aCX,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsReflowCommand& aReflowCommand,
- ReflowStatus& aStatus)
-{
+ // Set return status
aStatus = frComplete;
-
- if (aReflowCommand.GetTarget() == this) {
- // XXX for now, just do a complete reflow mapped (it'll kinda
- // work, but it's slow)
-
- nsBlockReflowState state;
- SetupState(aCX, state, aMaxSize, nsnull, aSpaceManager);
- PRBool reflowMappedOK = ReflowMappedChildren(aCX, state);
- if (!reflowMappedOK) {
- aStatus = frNotComplete;
- }
- } else {
- // XXX not yet implemented
- NS_ABORT();
- // XXX work to compute initial state goes *HERE*
- aDesiredRect.x = 0;
- aDesiredRect.y = 0;
- aDesiredRect.width = 0;
- aDesiredRect.height = 0;
+ if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) {
+ rv = NS_OK;
+ aStatus = frNotComplete;
}
+#ifdef NS_DEBUG
+ // Verify that the line layout code pulled everything up when it
+ // indicates a complete reflow.
+ if (frComplete == aStatus) {
+ nsBlockFrame* nextBlock = (nsBlockFrame*) mNextInFlow;
+ while (nsnull != nextBlock) {
+ NS_ASSERTION((nsnull == nextBlock->mLines) &&
+ (nextBlock->mChildCount == 0) &&
+ (nsnull == nextBlock->mFirstChild),
+ "bad completion status");
+ nextBlock = (nsBlockFrame*) nextBlock->mNextInFlow;
+ }
- mCurrentState = nsnull;
- return NS_OK;
+#if XXX
+ // We better not be in the same parent frame as our prev-in-flow.
+ // If we are it means that we were continued instead of pulling up
+ // children.
+ if (nsnull != mPrevInFlow) {
+ nsIFrame* ourParent = mGeometricParent;
+ nsIFrame* prevParent = ((nsBlockFrame*)mPrevInFlow)->mGeometricParent;
+ NS_ASSERTION(ourParent != prevParent, "bad continuation");
+ }
+#endif
+ }
+#endif
+
+ // Now that reflow has finished, remove the cached pointer
+ shell->RemoveCachedData(this);
+ NS_RELEASE(shell);
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+ PostReflowCheck(aStatus);
+#endif
+#ifdef NOISY_REFLOW
+ ListTag(stdout);
+ printf(": After:\n");
+ List(stdout, 1);
+#endif
+ return rv;
}
-PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
+//----------------------------------------------------------------------
+
+NS_METHOD
+nsBlockFrame::ContentAppended(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentInserted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentReplaced(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aOldChild,
+ nsIContent* aNewChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentDeleted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::GetReflowMetrics(nsIPresContext* aPresContext,
+ nsReflowMetrics& aMetrics)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+NS_METHOD
+nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ nsIFrame::ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ aStatus = frComplete;
+ nsBlockReflowState state;
+ rv = InitializeState(aPresContext, aSpaceManager, aMaxSize,
+ aMaxElementSize, state);
+ if (NS_OK == rv) {
+ nsRect desiredRect;
+ rv = DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus);
+ }
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsReflowCommand& aReflowCommand,
+ nsIFrame::ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+PRBool
+nsBlockFrame::AddFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder)
+{
+ nsIPresShell* shell = aPresContext->GetShell();
+ nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this);
+ NS_RELEASE(shell);
+
+ if (nsnull != state) {
+ // Get the frame associated with the space manager, and get its
+ // nsIAnchoredItems interface
+ nsIFrame* frame = state->mSpaceManager->GetFrame();
+ nsIAnchoredItems* anchoredItems = nsnull;
+
+ frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems);
+ NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface");
+ if (nsnull != anchoredItems) {
+ anchoredItems->AddAnchoredItem(aFloater,
+ nsIAnchoredItems::anHTMLFloater,
+ this);
+ PlaceFloater(aPresContext, aFloater, aPlaceholder, *state);
+ return PR_TRUE;
+ }
+ }
+
+ return PR_FALSE;
+}
+
+void
+nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder)
+{
+ nsIPresShell* shell = aPresContext->GetShell();
+ nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this);
+ NS_RELEASE(shell);
+ if (nsnull != state) {
+ PlaceFloater(aPresContext, aFloater, aPlaceholder, *state);
+ }
+}
+
+PRBool
+nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
{
do {
nsIFrame* parent;
-
aFrame->GetGeometricParent(parent);
- // See if there are any non-zero sized child frames that precede aFrame
- // in the child list
+ // See if there are any non-zero sized child frames that precede
+ // aFrame in the child list
nsIFrame* child;
-
parent->FirstChild(child);
while ((nsnull != child) && (aFrame != child)) {
nsSize size;
@@ -1949,7 +1285,6 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
// We found a non-zero sized child frame that precedes aFrame
return PR_FALSE;
}
-
child->GetNextSibling(child);
}
@@ -1957,184 +1292,127 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
// Walk up one level and check that its parent is left-most as well
aFrame = parent;
} while (aFrame != this);
-
return PR_TRUE;
}
-PRBool nsBlockFrame::AddFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
- PlaceholderFrame* aPlaceholder)
-{
- // Get the frame associated with the space manager, and get its nsIAnchoredItems
- // interface
- nsIFrame* frame = mCurrentState->spaceManager->GetFrame();
- nsIAnchoredItems* anchoredItems = nsnull;
-
- frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems);
- NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface");
-
- if (nsnull != anchoredItems) {
- anchoredItems->AddAnchoredItem(aFloater, nsIAnchoredItems::anHTMLFloater, this);
- PlaceFloater(aCX, aFloater, aPlaceholder);
- return PR_TRUE;
- }
-
- return PR_FALSE;
-}
-
-// XXX The size of the floater needs to be taken into consideration if we're
-// computing a maximum element size
-void nsBlockFrame::PlaceFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
- PlaceholderFrame* aPlaceholder)
+void
+nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder,
+ nsBlockReflowState& aState)
{
// If the floater is the left-most non-zero size child frame then insert
// it before the current line; otherwise add it to the below-current-line
// todo list and we'll handle it when we flush out the line
if (IsLeftMostChild(aPlaceholder)) {
- // Get the type of floater
- nsIStyleContextPtr styleContext;
+ nsISpaceManager* sm = aState.mSpaceManager;
- aFloater->GetStyleContext(aCX, styleContext.AssignRef());
+ // Get the type of floater
+ nsIStyleContextPtr styleContext;
+ aFloater->GetStyleContext(aPresContext, styleContext.AssignRef());
nsStyleDisplay* floaterDisplay = (nsStyleDisplay*)
styleContext->GetData(kStyleDisplaySID);
- if (!mCurrentState->isInline) {
- // Get the current band for this line
- GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin);
- }
-
- // Commit some space in the space manager
- nsRect region;
-
+ // Commit some space in the space manager and adjust our current
+ // band of available space.
+ nsRect region;
aFloater->GetRect(region);
- region.y = mCurrentState->currentBand->availSpace.y;
-
+ region.y = aState.mY;
if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) {
- region.x = mCurrentState->currentBand->availSpace.x;
+ region.x = aState.mX;
} else {
- NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, "bad float type");
- region.x = mCurrentState->currentBand->availSpace.XMost() - region.width;
+ NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats,
+ "bad float type");
+ region.x = aState.mCurrentBand.availSpace.XMost() - region.width;
}
// XXX Don't forget the floater's margins...
- mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, 0);
- mCurrentState->spaceManager->AddRectRegion(region, aFloater);
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->AddRectRegion(region, aFloater);
// Set the origin of the floater in world coordinates
nscoord worldX, worldY;
-
- mCurrentState->spaceManager->GetTranslation(worldX, worldY);
+ sm->GetTranslation(worldX, worldY);
aFloater->MoveTo(region.x + worldX, region.y + worldY);
- mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0);
+ sm->Translate(-aState.mBorderPadding.left, 0);
- // Update the band of available space to reflect space taken up by the floater
- GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin);
+ // Update the band of available space to reflect space taken up by
+ // the floater
+ GetAvailableSpace(aState, aState.mY);
+ if (nsnull != aState.mCurrentLine) {
+ nsLineLayout& lineLayout = *aState.mCurrentLine;
+ lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace);
+ }
} else {
// Add the floater to our to-do list
- mCurrentState->floaterToDo.AppendElement(aFloater);
+ aState.mPendingFloaters.AppendElement(aFloater);
}
}
-NS_METHOD nsBlockFrame::ContentAppended(nsIPresShell* aShell,
- nsIPresContext* aPresContext,
- nsIContent* aContainer)
+void
+nsBlockFrame::PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState,
+ nscoord aY)
{
- // Get the last-in-flow
- nsBlockFrame* flow = (nsBlockFrame*)GetLastInFlow();
- PRInt32 kidIndex = flow->NextChildOffset();
- PRInt32 startIndex = kidIndex;
+ NS_PRECONDITION(aState.mPendingFloaters.Count() > 0, "no floaters");
-#if 0
- nsIFrame* kidFrame = nsnull;
- nsIFrame* prevKidFrame;
-
- flow->LastChild(prevKidFrame);
- for (;;) {
- // Get the next content object
- nsIContentPtr kid = mContent->ChildAt(kidIndex);
- if (nsnull == kid) {
- break;
- }
+ nsISpaceManager* sm = aState.mSpaceManager;
+ nsBlockBandData* bd = &aState.mCurrentBand;
- // Resolve style for the kid
- nsIStyleContextPtr kidSC = aPresContext->ResolveStyleContextFor(kid, this);
- nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
- kidSC->GetData(kStyleDisplaySID);
+ // XXX Factor this code with PlaceFloater()...
+ PRInt32 numFloaters = aState.mPendingFloaters.Count();
+ for (PRInt32 i = 0; i < numFloaters; i++) {
+ nsIFrame* floater = (nsIFrame*) aState.mPendingFloaters[i];
+ nsRect region;
- // Is it a floater?
- if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
- PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this);
+ // Get the band of available space
+ // XXX This is inefficient to do this inside the loop...
+ GetAvailableSpace(aState, aY);
+
+ // Get the type of floater
+ nsIStyleContextPtr styleContext;
+ floater->GetStyleContext(aState.mPresContext, styleContext.AssignRef());
+ nsStyleDisplay* sd = (nsStyleDisplay*)
+ styleContext->GetData(kStyleDisplaySID);
+
+ floater->GetRect(region);
+ region.y = bd->availSpace.y;
+ if (NS_STYLE_FLOAT_LEFT == sd->mFloats) {
+ region.x = bd->availSpace.x;
} else {
- // Create initial frame for the child
- nsIContentDelegate* kidDel;
- nsresult fr;
- switch (kidDisplay->mDisplay) {
- case NS_STYLE_DISPLAY_BLOCK:
- case NS_STYLE_DISPLAY_LIST_ITEM:
- // Pseudo block frames do not contain other block elements
- // unless the block element would be the first child.
- if (IsPseudoFrame()) {
- // Update last content offset now that we are done drawing
- // children from our parent.
- SetLastContentOffset(prevKidFrame);
-
- // It better be true that we are not being asked to flow a
- // block element as our first child. That means the body
- // decided it needed a pseudo-frame when it shouldn't have.
- NS_ASSERTION(nsnull != mFirstChild, "bad body");
-
- return NS_OK;
- }
- // FALL THROUGH (and create frame)
-
- case NS_STYLE_DISPLAY_INLINE:
- kidDel = kid->GetDelegate(aPresContext);
- kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this);
- NS_RELEASE(kidDel);
- break;
-
- default:
- fr = nsFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- break;
- }
+ NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type");
+ region.x = bd->availSpace.XMost() - region.width;
}
- kidFrame->SetStyleContext(aCX,kidSC);
- // Link child frame into the list of children
- if (nsnull != prevKidFrame) {
-#ifdef NS_DEBUG
- nsIFrame* nextSibling;
+ // XXX Don't forget the floater's margins...
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->AddRectRegion(region, floater);
- prevKidFrame->GetNextSibling(nextSibling);
- NS_ASSERTION(nsnull == nextSibling, "bad append");
-#endif
- prevKidFrame->SetNextSibling(kidFrame);
- } else {
- NS_ASSERTION(nsnull == mFirstChild, "bad create");
- mFirstChild = kidFrame;
- SetFirstContentOffset(kidFrame);
- }
- prevKidFrame = kidFrame;
- kidIndex++;
- mChildCount++;
+ // Set the origin of the floater in world coordinates
+ nscoord worldX, worldY;
+ sm->GetTranslation(worldX, worldY);
+ floater->MoveTo(region.x + worldX, region.y + worldY);
+ sm->Translate(-aState.mBorderPadding.left, 0);
}
- SetLastContentOffset(prevKidFrame);
-#endif
- // If this is a pseudo-frame then our parent will generate the
- // reflow command. Otherwise, if the container is us then we should
- // generate the reflow command because we were directly called.
- if (!IsPseudoFrame() && (aContainer == mContent)) {
- nsReflowCommand* rc =
- new nsReflowCommand(aPresContext, flow, nsReflowCommand::FrameAppended,
- startIndex);
- aShell->AppendReflowCommand(rc);
+ aState.mPendingFloaters.Clear();
+
+ // Pass on updated available space to the current line
+ if (nsnull != aState.mCurrentLine) {
+ nsLineLayout& lineLayout = *aState.mCurrentLine;
+ lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace);
}
- return NS_OK;
}
-PRIntn nsBlockFrame::GetSkipSides() const
+//----------------------------------------------------------------------
+
+nsLineData*
+nsBlockFrame::GetFirstLine()
+{
+ return mLines;
+}
+
+PRIntn
+nsBlockFrame::GetSkipSides() const
{
PRIntn skip = 0;
if (nsnull != mPrevInFlow) {
@@ -2145,40 +1423,3 @@ PRIntn nsBlockFrame::GetSkipSides() const
}
return skip;
}
-
-nsHTMLFrameType nsBlockFrame::GetFrameType() const
-{
- return eHTMLFrame_Block;
-}
-
-NS_METHOD nsBlockFrame::ListTag(FILE* out) const
-{
- if ((nsnull != mGeometricParent) && IsPseudoFrame()) {
- fprintf(out, "*block<");
- nsIAtom* atom = mContent->GetTag();
- if (nsnull != atom) {
- nsAutoString tmp;
- atom->ToString(tmp);
- fputs(tmp, out);
- }
- fprintf(out, ">(%d)@%p", mIndexInParent, this);
- } else {
- nsHTMLContainerFrame::ListTag(out);
- }
- return NS_OK;
-}
-
-#ifdef NS_DEBUG
-void nsBlockFrame::DumpFlow() const
-{
-#ifdef NOISY_FLOW
- nsBlockFrame* flow = (nsBlockFrame*) mNextInFlow;
- while (flow != 0) {
- printf(" %p: [%d,%d,%c]\n",
- flow, flow->mFirstContentOffset, flow->mLastContentOffset,
- (flow->mLastContentIsComplete ? 'T' : 'F'));
- flow = (nsBlockFrame*) flow->mNextInFlow;
- }
-#endif
-}
-#endif
diff --git a/layout/generic/nsBlockReflowState.h b/layout/generic/nsBlockReflowState.h
index 59d7407ec5b1..0228ca80d413 100644
--- a/layout/generic/nsBlockReflowState.h
+++ b/layout/generic/nsBlockReflowState.h
@@ -16,66 +16,37 @@
* Reserved.
*/
#include "nsBlockFrame.h"
-#include "nsSize.h"
-#include "nsIAnchoredItems.h"
-#include "nsIContent.h"
-#include "nsIContentDelegate.h"
-#include "nsISpaceManager.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
-#include "nsIPresContext.h"
-#include "nsMargin.h"
-#include "nsHTMLIIDs.h"
-#include "nsCSSLayout.h"
-#include "nsCRT.h"
-#include "nsIPresShell.h"
-#include "nsReflowCommand.h"
-#include "nsPlaceholderFrame.h"
-#include "nsHTMLAtoms.h"
-#include "nsHTMLValue.h"
#include "nsIHTMLContent.h"
-#include "nsAbsoluteFrame.h"
+#include "nsIPresContext.h"
+#include "nsIPresShell.h"
+#include "nsIAnchoredItems.h"
+#include "nsPlaceholderFrame.h"
#include "nsIPtr.h"
+#include "nsHTMLAtoms.h"
+#include "nsHTMLIIDs.h"
+#include "nsHTMLValue.h"
-#ifdef NS_DEBUG
-#undef NOISY
-#undef NOISY_FLOW
-#else
-#undef NOISY
-#undef NOISY_FLOW
-#endif
+// XXX what do we do with catastrophic errors (rv < 0)? What is the
+// state of the reflow world after such an error?
-static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID);
-static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID);
-static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID);
+#undef NOISY_REFLOW
static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID);
-static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID);
-static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID);
-static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID);
static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID);
-NS_DEF_PTR(nsIStyleContext);
+static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID);
+static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID);
+static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID);
+
NS_DEF_PTR(nsIContent);
+NS_DEF_PTR(nsIStyleContext);
-struct BlockBandData : public nsBandData {
- nsBandTrapezoid data[12];
+//----------------------------------------------------------------------
- // Bounding rect of available space between any left and right floaters
- nsRect availSpace;
- // Constructor
- BlockBandData() {size = 12; trapezoids = data;}
-
- // Computes the bounding rect of the available space, i.e. space between
- // any left and right floaters
- //
- // Uses the current trapezoid data, see nsISpaceManager::GetBandData().
- // Updates member data "availSpace"
- void ComputeAvailSpaceRect();
-};
-
-void BlockBandData::ComputeAvailSpaceRect()
+void nsBlockBandData::ComputeAvailSpaceRect()
{
nsBandTrapezoid* trapezoid = data;
@@ -121,198 +92,60 @@ void BlockBandData::ComputeAvailSpaceRect()
}
}
-// XXX Bugs
-// 1. right to left reflow can generate negative x coordinates.
-
-// XXX Speedup idea (all containers)
-
-// If I reflow a child and it gives back not-complete status then
-// there is no sense in trying to pullup children. For blocks, it's a
-// little more complicated unless the child is a block - if the child
-// is a block, then we must be out of room hence we should stop. If
-// the child is not a block then our line should be flushed (see #2
-// below) if our line is already empty then we must be out of room.
-
-// For inline frames and column frames, if we reflow a child and get
-// back not-complete status then we should bail immediately because we
-// are out of room.
-
-// XXX Speedup ideas:
-// 1. change pullup code to use line information from next in flow
-// 2. we can advance to next line immediately after reflowing something
-// and noticing that it's not complete.
-// 3. pass down last child information in aState so that pullup, etc.,
-// don't need to recompute it
-
-// XXX TODO:
-// 0. Move justification into line flushing code
-
-// 1. To get ebina margins I need "auto" information from the style
-// system margin's. A bottom/top margin of auto will then be computed like
-// ebina computes it [however the heck that is].
-
-// 2. kicking out floaters and talking with floater container to adjust
-// left and right margins
+//----------------------------------------------------------------------
nsBlockReflowState::nsBlockReflowState()
{
}
-void nsBlockReflowState::Init(const nsSize& aMaxSize,
- nsSize* aMaxElementSize,
- nsIStyleContext* aBlockSC,
- nsISpaceManager* aSpaceManager)
+nsBlockReflowState::~nsBlockReflowState()
{
- firstLine = PR_TRUE;
- allowLeadingWhitespace = PR_FALSE;
- breakAfterChild = PR_FALSE;
- breakBeforeChild = PR_FALSE;
- firstChildIsInsideBullet = PR_FALSE;
- nextListOrdinal = -1;
- column = 0;
+}
- spaceManager = aSpaceManager;
- currentBand = new BlockBandData;
+nsresult
+nsBlockReflowState::Initialize(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockFrame* aBlock)
+{
+ nsresult rv = NS_OK;
- styleContext = aBlockSC;
- styleText = (nsStyleText*) aBlockSC->GetData(kStyleTextSID);
- styleFont = (nsStyleFont*) aBlockSC->GetData(kStyleFontSID);
- styleDisplay = (nsStyleDisplay*) aBlockSC->GetData(kStyleDisplaySID);
+ mPresContext = aPresContext;
+ mBlock = aBlock;
+ mSpaceManager = aSpaceManager;
+ mBlockIsPseudo = aBlock->IsPseudoFrame();
+ mCurrentLine = nsnull;
+ mPrevKidFrame = nsnull;
- justifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
- (NS_STYLE_WHITESPACE_PRE != styleText->mWhiteSpace);
-
- availSize.width = aMaxSize.width;
- availSize.height = aMaxSize.height;
- maxElementSize = aMaxElementSize;
+ mX = 0;
+ mY = 0;
+ mAvailSize = aMaxSize;
+ mUnconstrainedWidth = PRBool(mAvailSize.width == NS_UNCONSTRAINEDSIZE);
+ mUnconstrainedHeight = PRBool(mAvailSize.height == NS_UNCONSTRAINEDSIZE);
+ mMaxElementSizePointer = aMaxElementSize;
if (nsnull != aMaxElementSize) {
aMaxElementSize->width = 0;
aMaxElementSize->height = 0;
}
+ mKidXMost = 0;
- kidXMost = 0;
- x = 0;
- y = 0;
+ mPrevMaxPosBottomMargin = 0;
+ mPrevMaxNegBottomMargin = 0;
- isInline = PR_FALSE;
- currentLineNumber = 0;
- lineStart = nsnull;
- lineLength = 0;
- ascents = ascentBuf;
- maxAscent = 0;
- maxDescent = 0;
- lineWidth = 0;
- maxPosBottomMargin = 0;
- maxNegBottomMargin = 0;
- lineMaxElementSize.width = 0;
- lineMaxElementSize.height = 0;
- lastContentIsComplete = PR_TRUE;
+ mNextListOrdinal = -1;
+ mFirstChildIsInsideBullet = PR_FALSE;
- maxAscents = sizeof(ascentBuf) / sizeof(ascentBuf[0]);
- needRelativePos = PR_FALSE;
-
- prevLineLastFrame = nsnull;
- prevLineHeight = 0;
- topMargin = 0;
- prevMaxPosBottomMargin = 0;
- prevMaxNegBottomMargin = 0;
- prevLineLastContentIsComplete = PR_TRUE;
-
- unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE);
- unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE);
-
- reflowStatus = nsIFrame::frNotComplete;
+ return rv;
}
-nsBlockReflowState::~nsBlockReflowState()
-{
- if (ascents != ascentBuf) {
- delete ascents;
- }
- delete currentBand;
-}
-
-void nsBlockReflowState::AddAscent(nscoord aAscent)
-{
- NS_PRECONDITION(lineLength <= maxAscents, "bad line length");
- if (lineLength == maxAscents) {
- maxAscents *= 2;
- nscoord* newAscents = new nscoord[maxAscents];
- if (nsnull != newAscents) {
- nsCRT::memcpy(newAscents, ascents, sizeof(nscoord) * lineLength);
- if (ascents != ascentBuf) {
- delete ascents;
- }
- ascents = newAscents;
- } else {
- // Yikes! Out of memory!
- return;
- }
- }
- ascents[lineLength] = aAscent;
-}
-
-void nsBlockReflowState::AdvanceToNextLine(nsIFrame* aPrevLineLastFrame,
- nscoord aPrevLineHeight)
-{
- firstLine = PR_FALSE;
- allowLeadingWhitespace = PR_FALSE;
- column = 0;
- breakAfterChild = PR_FALSE;
- breakBeforeChild = PR_FALSE;
- lineStart = nsnull;
- lineLength = 0;
- currentLineNumber++;
- maxAscent = 0;
- maxDescent = 0;
- lineWidth = 0;
- needRelativePos = PR_FALSE;
-
- prevLineLastFrame = aPrevLineLastFrame;
- prevLineHeight = aPrevLineHeight;
- prevMaxPosBottomMargin = maxPosBottomMargin;
- prevMaxNegBottomMargin = maxNegBottomMargin;
-
- // Remember previous line's lastContentIsComplete
- prevLineLastContentIsComplete = lastContentIsComplete;
- lastContentIsComplete = PR_TRUE;
-
- topMargin = 0;
- maxPosBottomMargin = 0;
- maxNegBottomMargin = 0;
-}
-
-#ifdef NS_DEBUG
-void nsBlockReflowState::DumpLine()
-{
- nsIFrame* f = lineStart;
- PRInt32 ll = lineLength;
- while (--ll >= 0) {
- printf(" ");
- ((nsFrame*)f)->ListTag(stdout);/* XXX */
- printf("\n");
- f->GetNextSibling(f);
- }
-}
-
-void nsBlockReflowState::DumpList()
-{
- nsIFrame* f = lineStart;
- while (nsnull != f) {
- printf(" ");
- ((nsFrame*)f)->ListTag(stdout);/* XXX */
- printf("\n");
- f->GetNextSibling(f);
- }
-}
-#endif
-
//----------------------------------------------------------------------
-nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
- nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent)
+nsresult
+nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
+ nsIContent* aContent,
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
if (nsnull == aInstancePtrResult) {
@@ -327,963 +160,818 @@ nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
}
nsBlockFrame::nsBlockFrame(nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent)
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent)
: nsHTMLContainerFrame(aContent, aIndexInParent, aParent)
{
}
nsBlockFrame::~nsBlockFrame()
{
- if (nsnull != mLines) {
- delete mLines;
- }
+ DestroyLines();
}
-nsresult
+void nsBlockFrame::DestroyLines()
+{
+}
+
+NS_METHOD
nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
NS_PRECONDITION(0 != aInstancePtr, "null ptr");
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
}
- if (aIID.Equals(kIHTMLFrameTypeIID)) {
- *aInstancePtr = (void*) ((nsIHTMLFrameType*) this);
+ if (aIID.Equals(kBlockFrameCID)) {
+ *aInstancePtr = (void*) (this);
return NS_OK;
- } else if (aIID.Equals(kIRunaroundIID)) {
+ }
+ else if (aIID.Equals(kIRunaroundIID)) {
*aInstancePtr = (void*) ((nsIRunaround*) this);
return NS_OK;
- } else if (aIID.Equals(kIFloaterContainerIID)) {
+ }
+ else if (aIID.Equals(kIFloaterContainerIID)) {
*aInstancePtr = (void*) ((nsIFloaterContainer*) this);
return NS_OK;
}
return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
}
-// Computes the top margin to use for this child frames based on its display
-// type and the display type of the previous child frame.
-//
-// Adjacent vertical margins between block-level elements are collapsed.
-nscoord nsBlockFrame::GetTopMarginFor(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC,
- PRBool aIsInline)
+NS_METHOD
+nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const
{
- if (aIsInline) {
- // Just use whatever the previous bottom margin was
- return aState.prevMaxPosBottomMargin - aState.prevMaxNegBottomMargin;
+ aIsSplittable = frSplittableNonRectangular;
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX,
+ nsIFrame* aParent,
+ nsIFrame*& aContinuingFrame)
+{
+ nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent);
+ PrepareContinuingFrame(aCX, aParent, cf);
+ aContinuingFrame = cf;
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::ListTag(FILE* out) const
+{
+ if ((nsnull != mGeometricParent) && IsPseudoFrame()) {
+ fprintf(out, "*block<");
+ nsIAtom* atom = mContent->GetTag();
+ if (nsnull != atom) {
+ nsAutoString tmp;
+ atom->ToString(tmp);
+ fputs(tmp, out);
+ }
+ fprintf(out, ">(%d)@%p", mIndexInParent, this);
} else {
- // Does the frame have a prev-in-flow?
- nsIFrame* kidPrevInFlow;
+ nsHTMLContainerFrame::ListTag(out);
+ }
+ return NS_OK;
+}
- aKidFrame->GetPrevInFlow(kidPrevInFlow);
+NS_METHOD
+nsBlockFrame::List(FILE* out, PRInt32 aIndent) const
+{
+ // Indent
+ for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
- if (nsnull == kidPrevInFlow) {
+ // Output the tag
+ ListTag(out);
+
+ // Output the first/last content offset
+ fprintf(out, "[%d,%d,%c] pif=%p nif=%p",
+ mFirstContentOffset, mLastContentOffset,
+ (mLastContentIsComplete ? 'T' : 'F'),
+ mPrevInFlow, mNextInFlow);
+
+ // Output the rect
+ out << mRect;
+
+ // Output the children, one line at a time
+ if (nsnull != mLines) {
+ fputs("<\n", out);
+ aIndent++;
+
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ line->List(out, aIndent);
+ line = line->mNextLine;
+ }
+
+ aIndent--;
+ for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
+ fputs(">\n", out);
+ } else {
+ fputs("<>\n", out);
+ }
+
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::VerifyTree() const
+{
+ nsresult rv = nsHTMLContainerFrame::VerifyTree();
+ if (NS_OK != rv) {
+ return rv;
+ }
+ rv = VerifyLines(PR_TRUE);
+ return rv;
+}
+
+nsresult
+nsBlockFrame::VerifyLines(PRBool aFinalCheck) const
+{
+ nsresult rv = NS_OK;
+
+ // Make sure that the list of children agrees with our child count.
+ // If this is not the case then the child list and the line list are
+ // not properly arranged.
+ PRInt32 len = LengthOf(mFirstChild);
+ NS_ASSERTION(mChildCount == len, "bad child list");
+
+ // Verify that our lines are correctly setup
+ PRInt32 offset = mFirstContentOffset;
+ PRInt32 lineChildCount = 0;
+ nsLineData* line = mLines;
+ nsLineData* prevLine = nsnull;
+ while (nsnull != line) {
+ if (aFinalCheck) {
+ NS_ASSERTION(((offset == line->mFirstContentOffset) &&
+ (line->mFirstContentOffset <= line->mLastContentOffset)),
+ "bad line mFirstContentOffset");
+ NS_ASSERTION(line->mLastContentOffset <= mLastContentOffset,
+ "bad line mLastContentOffset");
+ offset = line->mLastContentOffset;
+ if (line->mLastContentIsComplete) {
+ offset++;
+ }
+ }
+ lineChildCount += line->mChildCount;
+ rv = line->Verify(aFinalCheck);
+ if (NS_OK != rv) {
+ return rv;
+ }
+ prevLine = line;
+ line = line->mNextLine;
+ }
+ if (aFinalCheck && (nsnull != prevLine)) {
+ NS_ASSERTION(prevLine->mLastContentOffset == mLastContentOffset,
+ "bad mLastContentOffset");
+ NS_ASSERTION(prevLine->mLastContentIsComplete == mLastContentIsComplete,
+ "bad mLastContentIsComplete");
+ }
+ NS_ASSERTION(lineChildCount == mChildCount, "bad line counts");
+
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+// Remove a next-in-flow from from this block's list of lines
+
+// XXX problems here:
+// 1. we always have to start from the first line: slow!
+// 2. we can avoid this when the child is not the last child in a line
+
+void
+nsBlockFrame::WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow)
+{
+ // When a reflow indicates completion it's possible that
+ // next-in-flows were just removed. We have to remove them from any
+ // nsLineData's that follow the current line.
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ if (line->mFirstChild == aNextInFlow) {
+ // Remove child from line.
+ if (0 == --line->mChildCount) {
+ line->mFirstChild = nsnull;
+ }
+ else {
+ // Fixup the line
+ nsIFrame* nextKid;
+ aNextInFlow->GetNextSibling(nextKid);
+ line->mFirstChild = nextKid;
+ nextKid->GetIndexInParent(line->mFirstContentOffset);
+ }
+ break;
+ }
+ line = line->mNextLine;
+ }
+}
+
+nsresult
+nsBlockFrame::ReflowInlineChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ aStatus = ReflowChild(aKidFrame, aPresContext, aDesiredSize, aMaxSize,
+ aMaxElementSize);
+ return NS_OK;
+}
+
+nsresult
+nsBlockFrame::ReflowBlockChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ aStatus = ReflowChild(aKidFrame, aPresContext, aSpaceManager,
+ aMaxSize, aDesiredRect, aMaxElementSize);
+ return NS_OK;
+}
+
+nsLineData*
+nsBlockFrame::CreateLineForOverflowList(nsIFrame* aOverflowList)
+{
+ nsLineData* newLine = new nsLineData();
+ if (nsnull != newLine) {
+ nsIFrame* kid = aOverflowList;
+ newLine->mFirstChild = kid;
+ kid->GetIndexInParent(newLine->mFirstContentOffset);
+ newLine->mLastContentOffset = -1;
+ newLine->mLastContentIsComplete = PRPackedBool(0x255);
+ PRInt32 kids = 0;
+ while (nsnull != kid) {
+ kids++;
+ kid->GetNextSibling(kid);
+ }
+ newLine->mChildCount = kids;
+ }
+ return newLine;
+}
+
+void
+nsBlockFrame::DrainOverflowList()
+{
+ nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow;
+ if (nsnull != prevBlock) {
+ nsIFrame* overflowList = prevBlock->mOverflowList;
+ if (nsnull != overflowList) {
+ NS_ASSERTION(nsnull == mFirstChild, "bad overflow list");
+ NS_ASSERTION(nsnull == mLines, "bad overflow list");
+
+ // Create a line to hold the entire overflow list
+ nsLineData* newLine = CreateLineForOverflowList(overflowList);
+
+ // Place the children on our child list; this also reassigns
+ // their geometric parent and updates our mChildCount.
+ AppendChildren(overflowList);
+ prevBlock->mOverflowList = nsnull;
+
+ // The new line is the first line
+ mLines = newLine;
+ }
+ }
+
+ if (nsnull != mOverflowList) {
+ NS_ASSERTION(nsnull != mFirstChild,
+ "overflow list but no mapped children");
+
+ // Create a line to hold the overflow list
+ nsLineData* newLine = CreateLineForOverflowList(mOverflowList);
+
+ // Place the children on our child list; this also reassigns
+ // their geometric parent and updates our mChildCount.
+ AppendChildren(mOverflowList, PR_FALSE);
+ mOverflowList = nsnull;
+
+ // The new line is appended after our other lines
+ nsLineData* prevLine = nsnull;
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ prevLine = line;
+ line = line->mNextLine;
+ }
+ if (nsnull == prevLine) {
+ mLines = newLine;
+ }
+ else {
+ prevLine->mNextLine = newLine;
+ newLine->mPrevLine = prevLine;
+ }
+ }
+#ifdef NS_DEBUG
+ VerifyLines(PR_FALSE);
+#endif
+}
+
+// XXX add in code here that notices if margin's were not provided by
+// the style system and when that is the case to apply the old layout
+// engines margin calculations.
+
+nsresult
+nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
+ nsLineLayout& aLineLayout,
+ nsLineData* aLine)
+{
+ nsresult rv = NS_LINE_LAYOUT_COMPLETE;
+
+ nscoord topMargin = 0;
+ nscoord bottomMargin = 0;
+ nscoord maxNegBottomMargin = 0;
+ nscoord maxPosBottomMargin = 0;
+
+ // See if block margins apply to this line or not
+ PRBool isBlockLine = PR_FALSE;
+ if (1 == aLine->mChildCount) {
+ nsIStyleContextPtr kidSC;
+ nsIFrame* kid = aLine->mFirstChild;
+ rv = kid->GetStyleContext(aState.mPresContext, kidSC.AssignRef());
+ if (NS_OK != rv) return rv;
+
+ nsStyleDisplay* display = (nsStyleDisplay*)
+ kidSC->GetData(kStyleDisplaySID);
+ switch (display->mDisplay) {
+ case NS_STYLE_DISPLAY_BLOCK:
+ case NS_STYLE_DISPLAY_LIST_ITEM:
+ isBlockLine = PR_TRUE;
+ nsStyleSpacing* spacing = (nsStyleSpacing*)
+ kidSC->GetData(kStyleSpacingSID);
+
+ // Calculate top margin by collapsing with previous bottom margin
+ // if any.
nscoord maxNegTopMargin = 0;
nscoord maxPosTopMargin = 0;
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- if (ss->mMargin.top < 0) {
- maxNegTopMargin = -ss->mMargin.top;
+ if (spacing->mMargin.top < 0) {
+ maxNegTopMargin = -spacing->mMargin.top;
} else {
- maxPosTopMargin = ss->mMargin.top;
+ maxPosTopMargin = spacing->mMargin.top;
}
-
- nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin);
- nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin);
- return maxPos - maxNeg;
- } else {
- return 0;
+ nscoord maxPos = PR_MAX(aState.mPrevMaxPosBottomMargin, maxPosTopMargin);
+ nscoord maxNeg = PR_MAX(aState.mPrevMaxNegBottomMargin, maxNegTopMargin);
+ topMargin = maxPos - maxNeg;
+
+ // Save away bottom information for later promotion into aState
+ if (spacing->mMargin.bottom < 0) {
+ maxNegBottomMargin = -spacing->mMargin.bottom;
+ } else {
+ maxPosBottomMargin = spacing->mMargin.bottom;
+ }
+ break;
}
}
+
+ // Before we move the line, make sure that it will fit in it's new
+ // location. It always fits if the height isn't constrained or it's
+ // the first line.
+ nscoord totalHeight = topMargin + aLine->mBounds.height;
+ if (!aState.mUnconstrainedHeight && (aLine != mLines)) {
+ if (aState.mY + totalHeight > aState.mAvailSize.height) {
+ // The line will not fit
+ rv = PushLines(aState, aLine);
+ goto done;
+ }
+ }
+ if (isBlockLine) {
+ if (0 != topMargin) {
+ // We have to move the line now that we know the top margin
+ // for it.
+ aLine->MoveLineBy(0, topMargin);
+ aState.mY += topMargin;
+ }
+ }
+ else {
+ // Apply previous line's bottom margin before the inline-line.
+ nscoord bottomMargin = aState.mPrevMaxPosBottomMargin -
+ aState.mPrevMaxNegBottomMargin;
+ if (0 != bottomMargin) {
+ aLine->MoveLineBy(0, bottomMargin);
+ aState.mY += topMargin;
+ }
+ }
+
+ // Consume space and advance running values
+ aState.mY += aLine->mBounds.height;
+ aState.mPrevMaxNegBottomMargin = maxNegBottomMargin;
+ aState.mPrevMaxPosBottomMargin = maxPosBottomMargin;
+ if (nsnull != aState.mMaxElementSizePointer) {
+ nsSize* maxSize = aState.mMaxElementSizePointer;
+ if (aLineLayout.mReflowData.mMaxElementSize.width > maxSize->width) {
+ maxSize->width = aLineLayout.mReflowData.mMaxElementSize.width;
+ }
+ if (aLineLayout.mReflowData.mMaxElementSize.height > maxSize->height) {
+ maxSize->height = aLineLayout.mReflowData.mMaxElementSize.height;
+ }
+ }
+ {
+ nscoord xmost = aLine->mBounds.XMost();
+ if (xmost > aState.mKidXMost) {
+ aState.mKidXMost = xmost;
+ }
+ }
+
+ // Any below current line floaters to place?
+ if (aState.mPendingFloaters.Count() > 0) {
+ PlaceBelowCurrentLineFloaters(aState, aState.mY);
+ // XXX Factor in the height of the floaters as well when considering
+ // whether the line fits.
+ // The default policy is that if there isn't room for the floaters then
+ // both the line and the floaters are pushed to the next-in-flow...
+ }
+
+ if (aState.mY >= aState.mCurrentBand.availSpace.YMost()) {
+ // The current y coordinate is now past our available space
+ // rectangle. Get a new band of space.
+ GetAvailableSpace(aState, aState.mY);
+ }
+
+done:
+ return rv;
}
-void nsBlockFrame::PlaceBelowCurrentLineFloaters(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nscoord aY)
+// aY has borderpadding.top already factored in
+// aResult is relative to left,aY
+nsresult
+nsBlockFrame::GetAvailableSpace(nsBlockReflowState& aState, nscoord aY)
{
- NS_PRECONDITION(aState.floaterToDo.Count() > 0, "no floaters");
+ nsresult rv = NS_OK;
- // XXX Factor this code with PlaceFloater()...
- PRInt32 numFloaters = aState.floaterToDo.Count();
-
- for (PRInt32 i = 0; i < numFloaters; i++) {
- nsIFrame* floater = (nsIFrame*)aState.floaterToDo[i];
- nsRect region;
+ nsISpaceManager* sm = aState.mSpaceManager;
- // Get the band of available space
- // XXX This is inefficient to do this inside the loop...
- GetAvailableSpaceBand(aState, aY);
+ // Fill in band data for the specific Y coordinate
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->GetBandData(aY, aState.mAvailSize, aState.mCurrentBand);
+ sm->Translate(-aState.mBorderPadding.left, 0);
- // Get the type of floater
- nsIStyleContextPtr styleContext;
-
- floater->GetStyleContext(aCX, styleContext.AssignRef());
- nsStyleDisplay* sd = (nsStyleDisplay*)
- styleContext->GetData(kStyleDisplaySID);
-
- floater->GetRect(region);
- region.y = mCurrentState->currentBand->availSpace.y;
+ // Compute the bounding rect of the available space, i.e. space
+ // between any left and right floaters
+ aState.mCurrentBand.ComputeAvailSpaceRect();
- if (NS_STYLE_FLOAT_LEFT == sd->mFloats) {
- region.x = mCurrentState->currentBand->availSpace.x;
- } else {
- NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type");
- region.x = mCurrentState->currentBand->availSpace.XMost() - region.width;
- }
-
- // XXX Don't forget the floater's margins...
- mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left,
- 0);
- mCurrentState->spaceManager->AddRectRegion(region, floater);
-
- // Set the origin of the floater in world coordinates
- nscoord worldX, worldY;
-
- mCurrentState->spaceManager->GetTranslation(worldX, worldY);
- floater->MoveTo(region.x + worldX, region.y + worldY);
- mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0);
- }
- aState.floaterToDo.Clear();
-}
-
-/**
- * Flush a line out. Return true if the line fits in our available
- * height. If the line does not fit then return false. When the line
- * fits we advance the y coordinate, reset the x coordinate and
- * prepare the nsBlockReflowState for the next line.
- */
-PRBool nsBlockFrame::AdvanceToNextLine(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
- NS_PRECONDITION(aState.lineLength > 0, "bad line");
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
-
- nscoord y = aState.y + aState.topMargin;
- nscoord lineHeight;
-
- if (aState.isInline) {
- // Vertically align the children on this line, returning the height of
- // the line upon completion.
- lineHeight = nsCSSLayout::VerticallyAlignChildren(aCX, this,
- aState.styleFont,
- y,
- aState.lineStart,
- aState.lineLength,
- aState.ascents,
- aState.maxAscent);
-
- // Any below current line floaters to place?
- if (aState.floaterToDo.Count() > 0) {
- PlaceBelowCurrentLineFloaters(aCX, aState, y + lineHeight);
- // XXX Factor in the height of the floaters as well when considering
- // whether the line fits.
- // The default policy is that if there isn't room for the floaters then
- // both the line and the floaters are pushed to the next-in-flow...
- }
- } else {
- nsSize size;
-
- aState.lineStart->GetSize(size);
- lineHeight = size.height;
- }
-
- // The first line always fits
- if (aState.currentLineNumber > 0) {
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- if (y + lineHeight > yb) {
- // After vertical alignment of the children and factoring in the
- // proper margin, the line doesn't fit.
- return PR_FALSE;
- }
- }
-
- if (aState.isInline) {
- // Check if the right-edge of the line exceeds our running x-most
- nscoord xMost = aState.borderPadding.left + aState.lineWidth;
- if (xMost > aState.kidXMost) {
- aState.kidXMost = xMost;
- }
- }
-
- // Advance the y coordinate to the new position where the next
- // line or block element will go.
- aState.y = y + lineHeight;
- aState.x = 0;
-
- // Now that the vertical alignment is done we can perform horizontal
- // alignment and relative positioning. Skip all of these if we are
- // doing an unconstrained (in x) reflow. There's no point in doing
- // the work if we *know* we are going to reflowed again.
- if (!aState.unconstrainedWidth) {
- nsCSSLayout::HorizontallyPlaceChildren(aCX, this, aState.styleText,
- aState.lineStart, aState.lineLength,
- aState.lineWidth,
- aState.availSize.width);
-
- // Finally, now that the in-flow positions of the line's frames are
- // known we can apply relative positioning if any of them need it.
- if (!aState.justifying) {
- nsCSSLayout::RelativePositionChildren(aCX, this,
- aState.lineStart,
- aState.lineLength);
- }
- }
-
- // Record line length
- aState.lineLengths.AppendElement((void*)aState.lineLength);
-
- // Find the last frame in the line
- // XXX keep this as running state in the nsBlockReflowState
- nsIFrame* lastFrame = aState.lineStart;
- PRInt32 lineLen = aState.lineLength - 1;
- while (--lineLen >= 0) {
- lastFrame->GetNextSibling(lastFrame);
- }
-
- // Update maxElementSize
- if (nsnull != aState.maxElementSize) {
- nsSize& lineMax = aState.lineMaxElementSize;
- nsSize* maxMax = aState.maxElementSize;
- if (lineMax.width > maxMax->width) {
- maxMax->width = lineMax.width;
- }
- if (lineMax.height > maxMax->height) {
- maxMax->height = lineMax.height;
- }
- aState.lineMaxElementSize.width = 0;
- aState.lineMaxElementSize.height = 0;
- }
-
- // Advance to the next line
- aState.AdvanceToNextLine(lastFrame, lineHeight);
-
- return PR_TRUE;
-}
-
-/**
- * Add an inline child to the current line. Advance various running
- * values after placement.
- */
-void nsBlockFrame::AddInlineChildToLine(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsReflowMetrics& aKidSize,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC)
-{
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
-
- nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID);
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID);
-
- if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) {
- aState.needRelativePos = PR_TRUE;
- }
-
- // Place and size the child
- // XXX add in left margin from kid
- nsRect r;
- r.y = aState.y;
- r.width = aKidSize.width;
- r.height = aKidSize.height;
- if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) {
- // Left to right positioning.
- r.x = aState.borderPadding.left + aState.x + ss->mMargin.left;
- aState.x += aKidSize.width + ss->mMargin.left + ss->mMargin.right;
- } else {
- // Right to left positioning
- // XXX what should we do when aState.x goes negative???
- r.x = aState.x - aState.borderPadding.right - ss->mMargin.right -
- aKidSize.width;
- aState.x -= aKidSize.width + ss->mMargin.right + ss->mMargin.left;
- }
- aKidFrame->SetRect(r);
- aState.AddAscent(aKidSize.ascent);
- aState.lineWidth += aKidSize.width;
- aState.lineLength++;
-
- // Update maximums for the line
- if (aKidSize.ascent > aState.maxAscent) {
- aState.maxAscent = aKidSize.ascent;
- }
- if (aKidSize.descent > aState.maxDescent) {
- aState.maxDescent = aKidSize.descent;
- }
- // Update running margin maximums
- if (aState.firstChildIsInsideBullet && (aKidFrame == mFirstChild)) {
- // XXX temporary code. Since the molecule for the bullet frame
- // is the same as the LI frame, we get bad style information.
- // ignore it.
- } else {
- nscoord margin;
#if 0
- // XXX CSS2 spec says that top/bottom margin don't affect line height
- // calculation. We're waiting for clarification on this issue...
- if ((margin = ss->mMargin.top) < 0) {
- margin = -margin;
- if (margin > aState.maxNegTopMargin) {
- aState.maxNegTopMargin = margin;
- }
- } else {
- if (margin > aState.maxPosTopMargin) {
- aState.maxPosTopMargin = margin;
- }
- }
+ // XXX For now we assume that there are no height restrictions
+ // (e.g. no "float to bottom of column/page")
+ nsRect& availSpace = aState.mCurrentBand.availSpace;
+ aResult.x = availSpace.x;
+ aResult.y = availSpace.y;
+ aResult.width = availSpace.width;
+ aResult.height = aState.mAvailSize.height;
+#else
+ aState.mCurrentBand.availSpace.MoveBy(aState.mBorderPadding.left,
+ aState.mY);
#endif
- if ((margin = ss->mMargin.bottom) < 0) {
- margin = -margin;
- if (margin > aState.maxNegBottomMargin) {
- aState.maxNegBottomMargin = margin;
- }
- } else {
- if (margin > aState.maxPosBottomMargin) {
- aState.maxPosBottomMargin = margin;
- }
- }
- }
- // Update line max element size
- nsSize& mes = aState.lineMaxElementSize;
- if (nsnull != aKidMaxElementSize) {
- if (aKidMaxElementSize->width > mes.width) {
- mes.width = aKidMaxElementSize->width;
- }
- if (aKidMaxElementSize->height > mes.height) {
- mes.height = aKidMaxElementSize->height;
- }
- }
+ return rv;
}
-// Places and sizes the block-level element, and advances the line.
-// The rect is in the local coordinate space of the kid frame.
-void nsBlockFrame::AddBlockChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsRect& aKidRect,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC)
+// Give aLine and any successive lines to the block's next-in-flow; if
+// we don't have a next-in-flow then push all the children onto our
+// overflow list.
+nsresult
+nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineData* aLine)
{
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
+ PRInt32 i;
- nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID);
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID);
-
- if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) {
- aState.needRelativePos = PR_TRUE;
+ // Split our child-list in two; revert our last content offset and
+ // completion status to the previous line.
+ nsLineData* prevLine = aLine->mPrevLine;
+ NS_PRECONDITION(nsnull != prevLine, "pushing first line");
+ nsIFrame* prevKidFrame = prevLine->mFirstChild;
+ for (i = prevLine->mChildCount - 1; --i >= 0; ) {
+ prevKidFrame->GetNextSibling(prevKidFrame);
}
-
- // Translate from the kid's coordinate space to our coordinate space
- aKidRect.x += aState.borderPadding.left + ss->mMargin.left;
- aKidRect.y += aState.y + aState.topMargin;
-
- // Place and size the child
- aKidFrame->SetRect(aKidRect);
-
- aState.AddAscent(aKidRect.height);
- aState.lineLength++;
-
- // Is this the widest child frame?
- nscoord xMost = aKidRect.XMost() + ss->mMargin.right;
- if (xMost > aState.kidXMost) {
- aState.kidXMost = xMost;
- }
-
- // Update the max element size
- if (nsnull != aKidMaxElementSize) {
- if (aKidMaxElementSize->width > aState.maxElementSize->width) {
- aState.maxElementSize->width = aKidMaxElementSize->width;
- }
- if (aKidMaxElementSize->height > aState.maxElementSize->height) {
- aState.maxElementSize->height = aKidMaxElementSize->height;
- }
- }
-
- // and the bottom line margin information which we'll use when placing
- // the next child
- if (ss->mMargin.bottom < 0) {
- aState.maxNegBottomMargin = -ss->mMargin.bottom;
- } else {
- aState.maxPosBottomMargin = ss->mMargin.bottom;
- }
-
- // Update the running y-offset
- aState.y += aKidRect.height + aState.topMargin;
-
- // Apply relative positioning if necessary
- nsCSSLayout::RelativePositionChildren(aCX, this, aKidFrame, 1);
-
- // Advance to the next line
- aState.AdvanceToNextLine(aKidFrame, aKidRect.height);
-}
-
-/**
- * Compute the available size for reflowing the given child at the
- * current x,y position in the state. Note that this may return
- * negative or zero width/height's if we are out of room.
- */
-void nsBlockFrame::GetAvailSize(nsSize& aResult,
- nsBlockReflowState& aState,
- nsIStyleContext* aKidSC,
- PRBool aIsInline)
-{
- // Determine the maximum available reflow height for the child
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- aResult.height = aState.unconstrainedHeight ? NS_UNCONSTRAINEDSIZE :
- yb - aState.y - aState.topMargin;
-
- // Determine the maximum available reflow width for the child
- if (aState.unconstrainedWidth) {
- aResult.width = NS_UNCONSTRAINEDSIZE;
- } else if (aIsInline) {
- if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) {
- aResult.width = aState.currentBand->availSpace.XMost() - aState.x;
- } else {
- aResult.width = aState.x - aState.currentBand->availSpace.x;
- }
- } else {
- // It's a block. Don't adjust for the left/right margin here. That happens
- // later on once we know the current left/right edge
- aResult.width = aState.availSize.width;
- }
-}
-
-/**
- * Push all of the kids that we have not reflowed, starting at
- * aState.lineStart. aPrevKid is the kid previous to aState.lineStart
- * and is also our last child. Note that line length is NOT a
- * reflection of the number of children we are actually pushing
- * (because we don't break the sibling list as we add children to the
- * line).
- */
-void nsBlockFrame::PushKids(nsBlockReflowState& aState)
-{
- nsIFrame* prevFrame = aState.prevLineLastFrame;
- NS_PRECONDITION(nsnull != prevFrame, "pushing all kids");
#ifdef NS_DEBUG
- nsIFrame* nextSibling;
-
- prevFrame->GetNextSibling(nextSibling);
- NS_PRECONDITION(nextSibling == aState.lineStart, "bad prev line");
+ nsIFrame* nextFrame;
+ prevKidFrame->GetNextSibling(nextFrame);
+ NS_ASSERTION(nextFrame == aLine->mFirstChild, "bad line list");
#endif
+ prevKidFrame->SetNextSibling(nsnull);
+ prevLine->mNextLine = nsnull;
+ mLastContentOffset = prevLine->mLastContentOffset;
+ mLastContentIsComplete = prevLine->mLastContentIsComplete;
+
+ // Push children to our next-in-flow if we have, or to our overflow list
+ nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
+ PRInt32 pushCount = 0;
+ if (nsnull == nextInFlow) {
+ // Place children on the overflow list
+ mOverflowList = aLine->mFirstChild;
+
+ // Destroy the lines
+ nsLineData* line = aLine;
+ while (nsnull != line) {
+ pushCount += line->mChildCount;
+ nsLineData* next = line->mNextLine;
+ delete line;
+ line = next;
+ }
+ }
+ else {
+ aLine->mPrevLine = nsnull;
+
+ // Pass on the children to our next-in-flow
+ nsLineData* line = aLine;
+ prevLine = line;
+ nsIFrame* lastKid = aLine->mFirstChild;
+ nsIFrame* kid = lastKid;
+ while (nsnull != line) {
+ i = line->mChildCount;
+ pushCount += i;
+ NS_ASSERTION(kid == line->mFirstChild, "bad line list");
+ while (--i >= 0) {
+ kid->SetGeometricParent(nextInFlow);
+ nsIFrame* contentParent;
+ kid->GetContentParent(contentParent);
+ if (this == contentParent) {
+ kid->SetContentParent(nextInFlow);
+ }
+ lastKid = kid;
+ kid->GetNextSibling(kid);
+ }
+ prevLine = line;
+ line = line->mNextLine;
+ }
+
+ // Join the two line lists
+ nsLineData* nextInFlowLine = nextInFlow->mLines;
+ if (nsnull != nextInFlowLine) {
+ lastKid->SetNextSibling(nextInFlowLine->mFirstChild);
+ nextInFlowLine->mPrevLine = prevLine;
+ }
+ nsIFrame* firstKid = aLine->mFirstChild;
+ prevLine->mNextLine = nextInFlowLine;
+ nextInFlow->mLines = aLine;
+ nextInFlow->mFirstChild = firstKid;
+ nextInFlow->mChildCount += pushCount;
+ firstKid->GetIndexInParent(nextInFlow->mFirstContentOffset);
#ifdef NS_DEBUG
- PRInt32 numKids = LengthOf(mFirstChild);
- NS_ASSERTION(numKids == mChildCount, "bad child count");
+ nextInFlow->VerifyLines(PR_FALSE);
+#endif
+ }
+ mChildCount -= pushCount;
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return NS_LINE_LAYOUT_NOT_COMPLETE;
+}
+
+nsresult
+nsBlockFrame::ReflowMapped(nsBlockReflowState& aState)
+{
+ nsresult rv = NS_OK;
+
+ // Get some space to start reflowing with
+ GetAvailableSpace(aState, aState.mY);
+
+ nsLineData* prevLine = nsnull;
+ nsLineData* line = mLines;
+ nsLineLayout lineLayout(aState);
+ aState.mCurrentLine = &lineLayout;
+ while (nsnull != line) {
+ // Initialize the line layout for this line
+ rv = lineLayout.Initialize(aState, line);
+ if (NS_OK != rv) {
+ goto done;
+ }
+ lineLayout.mPrevKidFrame = aState.mPrevKidFrame;
+
+ // Reflow the line
+ nsresult lineReflowStatus = lineLayout.ReflowLine();
+ if (lineReflowStatus < 0) {
+ // Some kind of hard error
+ rv = lineReflowStatus;
+ goto done;
+ }
+ mChildCount += lineLayout.mNewFrames;
+
+ // Now place it. It's possible it won't fit.
+ rv = PlaceLine(aState, lineLayout, line);
+ if (NS_LINE_LAYOUT_COMPLETE != rv) {
+ goto done;
+ }
+
+ mLastContentOffset = line->mLastContentOffset;
+ mLastContentIsComplete = PRBool(line->mLastContentIsComplete);
+ prevLine = line;
+ line = line->mNextLine;
+ aState.mPrevKidFrame = lineLayout.mPrevKidFrame;
+ }
+
+done:
+ aState.mCurrentLine = nsnull;
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return rv;
+}
+
+nsresult
+nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState)
+{
+ nsresult rv = NS_OK;
+
+ // If we have no children and we have a prev-in-flow then we need to
+ // pick up where it left off. If we have children, e.g. we're being
+ // resized, then our content offset will have already been set
+ // correctly.
+ nsIFrame* kidPrevInFlow = nsnull;
+ if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
+ nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow;
+ mFirstContentOffset = prev->NextChildOffset();// XXX Is this necessary?
+ if (PR_FALSE == prev->mLastContentIsComplete) {
+ // Our prev-in-flow's last child is not complete
+ prev->LastChild(kidPrevInFlow);
+ }
+ }
+
+ // Get to the last line where the new content may be added
+ nsLineData* line = nsnull;
+ nsLineData* prevLine = nsnull;
+ if (nsnull != mLines) {
+ line = mLines;
+ while (nsnull != line->mNextLine) {
+ line = line->mNextLine;
+ }
+ prevLine = line;
+
+ // If the last line is not complete then kidPrevInFlow should be
+ // set to the last-line's last child.
+ if (!prevLine->mLastContentIsComplete) {
+ kidPrevInFlow = prevLine->GetLastChild();
+ }
+ }
+
+ // Get some space to start reflowing with
+ GetAvailableSpace(aState, aState.mY);
+
+ // Now reflow the new content until we are out of new content or out
+ // of vertical space.
+ PRInt32 kidIndex = NextChildOffset();
+ nsLineLayout lineLayout(aState);
+ aState.mCurrentLine = &lineLayout;
+ lineLayout.mKidPrevInFlow = kidPrevInFlow;
+ PRInt32 contentChildCount = mContent->ChildCount();
+ while (kidIndex < contentChildCount) {
+ if (nsnull == line) {
+ if (!MoreToReflow(aState)) {
+ break;
+ }
+ line = new nsLineData();
+ if (nsnull == line) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+ line->mFirstContentOffset = kidIndex;
+ }
+
+ // Initialize the line layout for this line
+ rv = lineLayout.Initialize(aState, line);
+ if (NS_OK != rv) {
+ goto done;
+ }
+ lineLayout.mKidPrevInFlow = kidPrevInFlow;
+ lineLayout.mPrevKidFrame = aState.mPrevKidFrame;
+
+ // Reflow the line
+ nsresult lineReflowStatus = lineLayout.ReflowLine();
+ if (lineReflowStatus < 0) {
+ // Some kind of hard error
+ rv = lineReflowStatus;
+ goto done;
+ }
+ mChildCount += lineLayout.mNewFrames;
+
+ // Add line to the block; do this before placing the line in case
+ // PushLines is needed.
+ if (nsnull == prevLine) {
+ // For the first line, initialize mFirstContentOffset
+ mFirstContentOffset = line->mFirstContentOffset;
+ mFirstChild = line->mFirstChild;
+ mLines = line;
+ }
+ else {
+ prevLine->mNextLine = line;
+ line->mPrevLine = prevLine;
+ }
+
+ // Now place it. It's possible it won't fit.
+ rv = PlaceLine(aState, lineLayout, line);
+ if (NS_LINE_LAYOUT_COMPLETE != rv) {
+ goto done;
+ }
+ kidIndex = lineLayout.mKidIndex;
+ kidPrevInFlow = lineLayout.mKidPrevInFlow;
+
+ mLastContentOffset = line->mLastContentOffset;
+ mLastContentIsComplete = PRBool(line->mLastContentIsComplete);
+ prevLine = line;
+ line = line->mNextLine;
+ aState.mPrevKidFrame = lineLayout.mPrevKidFrame;
+ }
+
+done:
+ aState.mCurrentLine = nsnull;
+ if (aState.mBlockIsPseudo) {
+ PropagateContentOffsets();
+ }
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return rv;
+}
+
+nsresult
+nsBlockFrame::InitializeState(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockReflowState& aState)
+{
+ nsresult rv;
+ rv = aState.Initialize(aPresContext, aSpaceManager,
+ aMaxSize, aMaxElementSize, this);
+
+ nsStyleSpacing* mySpacing = (nsStyleSpacing*)
+ mStyleContext->GetData(kStyleSpacingSID);
+
+ // Apply border and padding adjustments for regular frames only
+ if (!aState.mBlockIsPseudo) {
+ aState.mY = mySpacing->mBorderPadding.top;
+ aState.mX = mySpacing->mBorderPadding.left;
+ aState.mAvailSize.width -=
+ (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right);
+ aState.mAvailSize.height -=
+ (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom);
+ aState.mBorderPadding = mySpacing->mBorderPadding;
+ }
+ else {
+ aState.mBorderPadding.SizeTo(0, 0, 0, 0);
+ }
+
+ // Setup initial list ordinal value
+ nsIAtom* tag = mContent->GetTag();
+ if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
+ (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
+ nsHTMLValue value;
+ if (eContentAttr_HasValue ==
+ ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) {
+ if (eHTMLUnit_Integer == value.GetUnit()) {
+ aState.mNextListOrdinal = value.GetIntValue();
+ }
+ }
+ }
+ NS_RELEASE(tag);
+
+ return rv;
+}
+
+#if XXX
+NS_METHOD
+nsBlockFrame::SizeTo(nscoord aWidth, nscoord aHeight)
+{
+ printf("size to %g,%g\n", NS_TWIPS_TO_POINTS_FLOAT(aWidth),
+ NS_TWIPS_TO_POINTS_FLOAT(aHeight));
+ return nsHTMLContainerFrame::SizeTo(aWidth, aHeight);
+}
+
+NS_METHOD
+nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ aStatus = frComplete;
+ nsBlockReflowState state;
+ rv = InitializeState(aPresContext, aMaxSize, aMaxElementSize, state);
+ if (NS_OK == rv) {
+ nsRect desiredRect;
+ rv = DoResizeReflow(state, desiredRect, aStatus);
+ aDesiredSize.width = desiredRect.width;
+ aDesiredSize.height = desiredRect.height;
+ aDesiredSize.ascent = aDesiredSize.height;
+ aDesiredSize.descent = 0;
+ }
+ return rv;
+}
#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": push kids (childCount=%d)\n", mChildCount);
- DumpFlow();
-#endif
-
- PushChildren(aState.lineStart, prevFrame, mLastContentIsComplete);
- SetLastContentOffset(prevFrame);
-
- // Set mLastContentIsComplete to the previous lines last content is
- // complete now that the previous line's last child is our last
- // child.
- mLastContentIsComplete = aState.prevLineLastContentIsComplete;
-
- // Fix up child count
- // XXX is there a better way? aState.lineLength doesn't work because
- // we might be pushing more than just the pending line.
- nsIFrame* kid = mFirstChild;
- PRInt32 kids = 0;
- while (nsnull != kid) {
- kids++;
- kid->GetNextSibling(kid);
- }
- mChildCount = kids;
-
- // Make sure we have no lingering line data
- aState.lineLength = 0;
- aState.lineStart = nsnull;
-
-#ifdef NOISY
- ListTag(stdout);
- printf(": push kids done (childCount=%d) [%c]\n", mChildCount,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-}
-
-/**
- * Gets a band of available space starting at the specified y-offset. Assumes
- * the local coordinate space is currently set to the upper-left origin of the
- * bounding rect
- *
- * Updates "currentBand" and "x" member data of the block reflow state
- */
-void nsBlockFrame::GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY)
-{
- // Gets a band of available space.
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
- aState.spaceManager->GetBandData(aY, aState.availSize, *aState.currentBand);
-
- // Compute the bounding rect of the available space, i.e. space between any
- // left and right floaters
- aState.currentBand->ComputeAvailSpaceRect();
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
- aState.x = aState.currentBand->availSpace.x;
-}
-
-void nsBlockFrame::ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear)
-{
- // Translate the coordinate space
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
-
-getBand:
- nscoord y = aState.y + aState.topMargin;
- PRBool isLeftFloater = PR_FALSE;
- PRBool isRightFloater = PR_FALSE;
-
- // Get a band of available space
- aState.spaceManager->GetBandData(y, aState.availSize, *aState.currentBand);
-
- // Scan the trapezoids looking for left and right floaters
- nsBandTrapezoid* trapezoid = aState.currentBand->trapezoids;
- for (PRInt32 i = 0; i < aState.currentBand->count; i++) {
- // XXX Handle multiply occupied
- if (nsBandTrapezoid::smOccupied == trapezoid->state) {
- nsStyleDisplay* display;
-
- trapezoid->frame->GetStyleData(kStylePositionSID, (nsStyleStruct*&)display);
- if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
- isLeftFloater = PR_TRUE;
- } else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) {
- isRightFloater = PR_TRUE;
- }
- }
-
- trapezoid++;
- }
-
- if (isLeftFloater) {
- if ((aClear == NS_STYLE_CLEAR_LEFT) ||
- (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
- aState.y += aState.currentBand->trapezoids[0].GetHeight();
- goto getBand;
- }
- }
- if (isRightFloater) {
- if ((aClear == NS_STYLE_CLEAR_RIGHT) ||
- (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
- aState.y += aState.currentBand->trapezoids[0].GetHeight();
- goto getBand;
- }
- }
-
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
-}
-
-// Bit's for PlaceAndReflowChild return value
-#define PLACE_FIT 0x1
-#define PLACE_FLOWED 0x2
-
-PRIntn
-nsBlockFrame::PlaceAndReflowChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC)
-{
- nsSize kidMaxElementSize;
- nsSize* pKidMaxElementSize =
- (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull;
-
- // Get line start setup if we are at the start of a new line
- if (nsnull == aState.lineStart) {
- NS_ASSERTION(0 == aState.lineLength, "bad line length");
- aState.lineStart = aKidFrame;
- }
-
- // Get kid and its style
- nsStyleDisplay* styleDisplay = (nsStyleDisplay*)
- aKidSC->GetData(kStyleDisplaySID);
-
- // Figure out if kid is a block element or not
- PRBool isInline = PR_TRUE;
- PRIntn display = styleDisplay->mDisplay;
- if (aState.firstChildIsInsideBullet && (mFirstChild == aKidFrame)) {
- // XXX Special hack for properly reflowing bullets that have the
- // inside value for list-style-position.
- display = NS_STYLE_DISPLAY_INLINE;
- }
- if ((NS_STYLE_DISPLAY_BLOCK == display) ||
- (NS_STYLE_DISPLAY_LIST_ITEM == display)) {
- // Block elements always end up on the next line (unless they are
- // already at the start of the line).
- isInline = PR_FALSE;
- if (aState.lineLength > 0) {
- aState.breakAfterChild = PR_TRUE;
- }
- }
-
- // Handle forced break first
- if (aState.breakAfterChild) {
- NS_ASSERTION(aState.lineStart != aKidFrame, "bad line");
-
- // Get the last child in the current line
- nsIFrame* lastFrame = aState.lineStart;
- PRInt32 lineLen = aState.lineLength - 1;
- while (--lineLen >= 0) {
- lastFrame->GetNextSibling(lastFrame);
- }
-
- if (!AdvanceToNextLine(aCX, aState)) {
- // The previous line didn't fit.
- return 0;
- }
- aState.lineStart = aKidFrame;
-
- // Get the style for the last child, and see if it wanted to clear
- // floaters. This handles the BR tag, which is the only inline
- // element for which clear applies
- nsIStyleContextPtr lastChildSC;
-
- lastFrame->GetStyleContext(aCX, lastChildSC.AssignRef());
- nsStyleDisplay* lastChildDisplay = (nsStyleDisplay*)
- lastChildSC->GetData(kStyleDisplaySID);
- switch (lastChildDisplay->mBreakType) {
- case NS_STYLE_CLEAR_LEFT:
- case NS_STYLE_CLEAR_RIGHT:
- case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
- ClearFloaters(aState, lastChildDisplay->mBreakType);
- break;
- }
- }
-
- // Now that we've handled force breaks (and maybe called AdvanceToNextLine()
- // which checks), remember whether it's an inline frame
- aState.isInline = isInline;
-
- // If we're at the beginning of a line then compute the top margin that we
- // should use
- if (aState.lineStart == aKidFrame) {
- // Compute the top margin to use for this line
- aState.topMargin = GetTopMarginFor(aCX, aState, aKidFrame, aKidSC,
- aState.isInline);
-
- // If it's an inline element then get a band of available space
- //
- // XXX If we have a current band and there's unused space in that band
- // then avoid this call to get a band...
- if (aState.isInline) {
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
- }
- }
-
- // Compute the available space to reflow the child into and then
- // reflow it into that space.
- nsSize kidAvailSize;
- GetAvailSize(kidAvailSize, aState, aKidSC, aState.isInline);
- if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) {
- // No more room
- return 0;
- }
-
- ReflowStatus status;
-
- if (aState.isInline) {
- nsReflowMetrics kidSize;
-
- // Inline elements are never passed the space manager
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
-
- // For first children, we skip all the fit checks because we must
- // fit at least one child for a parent to figure what to do with us.
- if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) {
- NS_ASSERTION(nsnull != aState.lineStart, "bad line start");
-
- if (aKidFrame == aState.lineStart) {
- // Width always fits when we are at the logical left margin.
- // Just check the height.
- //
- // XXX This height check isn't correct now that we have bands of
- // available space...
- if (kidSize.height > kidAvailSize.height) {
- // It's too tall
- return PLACE_FLOWED;
- }
- } else {
- // Examine state and if the breakBeforeChild is set and we
- // aren't already on the new line, do the forcing now.
- // XXX Why aren't we doing this check BEFORE we resize reflow the child?
- if (aState.breakBeforeChild) {
- aState.breakBeforeChild = PR_FALSE;
- if (aKidFrame != aState.lineStart) {
- if (!AdvanceToNextLine(aCX, aState)) {
- // Flushing out the line failed.
- return PLACE_FLOWED;
- }
- aState.lineStart = aKidFrame;
-
- // Get a band of available space
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
-
- // Reflow child now that it has the line to itself
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE);
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
- }
- }
-
- // When we are not at the logical left margin then we need
- // to check the width first. If we are too wide then advance
- // to the next line and try reflowing again.
- if (kidSize.width > kidAvailSize.width) {
- // Too wide. Try next line
- if (!AdvanceToNextLine(aCX, aState)) {
- // Flushing out the line failed.
- return PLACE_FLOWED;
- }
- aState.lineStart = aKidFrame;
-
- // Get a band of available space
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
-
- // Reflow splittable children
- SplittableType isSplittable;
-
- aKidFrame->IsSplittable(isSplittable);
- if (isSplittable != frNotSplittable) {
- // Update size info now that we are on the next line. Then
- // reflow the child into the new available space.
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE);
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
-
- // If we just reflowed our last child then update the
- // mLastContentIsComplete state.
- nsIFrame* nextSibling;
-
- aKidFrame->GetNextSibling(nextSibling);
- if (nsnull == nextSibling) {
- // Use state from the reflow we just did
- mLastContentIsComplete = PRBool(status == frComplete);
- }
- }
-
- // XXX This height check isn't correct now that we have bands of
- // available space...
- if (kidSize.height > kidAvailSize.height) {
- // It's too tall on the next line
- return PLACE_FLOWED;
- }
- // It's ok if it's too wide on the next line.
- }
- }
- }
-
- // Add child to the line
- AddInlineChildToLine(aCX, aState, aKidFrame, kidSize,
- pKidMaxElementSize, aKidSC);
- } else {
- nsRect kidRect;
-
- // Does the block-level element want to clear any floaters that impact
- // it? Note that the clear property only applies to block-level elements
- // and the BR tag
- nsStyleDisplay* styleDisplay = (nsStyleDisplay*)
- aKidSC->GetData(kStyleDisplaySID);
- switch (styleDisplay->mBreakType) {
- case NS_STYLE_CLEAR_LEFT:
- case NS_STYLE_CLEAR_RIGHT:
- case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
- ClearFloaters(aState, styleDisplay->mBreakType);
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_FALSE);
- if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) {
- // No more room
- return 0;
- }
- break;
- }
-
- // Give the block its own local coordinate space.. Note: ReflowChild()
- // will adjust for the child's left/right margin after determining the
- // current left/right edge
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
- // Give the block-level element the opportunity to use the space manager
- status = ReflowChild(aKidFrame, aCX, aState.spaceManager,
- kidAvailSize, kidRect, pKidMaxElementSize);
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
-
- // For first children, we skip all the fit checks because we must
- // fit at least one child for a parent to figure what to do with us.
- if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) {
- // Block elements always fit horizontally (because they are
- // always placed at the logical left margin). Check to see if
- // the block fits vertically
- if (kidRect.YMost() > kidAvailSize.height) {
- // Nope
- return PLACE_FLOWED;
- }
- }
-
- // Add block child
- // XXX We need to set lastContentIsComplete here, because AddBlockChild()
- // calls AdvaneceToNextLine(). We need to restructure the flow of control,
- // and use a state machine...
- aState.lastContentIsComplete = PRBool(status == frComplete);
- AddBlockChild(aCX, aState, aKidFrame, kidRect, pKidMaxElementSize, aKidSC);
- }
-
- // If we just reflowed our last child then update the
- // mLastContentIsComplete state.
- nsIFrame* nextSibling;
-
- aKidFrame->GetNextSibling(nextSibling);
- if (nsnull == nextSibling) {
- // Use state from the reflow we just did
- mLastContentIsComplete = PRBool(status == frComplete);
- }
-
- aState.lastContentIsComplete = PRBool(status == frComplete);
- if (aState.isInline && (frNotComplete == status)) {
- // Since the inline child didn't complete its reflow we *know*
- // that a continuation of it can't possibly fit on the current
- // line. Therefore, set a flag in the state that will cause the
- // a line break before the next frame is placed.
- aState.breakAfterChild = PR_TRUE;
- }
-
- aState.reflowStatus = status;
- return PLACE_FLOWED | PLACE_FIT;
-}
-
-/**
- * Reflow the existing frames.
- *
- * @param aCX presentation context to use
- * @param aState in out parameter which tracks the state of
- * reflow for the block frame.
- * @return true if we successfully reflowed all the mapped children and false
- * otherwise, e.g. we pushed children to the next in flow
- */
PRBool
-nsBlockFrame::ReflowMappedChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n",
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-
- PRBool result = PR_TRUE;
- nsIFrame* kidFrame;
-
- for (kidFrame = mFirstChild; nsnull != kidFrame; ) {
-
- /* we get the kid's style from kidFrame's cached style context */
- nsIStyleContextPtr kidSC;
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
-
- // Attempt to place and reflow the child
-
- // XXX if child is not splittable and it fits just place it where
- // it is, otherwise advance to the next line and place it there if
- // possible
-
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- ReflowStatus status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // The child doesn't fit. Push it and any remaining children.
- PushKids(aState);
- result = PR_FALSE;
- goto push_done;
- }
-
- // Is the child complete?
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- if (frComplete == status) {
- // Yes, the child is complete
- NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
- } else {
- // No the child isn't complete
- if (nsnull == kidNextInFlow) {
- // The child doesn't have a next-in-flow so create a continuing
- // frame. This hooks the child into the flow
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
- NS_ASSERTION(nsnull != continuingFrame, "frame creation failed");
-
- // Add the continuing frame to the sibling list
- nsIFrame* nextSib;
-
- kidFrame->GetNextSibling(nextSib);
- continuingFrame->SetNextSibling(nextSib);
- kidFrame->SetNextSibling(continuingFrame);
- mChildCount++;
- }
-
- // Unlike the inline frame code we can't assume that we used
- // up all of our space because the child's reflow status is
- // frNotComplete. Instead, the child is probably split and
- // we need to reflow the continuations as well.
- }
-
- // Get the next child frame
- kidFrame->GetNextSibling(kidFrame);
- }
-
- push_done:
-#ifdef NS_DEBUG
- nsIFrame* lastChild;
- PRInt32 lastIndexInParent;
-
- LastChild(lastChild);
- lastChild->GetIndexInParent(lastIndexInParent);
- NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset");
-
- PRInt32 len = LengthOf(mFirstChild);
- NS_POSTCONDITION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
-
-#ifdef NOISY
- ListTag(stdout);
- printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n",
- (result ? "" : "NOT "),
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
- return result;
-}
-
-/* XXX:
- * In this method, we seem to be getting the style for the next child,
- * and checking that child's style for the child's display type.
- * The problem is that it's very inefficient to ResolveStyleFor(kid) here
- * and then just throw it away, when we end up doing a ResolveStyleFor(kid)
- * again when we actually create a frame for the content.
- * It would be nice to cache the resolved style, maybe in the reflow state?
- */
-PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX)
+nsBlockFrame::MoreToReflow(nsBlockReflowState& aState)
{
PRBool rv = PR_FALSE;
- if (IsPseudoFrame()) {
+ if (aState.mBlockIsPseudo) {
// Get the next content object that we would like to reflow
PRInt32 kidIndex = NextChildOffset();
nsIContentPtr kid = mContent->ChildAt(kidIndex);
if (kid.IsNotNull()) {
// Resolve style for the kid
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
+ nsIStyleContextPtr kidSC =
+ aState.mPresContext->ResolveStyleContextFor(kid, this);
nsStyleDisplay* kidStyleDisplay = (nsStyleDisplay*)
kidSC->GetData(kStyleDisplaySID);
switch (kidStyleDisplay->mDisplay) {
@@ -1305,640 +993,288 @@ PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX)
return rv;
}
-/**
- * Create new frames for content we haven't yet mapped
- *
- * @param aCX presentation context to use
- * @return frComplete if all content has been mapped and frNotComplete
- * if we should be continued
- */
-nsIFrame::ReflowStatus
-nsBlockFrame::ReflowAppendedChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
+nsBlockReflowState*
+nsBlockFrame::FindBlockReflowState(nsIPresContext* aPresContext,
+ nsIFrame* aFrame)
{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
- nsIFrame* kidPrevInFlow = nsnull;
- ReflowStatus result = frNotComplete;
-
- // If we have no children and we have a prev-in-flow then we need to pick
- // up where it left off. If we have children, e.g. we're being resized, then
- // our content offset should already be set correctly...
- if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
- nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow;
- NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow");
- mFirstContentOffset = prev->NextChildOffset();
- if (PR_FALSE == prev->mLastContentIsComplete) {
- // Our prev-in-flow's last child is not complete
- prev->LastChild(kidPrevInFlow);
- }
- }
-
- // Place our children, one at a time until we are out of children
- PRInt32 kidIndex = NextChildOffset();
- nsIFrame* kidFrame = nsnull;
- nsIFrame* prevKidFrame;
-
- LastChild(prevKidFrame);
- for (;;) {
- // Get the next content object
- nsIContentPtr kid = mContent->ChildAt(kidIndex);
- if (kid.IsNull()) {
- result = frComplete;
- break;
- }
-
- // Resolve style for the kid
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
- nsStylePosition* kidPosition = (nsStylePosition*)
- kidSC->GetData(kStylePositionSID);
- nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
- kidSC->GetData(kStyleDisplaySID);
-
- // Check whether it wants to floated or absolutely positioned
- if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) {
- AbsoluteFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- kidFrame->SetStyleContext(aCX,kidSC);
- } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
- PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- kidFrame->SetStyleContext(aCX,kidSC);
- } else if (nsnull == kidPrevInFlow) {
- // Create initial frame for the child
- nsIContentDelegate* kidDel;
- switch (kidDisplay->mDisplay) {
- case NS_STYLE_DISPLAY_BLOCK:
- case NS_STYLE_DISPLAY_LIST_ITEM:
- // Pseudo block frames do not contain other block elements
- // unless the block element would be the first child.
- if (IsPseudoFrame()) {
- // If we're being used as a pseudo frame, i.e. we map the same
- // content as our parent then we want to indicate we're complete;
- // otherwise we'll be continued and go on mapping children...
-
- // It better be true that we are not being asked to flow a
- // block element as our first child. That means the body
- // decided it needed a pseudo-frame when it shouldn't have.
- NS_ASSERTION(nsnull != mFirstChild, "bad body");
-
- result = frComplete;
- goto done;
- }
- // FALL THROUGH (and create frame)
-
- case NS_STYLE_DISPLAY_INLINE:
- kidDel = kid->GetDelegate(aCX);
- kidFrame = kidDel->CreateFrame(aCX, kid, kidIndex, this);
- NS_RELEASE(kidDel);
- break;
-
- default:
- NS_ASSERTION(nsnull == kidPrevInFlow, "bad prev in flow");
- nsFrame::NewFrame(&kidFrame, kid, kidIndex, this);
+ nsBlockReflowState* state = nsnull;
+ if (nsnull != aFrame) {
+ nsIFrame* parent;
+ aFrame->GetGeometricParent(parent);
+ while (nsnull != parent) {
+ nsBlockFrame* block;
+ nsresult rv = parent->QueryInterface(kBlockFrameCID, (void**) &block);
+ if (NS_OK == rv) {
+ nsIPresShell* shell = aPresContext->GetShell();
+ state = (nsBlockReflowState*) shell->GetCachedData(block);
+ NS_RELEASE(shell);
break;
}
- kidFrame->SetStyleContext(aCX,kidSC);
- } else {
- // Since kid has a prev-in-flow, use that to create the next
- // frame.
- kidPrevInFlow->CreateContinuingFrame(aCX, this, kidFrame);
+ parent->GetGeometricParent(parent);
}
-
- // Link child frame into the list of children. If the frame ends
- // up not fitting and getting pushed, the PushKids code will fixup
- // the child count for us.
- if (nsnull != prevKidFrame) {
-#ifdef NS_DEBUG
- nsIFrame* nextSibling;
-
- prevKidFrame->GetNextSibling(nextSibling);
- NS_ASSERTION(nsnull == nextSibling, "bad append");
-#endif
- prevKidFrame->SetNextSibling(kidFrame);
- } else {
- NS_ASSERTION(nsnull == mFirstChild, "bad create");
- mFirstChild = kidFrame;
- SetFirstContentOffset(kidFrame);
- }
- prevKidFrame = kidFrame;
- mChildCount++;
-
- // Reflow child frame as many times as necessary until it is
- // complete.
- ReflowStatus status;
- do {
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // We ran out of room.
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- mLastContentIsComplete = PRBool(nsnull == kidNextInFlow);
- PushKids(aState);
-
- goto push_done;
- }
-
- // Did the child complete?
- prevKidFrame = kidFrame;
- if (frNotComplete == status) {
- // Child didn't complete so create a continuing frame
- kidPrevInFlow = kidFrame;
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
-
- // Add the continuing frame to the sibling list
- nsIFrame* kidNextSibling;
-
- kidFrame->GetNextSibling(kidNextSibling);
- continuingFrame->SetNextSibling(kidNextSibling);
- kidFrame->SetNextSibling(continuingFrame);
- kidFrame = continuingFrame;
- mChildCount++;
-
- // Switch to new kid style
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
- }
-#ifdef NS_DEBUG
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- NS_ASSERTION(nsnull == kidNextInFlow, "huh?");
-#endif
- } while (frNotComplete == status);
-
- // The child that we just reflowed is complete
-#ifdef NS_DEBUG
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
-#endif
- kidIndex++;
- kidPrevInFlow = nsnull;
}
-
- done:
- // To get here we either completely reflowed all our appended
- // children OR we are a pseudo-frame and we ran into a block
- // element. In either case our last content MUST be complete.
- NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state");
- NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
- SetLastContentOffset(prevKidFrame);
-
- push_done:
-#ifdef NS_DEBUG
- PRInt32 len = LengthOf(mFirstChild);
- NS_ASSERTION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
- return result;
+ return state;
}
-/**
- * Pullup frames from our next in flow and try to place them. Before
- * this is called our previously mapped children, if any have been
- * reflowed which means that the block reflow state's x and y
- * coordinates and other data are ready to go.
- *
- * Return true if we pulled everything up.
- */
-PRBool
-nsBlockFrame::PullUpChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": pullup (childCount=%d) [%d,%d,%c]\n",
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-
- PRBool result = PR_TRUE;
- nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
- nsIFrame* prevKidFrame;
-
- LastChild(prevKidFrame);
- while (nsnull != nextInFlow) {
- // Get first available frame from the next-in-flow
- nsIFrame* kidFrame = PullUpOneChild(nextInFlow, prevKidFrame);
- if (nsnull == kidFrame) {
- // We've pulled up all the children from that next-in-flow, so
- // move to the next next-in-flow.
- nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
- continue;
- }
-
- // Get style information for the pulled up kid
- nsIContentPtr kid;
-
- kidFrame->GetContent(kid.AssignRef());
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
-
- ReflowStatus status;
- do {
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // Push the kids that didn't fit back down to the next-in-flow
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- mLastContentIsComplete = PRBool(nsnull == kidNextInFlow);
- PushKids(aState);
-
- result = PR_FALSE;
- goto push_done;
- }
-
- if (frNotComplete == status) {
- // Child is not complete
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- if (nsnull == kidNextInFlow) {
- // Create a continuing frame for the incomplete child
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
-
- // Add the continuing frame to our sibling list.
- nsIFrame* nextSibling;
-
- kidFrame->GetNextSibling(nextSibling);
- continuingFrame->SetNextSibling(nextSibling);
- kidFrame->SetNextSibling(continuingFrame);
- prevKidFrame = kidFrame;
- kidFrame = continuingFrame;
- mChildCount++;
-
- // Switch to new kid style
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
- } else {
- // The child has a next-in-flow, but it's not one of ours.
- // It *must* be in one of our next-in-flows. Collect it
- // then.
- NS_ASSERTION(!IsChild(kidNextInFlow), "busted kid next-in-flow");
- break;
- }
- }
- } while (frNotComplete == status);
-
- prevKidFrame = kidFrame;
- }
-
- if (nsnull != prevKidFrame) {
- // The only way we can get here is by pulling up every last child
- // in our next-in-flows (and reflowing any continunations they
- // have). Therefore we KNOW that our last child is complete.
- NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state");
- NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
- SetLastContentOffset(prevKidFrame);
- }
-
- push_done:;
-
- if (result == PR_FALSE) {
- // If our next-in-flow is empty OR our next next-in-flow is empty
- // then adjust the offsets of all of the empty next-in-flows.
- nextInFlow = (nsBlockFrame*) mNextInFlow;
- if ((0 == nextInFlow->mChildCount) ||
- ((nsnull != nextInFlow->mNextInFlow) &&
- (0 == ((nsBlockFrame*)(nextInFlow->mNextInFlow))->mChildCount))) {
- // We didn't pullup everything and we need to fixup one of our
- // next-in-flows content offsets.
- AdjustOffsetOfEmptyNextInFlows();
- }
- }
-
-
-#ifdef NS_DEBUG
- PRInt32 len = LengthOf(mFirstChild);
- NS_ASSERTION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n",
- (result ? "" : "NOT "),
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
- return result;
-}
-
-void nsBlockFrame::SetupState(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- const nsSize& aMaxSize,
- nsSize* aMaxElementSize,
- nsISpaceManager* aSpaceManager)
-{
- // Setup reflow state
- aState.Init(aMaxSize, aMaxElementSize, mStyleContext, aSpaceManager);
-
- nsStyleSpacing* mySpacing = (nsStyleSpacing*)
- mStyleContext->GetData(kStyleSpacingSID);
-
- // Apply border and padding adjustments for regular frames only
- if (PR_FALSE == IsPseudoFrame()) {
- aState.borderPadding = mySpacing->mBorderPadding;
- aState.y = mySpacing->mBorderPadding.top;
- aState.availSize.width -=
- (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right);
- aState.availSize.height -=
- (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom);
- } else {
- aState.borderPadding.SizeTo(0, 0, 0, 0);
- }
-
- // Setup initial list ordinal value
- nsIAtom* tag = mContent->GetTag();
- if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
- (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
- nsHTMLValue value;
- if (eContentAttr_HasValue ==
- ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) {
- if (eHTMLUnit_Integer == value.GetUnit()) {
- aState.nextListOrdinal = value.GetIntValue();
- }
- }
- }
- NS_RELEASE(tag);
-
- mCurrentState = &aState;
-}
-
-#include "nsUnitConversion.h"/* XXX */
-NS_METHOD nsBlockFrame::ResizeReflow(nsIPresContext* aCX,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsSize* aMaxElementSize,
- ReflowStatus& aStatus)
-{
- nsBlockReflowState state;
- SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager);
- return DoResizeReflow(aCX, state, aDesiredRect, aStatus);
-}
-
-nsresult nsBlockFrame::DoResizeReflow(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsRect& aDesiredRect,
- ReflowStatus& aStatus)
+nsresult
+nsBlockFrame::DoResizeReflow(nsBlockReflowState& aState,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ ReflowStatus& aStatus)
{
#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
PreReflowCheck();
#endif
-#ifdef NOISY
+#ifdef NOISY_REFLOW
ListTag(stdout);
- printf(": resize reflow %g,%g\n",
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width),
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height));
- DumpFlow();
+ printf(": Before:\n");
+ List(stdout, 1);
#endif
- // Zap old line data
- if (nsnull != mLines) {
- delete mLines;
- mLines = nsnull;
- }
- mNumLines = 0;
+ nsresult rv = NS_OK;
- // Check for an overflow list
- MoveOverflowToChildList();
-
- // Before we start reflowing, cache a pointer to our state structure
- // so that inline frames can find it.
- nsIPresShell* shell = aCX->GetShell();
+ nsIPresShell* shell = aState.mPresContext->GetShell();
shell->PutCachedData(this, &aState);
- // First reflow any existing frames
- PRBool reflowMappedOK = PR_TRUE;
- aStatus = frComplete;
- if (nsnull != mFirstChild) {
- reflowMappedOK = ReflowMappedChildren(aCX, aState);
- if (!reflowMappedOK) {
- aStatus = frNotComplete;
- }
+ // Check for an overflow list
+ DrainOverflowList();
+
+ if (nsnull != mLines) {
+ rv = ReflowMapped(aState);
}
- if (reflowMappedOK) {
- // Any space left?
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- if ((nsnull != mFirstChild) && (aState.y >= yb)) {
- // No space left. Don't try to pull-up children or reflow
- // unmapped. We need to return the correct completion status,
- // so see if there is more to reflow.
- if (MoreToReflow(aCX)) {
- aStatus = frNotComplete;
- }
- } else if (MoreToReflow(aCX)) {
- // Try and pull-up some children from a next-in-flow
- if ((nsnull == mNextInFlow) || PullUpChildren(aCX, aState)) {
- // If we still have unmapped children then create some new frames
- if (MoreToReflow(aCX)) {
- aStatus = ReflowAppendedChildren(aCX, aState);
- }
- } else {
- // We were unable to pull-up all the existing frames from the
- // next in flow
- aStatus = frNotComplete;
- }
+ if (NS_OK == rv) {
+ if ((nsnull != mLines) && (aState.mAvailSize.height <= 0)) {
+ // We are out of space
}
- }
-
- // Deal with last line - make sure it gets vertically and
- // horizontally aligned. This also updates the state's y coordinate
- // which is good because that's how we size ourselves.
- if (0 != aState.lineLength) {
- if (!AdvanceToNextLine(aCX, aState)) {
- // The last line of output doesn't fit. Push all of the kids to
- // the next in flow and change our reflow status to not complete
- // so that we are continued.
-#ifdef NOISY
- ListTag(stdout);
- printf(": pushing kids since last line doesn't fit\n");
-#endif
-
- PushKids(aState);
- aStatus = frNotComplete;
+ if (MoreToReflow(aState)) {
+ rv = ReflowUnmapped(aState);
}
}
- if (frComplete == aStatus) {
- // Don't forget to add in the bottom margin from our last child.
- // Only add it in if there is room for it.
- nscoord margin = aState.prevMaxPosBottomMargin -
- aState.prevMaxNegBottomMargin;
- nscoord y = aState.y + margin;
- if (y <= aState.borderPadding.top + aState.availSize.height) {
- aState.y = y;
- }
- }
-
- // Now that reflow has finished, remove the cached pointer
- shell->RemoveCachedData(this);
- NS_RELEASE(shell);
-
- // Translate state.lineLengths into an integer array
- mNumLines = aState.lineLengths.Count();
- if (mNumLines > 0) {
- mLines = new PRInt32[mNumLines];
- for (PRInt32 i = 0; i < mNumLines; i++) {
- PRInt32 ll = (PRInt32) aState.lineLengths.ElementAt(i);
- mLines[i] = ll;
- }
- }
-
- if (!aState.unconstrainedWidth && aState.justifying) {
- // Perform justification now that we know how many lines we have.
- JustifyLines(aCX, aState);
- }
-
// Return our desired rect and our status
aDesiredRect.x = 0;
aDesiredRect.y = 0;
- aDesiredRect.width = aState.kidXMost + aState.borderPadding.right;
- if (!aState.unconstrainedWidth) {
+ aDesiredRect.width = aState.mKidXMost + aState.mBorderPadding.right;
+ if (!aState.mUnconstrainedWidth) {
// Make sure we're at least as wide as the max size we were given
- nscoord maxWidth = aState.availSize.width + aState.borderPadding.left +
- aState.borderPadding.right;
-
+ nscoord maxWidth = aState.mAvailSize.width + aState.mBorderPadding.left +
+ aState.mBorderPadding.right;
if (aDesiredRect.width < maxWidth) {
aDesiredRect.width = maxWidth;
}
}
- aState.y += aState.borderPadding.bottom;
- aDesiredRect.height = aState.y;
-
-#ifdef NS_DEBUG
- PostReflowCheck(aStatus);
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": resize reflow %g,%g %scomplete [%d,%d,%c]\n",
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width),
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height),
- ((status == frNotComplete) ? "NOT " : ""),
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F')
- );
- DumpFlow();
-#endif
- mCurrentState = nsnull;
- return NS_OK;
-}
-
-void nsBlockFrame::JustifyLines(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
- // XXX we don't justify the last line; what if we are continued,
- // should we do it then?
- nsIFrame* kid = mFirstChild;
- for (PRInt32 i = 0; i < mNumLines; i++) {
- nsIFrame* lineStart = kid;
- PRInt32 lineLength = mLines[i];
- if (i < mNumLines - 1) {
- if (1 == lineLength) {
- // For lines with one element on them we can take a shortcut and
- // let them do the justification. Note that we still call
- // JustifyReflow even if the available space is zero in case the
- // child is a hunk of text that ends in whitespace. The whitespace
- // will be distributed elsewhere causing a proper flush right look
- // for the last word.
- nsRect r;
- kid->GetRect(r);
- nscoord maxWidth = aState.availSize.width;
- nscoord availableSpace = maxWidth - r.width;
- nscoord maxAvailSpace = nscoord(maxWidth * 0.1f);
- if ((availableSpace >= 0) && (availableSpace < maxAvailSpace)) {
- kid->JustifyReflow(aCX, availableSpace);
- kid->SizeTo(r.width + availableSpace, r.height);
- }
- kid->GetNextSibling(kid);
- } else {
- // XXX Get justification of multiple elements working
- while (--lineLength >= 0) {
- kid->GetNextSibling(kid);
- }
+ aState.mY += aState.mBorderPadding.bottom;
+ nscoord lastBottomMargin = aState.mPrevMaxPosBottomMargin -
+ aState.mPrevMaxNegBottomMargin;
+ if (!aState.mUnconstrainedHeight && (lastBottomMargin > 0)) {
+ // It's possible that we don't have room for the last bottom
+ // margin (the last bottom margin is the margin following a block
+ // element that we contain; it isn't applied immediately because
+ // of the margin collapsing logic). This can happen when we are
+ // reflowed in a limited amount of space because we don't know in
+ // advance what the last bottom margin will be.
+ nscoord maxY = aMaxSize.height;
+ if (aState.mY + lastBottomMargin > maxY) {
+ lastBottomMargin = maxY - aState.mY;
+ if (lastBottomMargin < 0) {
+ lastBottomMargin = 0;
}
}
-
- // Finally, now that the in-flow positions of the line's frames are
- // known we can apply relative positioning if any of them need it.
- nsCSSLayout::RelativePositionChildren(aCX, this, lineStart, mLines[i]);
+ aState.mY += lastBottomMargin;
}
-}
+ aDesiredRect.height = aState.mY;
-NS_METHOD nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const
-{
- aIsSplittable = frSplittableNonRectangular;
- return NS_OK;
-}
-
-NS_METHOD nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX,
- nsIFrame* aParent,
- nsIFrame*& aContinuingFrame)
-{
- nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent);
- PrepareContinuingFrame(aCX, aParent, cf);
- aContinuingFrame = cf;
- return NS_OK;
-}
-
-NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aCX,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsReflowCommand& aReflowCommand,
- ReflowStatus& aStatus)
-{
+ // Set return status
aStatus = frComplete;
-
- if (aReflowCommand.GetTarget() == this) {
- // XXX for now, just do a complete reflow mapped (it'll kinda
- // work, but it's slow)
-
- nsBlockReflowState state;
- SetupState(aCX, state, aMaxSize, nsnull, aSpaceManager);
- PRBool reflowMappedOK = ReflowMappedChildren(aCX, state);
- if (!reflowMappedOK) {
- aStatus = frNotComplete;
- }
- } else {
- // XXX not yet implemented
- NS_ABORT();
- // XXX work to compute initial state goes *HERE*
- aDesiredRect.x = 0;
- aDesiredRect.y = 0;
- aDesiredRect.width = 0;
- aDesiredRect.height = 0;
+ if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) {
+ rv = NS_OK;
+ aStatus = frNotComplete;
}
+#ifdef NS_DEBUG
+ // Verify that the line layout code pulled everything up when it
+ // indicates a complete reflow.
+ if (frComplete == aStatus) {
+ nsBlockFrame* nextBlock = (nsBlockFrame*) mNextInFlow;
+ while (nsnull != nextBlock) {
+ NS_ASSERTION((nsnull == nextBlock->mLines) &&
+ (nextBlock->mChildCount == 0) &&
+ (nsnull == nextBlock->mFirstChild),
+ "bad completion status");
+ nextBlock = (nsBlockFrame*) nextBlock->mNextInFlow;
+ }
- mCurrentState = nsnull;
- return NS_OK;
+#if XXX
+ // We better not be in the same parent frame as our prev-in-flow.
+ // If we are it means that we were continued instead of pulling up
+ // children.
+ if (nsnull != mPrevInFlow) {
+ nsIFrame* ourParent = mGeometricParent;
+ nsIFrame* prevParent = ((nsBlockFrame*)mPrevInFlow)->mGeometricParent;
+ NS_ASSERTION(ourParent != prevParent, "bad continuation");
+ }
+#endif
+ }
+#endif
+
+ // Now that reflow has finished, remove the cached pointer
+ shell->RemoveCachedData(this);
+ NS_RELEASE(shell);
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+ PostReflowCheck(aStatus);
+#endif
+#ifdef NOISY_REFLOW
+ ListTag(stdout);
+ printf(": After:\n");
+ List(stdout, 1);
+#endif
+ return rv;
}
-PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
+//----------------------------------------------------------------------
+
+NS_METHOD
+nsBlockFrame::ContentAppended(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentInserted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentReplaced(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aOldChild,
+ nsIContent* aNewChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentDeleted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::GetReflowMetrics(nsIPresContext* aPresContext,
+ nsReflowMetrics& aMetrics)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+NS_METHOD
+nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ nsIFrame::ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ aStatus = frComplete;
+ nsBlockReflowState state;
+ rv = InitializeState(aPresContext, aSpaceManager, aMaxSize,
+ aMaxElementSize, state);
+ if (NS_OK == rv) {
+ nsRect desiredRect;
+ rv = DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus);
+ }
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsReflowCommand& aReflowCommand,
+ nsIFrame::ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+PRBool
+nsBlockFrame::AddFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder)
+{
+ nsIPresShell* shell = aPresContext->GetShell();
+ nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this);
+ NS_RELEASE(shell);
+
+ if (nsnull != state) {
+ // Get the frame associated with the space manager, and get its
+ // nsIAnchoredItems interface
+ nsIFrame* frame = state->mSpaceManager->GetFrame();
+ nsIAnchoredItems* anchoredItems = nsnull;
+
+ frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems);
+ NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface");
+ if (nsnull != anchoredItems) {
+ anchoredItems->AddAnchoredItem(aFloater,
+ nsIAnchoredItems::anHTMLFloater,
+ this);
+ PlaceFloater(aPresContext, aFloater, aPlaceholder, *state);
+ return PR_TRUE;
+ }
+ }
+
+ return PR_FALSE;
+}
+
+void
+nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder)
+{
+ nsIPresShell* shell = aPresContext->GetShell();
+ nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this);
+ NS_RELEASE(shell);
+ if (nsnull != state) {
+ PlaceFloater(aPresContext, aFloater, aPlaceholder, *state);
+ }
+}
+
+PRBool
+nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
{
do {
nsIFrame* parent;
-
aFrame->GetGeometricParent(parent);
- // See if there are any non-zero sized child frames that precede aFrame
- // in the child list
+ // See if there are any non-zero sized child frames that precede
+ // aFrame in the child list
nsIFrame* child;
-
parent->FirstChild(child);
while ((nsnull != child) && (aFrame != child)) {
nsSize size;
@@ -1949,7 +1285,6 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
// We found a non-zero sized child frame that precedes aFrame
return PR_FALSE;
}
-
child->GetNextSibling(child);
}
@@ -1957,184 +1292,127 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
// Walk up one level and check that its parent is left-most as well
aFrame = parent;
} while (aFrame != this);
-
return PR_TRUE;
}
-PRBool nsBlockFrame::AddFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
- PlaceholderFrame* aPlaceholder)
-{
- // Get the frame associated with the space manager, and get its nsIAnchoredItems
- // interface
- nsIFrame* frame = mCurrentState->spaceManager->GetFrame();
- nsIAnchoredItems* anchoredItems = nsnull;
-
- frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems);
- NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface");
-
- if (nsnull != anchoredItems) {
- anchoredItems->AddAnchoredItem(aFloater, nsIAnchoredItems::anHTMLFloater, this);
- PlaceFloater(aCX, aFloater, aPlaceholder);
- return PR_TRUE;
- }
-
- return PR_FALSE;
-}
-
-// XXX The size of the floater needs to be taken into consideration if we're
-// computing a maximum element size
-void nsBlockFrame::PlaceFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
- PlaceholderFrame* aPlaceholder)
+void
+nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder,
+ nsBlockReflowState& aState)
{
// If the floater is the left-most non-zero size child frame then insert
// it before the current line; otherwise add it to the below-current-line
// todo list and we'll handle it when we flush out the line
if (IsLeftMostChild(aPlaceholder)) {
- // Get the type of floater
- nsIStyleContextPtr styleContext;
+ nsISpaceManager* sm = aState.mSpaceManager;
- aFloater->GetStyleContext(aCX, styleContext.AssignRef());
+ // Get the type of floater
+ nsIStyleContextPtr styleContext;
+ aFloater->GetStyleContext(aPresContext, styleContext.AssignRef());
nsStyleDisplay* floaterDisplay = (nsStyleDisplay*)
styleContext->GetData(kStyleDisplaySID);
- if (!mCurrentState->isInline) {
- // Get the current band for this line
- GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin);
- }
-
- // Commit some space in the space manager
- nsRect region;
-
+ // Commit some space in the space manager and adjust our current
+ // band of available space.
+ nsRect region;
aFloater->GetRect(region);
- region.y = mCurrentState->currentBand->availSpace.y;
-
+ region.y = aState.mY;
if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) {
- region.x = mCurrentState->currentBand->availSpace.x;
+ region.x = aState.mX;
} else {
- NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, "bad float type");
- region.x = mCurrentState->currentBand->availSpace.XMost() - region.width;
+ NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats,
+ "bad float type");
+ region.x = aState.mCurrentBand.availSpace.XMost() - region.width;
}
// XXX Don't forget the floater's margins...
- mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, 0);
- mCurrentState->spaceManager->AddRectRegion(region, aFloater);
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->AddRectRegion(region, aFloater);
// Set the origin of the floater in world coordinates
nscoord worldX, worldY;
-
- mCurrentState->spaceManager->GetTranslation(worldX, worldY);
+ sm->GetTranslation(worldX, worldY);
aFloater->MoveTo(region.x + worldX, region.y + worldY);
- mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0);
+ sm->Translate(-aState.mBorderPadding.left, 0);
- // Update the band of available space to reflect space taken up by the floater
- GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin);
+ // Update the band of available space to reflect space taken up by
+ // the floater
+ GetAvailableSpace(aState, aState.mY);
+ if (nsnull != aState.mCurrentLine) {
+ nsLineLayout& lineLayout = *aState.mCurrentLine;
+ lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace);
+ }
} else {
// Add the floater to our to-do list
- mCurrentState->floaterToDo.AppendElement(aFloater);
+ aState.mPendingFloaters.AppendElement(aFloater);
}
}
-NS_METHOD nsBlockFrame::ContentAppended(nsIPresShell* aShell,
- nsIPresContext* aPresContext,
- nsIContent* aContainer)
+void
+nsBlockFrame::PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState,
+ nscoord aY)
{
- // Get the last-in-flow
- nsBlockFrame* flow = (nsBlockFrame*)GetLastInFlow();
- PRInt32 kidIndex = flow->NextChildOffset();
- PRInt32 startIndex = kidIndex;
+ NS_PRECONDITION(aState.mPendingFloaters.Count() > 0, "no floaters");
-#if 0
- nsIFrame* kidFrame = nsnull;
- nsIFrame* prevKidFrame;
-
- flow->LastChild(prevKidFrame);
- for (;;) {
- // Get the next content object
- nsIContentPtr kid = mContent->ChildAt(kidIndex);
- if (nsnull == kid) {
- break;
- }
+ nsISpaceManager* sm = aState.mSpaceManager;
+ nsBlockBandData* bd = &aState.mCurrentBand;
- // Resolve style for the kid
- nsIStyleContextPtr kidSC = aPresContext->ResolveStyleContextFor(kid, this);
- nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
- kidSC->GetData(kStyleDisplaySID);
+ // XXX Factor this code with PlaceFloater()...
+ PRInt32 numFloaters = aState.mPendingFloaters.Count();
+ for (PRInt32 i = 0; i < numFloaters; i++) {
+ nsIFrame* floater = (nsIFrame*) aState.mPendingFloaters[i];
+ nsRect region;
- // Is it a floater?
- if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
- PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this);
+ // Get the band of available space
+ // XXX This is inefficient to do this inside the loop...
+ GetAvailableSpace(aState, aY);
+
+ // Get the type of floater
+ nsIStyleContextPtr styleContext;
+ floater->GetStyleContext(aState.mPresContext, styleContext.AssignRef());
+ nsStyleDisplay* sd = (nsStyleDisplay*)
+ styleContext->GetData(kStyleDisplaySID);
+
+ floater->GetRect(region);
+ region.y = bd->availSpace.y;
+ if (NS_STYLE_FLOAT_LEFT == sd->mFloats) {
+ region.x = bd->availSpace.x;
} else {
- // Create initial frame for the child
- nsIContentDelegate* kidDel;
- nsresult fr;
- switch (kidDisplay->mDisplay) {
- case NS_STYLE_DISPLAY_BLOCK:
- case NS_STYLE_DISPLAY_LIST_ITEM:
- // Pseudo block frames do not contain other block elements
- // unless the block element would be the first child.
- if (IsPseudoFrame()) {
- // Update last content offset now that we are done drawing
- // children from our parent.
- SetLastContentOffset(prevKidFrame);
-
- // It better be true that we are not being asked to flow a
- // block element as our first child. That means the body
- // decided it needed a pseudo-frame when it shouldn't have.
- NS_ASSERTION(nsnull != mFirstChild, "bad body");
-
- return NS_OK;
- }
- // FALL THROUGH (and create frame)
-
- case NS_STYLE_DISPLAY_INLINE:
- kidDel = kid->GetDelegate(aPresContext);
- kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this);
- NS_RELEASE(kidDel);
- break;
-
- default:
- fr = nsFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- break;
- }
+ NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type");
+ region.x = bd->availSpace.XMost() - region.width;
}
- kidFrame->SetStyleContext(aCX,kidSC);
- // Link child frame into the list of children
- if (nsnull != prevKidFrame) {
-#ifdef NS_DEBUG
- nsIFrame* nextSibling;
+ // XXX Don't forget the floater's margins...
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->AddRectRegion(region, floater);
- prevKidFrame->GetNextSibling(nextSibling);
- NS_ASSERTION(nsnull == nextSibling, "bad append");
-#endif
- prevKidFrame->SetNextSibling(kidFrame);
- } else {
- NS_ASSERTION(nsnull == mFirstChild, "bad create");
- mFirstChild = kidFrame;
- SetFirstContentOffset(kidFrame);
- }
- prevKidFrame = kidFrame;
- kidIndex++;
- mChildCount++;
+ // Set the origin of the floater in world coordinates
+ nscoord worldX, worldY;
+ sm->GetTranslation(worldX, worldY);
+ floater->MoveTo(region.x + worldX, region.y + worldY);
+ sm->Translate(-aState.mBorderPadding.left, 0);
}
- SetLastContentOffset(prevKidFrame);
-#endif
- // If this is a pseudo-frame then our parent will generate the
- // reflow command. Otherwise, if the container is us then we should
- // generate the reflow command because we were directly called.
- if (!IsPseudoFrame() && (aContainer == mContent)) {
- nsReflowCommand* rc =
- new nsReflowCommand(aPresContext, flow, nsReflowCommand::FrameAppended,
- startIndex);
- aShell->AppendReflowCommand(rc);
+ aState.mPendingFloaters.Clear();
+
+ // Pass on updated available space to the current line
+ if (nsnull != aState.mCurrentLine) {
+ nsLineLayout& lineLayout = *aState.mCurrentLine;
+ lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace);
}
- return NS_OK;
}
-PRIntn nsBlockFrame::GetSkipSides() const
+//----------------------------------------------------------------------
+
+nsLineData*
+nsBlockFrame::GetFirstLine()
+{
+ return mLines;
+}
+
+PRIntn
+nsBlockFrame::GetSkipSides() const
{
PRIntn skip = 0;
if (nsnull != mPrevInFlow) {
@@ -2145,40 +1423,3 @@ PRIntn nsBlockFrame::GetSkipSides() const
}
return skip;
}
-
-nsHTMLFrameType nsBlockFrame::GetFrameType() const
-{
- return eHTMLFrame_Block;
-}
-
-NS_METHOD nsBlockFrame::ListTag(FILE* out) const
-{
- if ((nsnull != mGeometricParent) && IsPseudoFrame()) {
- fprintf(out, "*block<");
- nsIAtom* atom = mContent->GetTag();
- if (nsnull != atom) {
- nsAutoString tmp;
- atom->ToString(tmp);
- fputs(tmp, out);
- }
- fprintf(out, ">(%d)@%p", mIndexInParent, this);
- } else {
- nsHTMLContainerFrame::ListTag(out);
- }
- return NS_OK;
-}
-
-#ifdef NS_DEBUG
-void nsBlockFrame::DumpFlow() const
-{
-#ifdef NOISY_FLOW
- nsBlockFrame* flow = (nsBlockFrame*) mNextInFlow;
- while (flow != 0) {
- printf(" %p: [%d,%d,%c]\n",
- flow, flow->mFirstContentOffset, flow->mLastContentOffset,
- (flow->mLastContentIsComplete ? 'T' : 'F'));
- flow = (nsBlockFrame*) flow->mNextInFlow;
- }
-#endif
-}
-#endif
diff --git a/layout/generic/nsHTMLContainerFrame.h b/layout/generic/nsHTMLContainerFrame.h
index 9364e3f870b9..0d2d6879e04f 100644
--- a/layout/generic/nsHTMLContainerFrame.h
+++ b/layout/generic/nsHTMLContainerFrame.h
@@ -42,14 +42,6 @@ public:
nsIFrame** aFrame,
PRInt32& aCursor);
-#if 0
- virtual ReflowStatus IncrementalReflow(nsIPresContext* aPresContext,
- nsReflowMetrics& aDesiredSize,
- const nsSize& aMaxSize,
- nsISpaceManager* aSpaceManager,
- nsReflowCommand& aReflowCommand);
-#endif
-
protected:
virtual ~nsHTMLContainerFrame();
@@ -59,32 +51,6 @@ protected:
const nsString& aBase,
const nsString& aURLSpec,
const nsString& aTargetSpec);
-
-#if 0
- virtual ReflowStatus ReflowAppended(nsIPresContext* aPresContext,
- nsReflowMetrics& aDesiredSize,
- const nsSize& aMaxSize,
- nsISpaceManager* aSpaceManager,
- nsReflowCommand& aReflowCommand);
-
- virtual ReflowStatus ReflowInserted(nsIPresContext* aPresContext,
- nsReflowMetrics& aDesiredSize,
- const nsSize& aMaxSize,
- nsISpaceManager* aSpaceManager,
- nsReflowCommand& aReflowCommand);
-
- virtual ReflowStatus ReflowDeleted(nsIPresContext* aPresContext,
- nsReflowMetrics& aDesiredSize,
- const nsSize& aMaxSize,
- nsISpaceManager* aSpaceManager,
- nsReflowCommand& aReflowCommand);
-
- virtual ReflowStatus ReflowChanged(nsIPresContext* aPresContext,
- nsReflowMetrics& aDesiredSize,
- const nsSize& aMaxSize,
- nsISpaceManager* aSpaceManager,
- nsReflowCommand& aReflowCommand);
-#endif
};
#endif /* nsHTMLContainerFrame_h___ */
diff --git a/layout/generic/nsInlineFrame.cpp b/layout/generic/nsInlineFrame.cpp
index 4864674f0487..9cd92de6cf59 100644
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -592,42 +592,6 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext,
// FALLTHROUGH
case NS_STYLE_DISPLAY_INLINE:
- // XXX temporary hack to make plain BR's in inlines "work"
- // get style for break-before-after; get break-type (line, page, etc.)
- {
- nsIAtom* tag = kid->GetTag();
- if (nsHTMLAtoms::br == tag) {
- // Set break-after flag so we stop mapping children (we
- // will end up being continued if there are more children)
- breakAfter = PR_TRUE;
-
- // Get cached state for containing block frame
- // XXX how about QueryInterface(kIHTMLBlockFrameIID)? DOH!
- nsBlockReflowState* state = nsnull;
- nsIFrame* parent = mGeometricParent;
- while (nsnull != parent) {
- nsIHTMLFrameType* ft;
- nsresult status =
- parent->QueryInterface(kIHTMLFrameTypeIID, (void**) &ft);
- if (NS_OK == status) {
- nsHTMLFrameType type = ft->GetFrameType();
- if (eHTMLFrame_Block == type) {
- break;
- }
- }
- parent->GetGeometricParent(parent);
- }
- if (nsnull != parent) {
- nsIPresShell* shell = aPresContext->GetShell();
- state = (nsBlockReflowState*) shell->GetCachedData(parent);
- // XXX Of course this won't work if the inline span is nested
- // in another inline span!
- state->breakAfterChild = PR_TRUE;
- NS_RELEASE(shell);
- }
- }
- NS_IF_RELEASE(tag);
- }
kidDel = kid->GetDelegate(aPresContext);
kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this);
NS_RELEASE(kidDel);
diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp
index e2371682c2e1..d3ec7ec27864 100644
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -841,10 +841,10 @@ nsLineLayout::CreateFrameFor(nsIContent* aKid)
nsIFrame* kidFrame;
if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) {
AbsoluteFrame::NewFrame(&kidFrame, aKid, mKidIndex, mBlock);
- kidFrame->SetStyleContext(kidSC);
+ kidFrame->SetStyleContext(mPresContext, kidSC);
} else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
PlaceholderFrame::NewFrame(&kidFrame, aKid, mKidIndex, mBlock);
- kidFrame->SetStyleContext(kidSC);
+ kidFrame->SetStyleContext(mPresContext, kidSC);
} else if (nsnull == mKidPrevInFlow) {
// Create initial frame for the child
@@ -882,7 +882,7 @@ nsLineLayout::CreateFrameFor(nsIContent* aKid)
nsFrame::NewFrame(&kidFrame, aKid, mKidIndex, mBlock);
break;
}
- kidFrame->SetStyleContext(kidSC);
+ kidFrame->SetStyleContext(mPresContext, kidSC);
} else {
// Since kid has a prev-in-flow, use that to create the next
// frame.
diff --git a/layout/html/base/src/makefile.win b/layout/html/base/src/makefile.win
index 789faf639de4..fe83b5e0d169 100644
--- a/layout/html/base/src/makefile.win
+++ b/layout/html/base/src/makefile.win
@@ -42,6 +42,7 @@ CPPSRCS= \
nsImageMap.cpp \
nsInlineFrame.cpp \
nsLeafFrame.cpp \
+ nsLineLayout.cpp \
nsListItemFrame.cpp \
nsPageFrame.cpp \
nsPlaceholderFrame.cpp \
@@ -73,6 +74,7 @@ CPP_OBJS= \
.\$(OBJDIR)\nsImageMap.obj \
.\$(OBJDIR)\nsInlineFrame.obj \
.\$(OBJDIR)\nsLeafFrame.obj \
+ .\$(OBJDIR)\nsLineLayout.obj \
.\$(OBJDIR)\nsListItemFrame.obj \
.\$(OBJDIR)\nsPageFrame.obj \
.\$(OBJDIR)\nsPlaceholderFrame.obj \
diff --git a/layout/html/base/src/nsBRPart.cpp b/layout/html/base/src/nsBRPart.cpp
index df04ebd6036a..b2cf9a7e5a9a 100644
--- a/layout/html/base/src/nsBRPart.cpp
+++ b/layout/html/base/src/nsBRPart.cpp
@@ -28,8 +28,10 @@
#include "nsIHTMLAttributes.h"
#include "nsIStyleContext.h"
#include "nsIFontMetrics.h"
+#include "nsIRenderingContext.h"
static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID);
+static NS_DEFINE_IID(kStyleColorSID, NS_STYLECOLOR_SID);
class BRFrame : public nsFrame
{
@@ -37,6 +39,10 @@ public:
BRFrame(nsIContent* aContent,
PRInt32 aIndexInParent,
nsIFrame* aParentFrame);
+
+ NS_IMETHOD Paint(nsIPresContext& aPresContext,
+ nsIRenderingContext& aRenderingContext,
+ const nsRect& aDirtyRect);
NS_IMETHOD ResizeReflow(nsIPresContext* aPresContext,
nsReflowMetrics& aDesiredSize,
const nsSize& aMaxSize,
@@ -60,13 +66,34 @@ BRFrame::~BRFrame()
{
}
+NS_METHOD BRFrame::Paint(nsIPresContext& aPresContext,
+ nsIRenderingContext& aRenderingContext,
+ const nsRect& aDirtyRect)
+{
+ if (nsIFrame::GetShowFrameBorders()) {
+ nsStyleColor* color = (nsStyleColor*)
+ mStyleContext->GetData(kStyleColorSID);
+ float p2t = aPresContext.GetPixelsToTwips();
+ aRenderingContext.SetColor(color->mColor);
+ aRenderingContext.FillRect(0, 0, nscoord(5 * p2t), mRect.height);
+ }
+ return NS_OK;
+}
+
NS_METHOD BRFrame::GetReflowMetrics(nsIPresContext* aPresContext, nsReflowMetrics& aMetrics)
{
// We have no width, but we're the height of the default font
nsStyleFont* font =
(nsStyleFont*)mStyleContext->GetData(kStyleFontSID);
nsIFontMetrics* fm = aPresContext->GetMetricsFor(font->mFont);
+
aMetrics.width = 0;
+ if (nsIFrame::GetShowFrameBorders()) {
+ // Reserve a tiny bit of space so that our frame won't be zero
+ // sized so we get a chance to paint.
+ aMetrics.width = 1;
+ }
+
aMetrics.height = fm->GetHeight();
aMetrics.ascent = fm->GetMaxAscent();
aMetrics.descent = fm->GetMaxDescent();
@@ -81,27 +108,15 @@ NS_METHOD BRFrame::ResizeReflow(nsIPresContext* aPresContext,
ReflowStatus& aStatus)
{
// Get cached state for containing block frame
- nsBlockReflowState* state = nsnull;
- nsIFrame* parent = mGeometricParent;
- while (nsnull != parent) {
- nsIHTMLFrameType* ft;
- nsresult status = parent->QueryInterface(kIHTMLFrameTypeIID, (void**) &ft);
- if (NS_OK == status) {
- nsHTMLFrameType type = ft->GetFrameType();
- if (eHTMLFrame_Block == type) {
- break;
- }
- }
- parent->GetGeometricParent(parent);
- }
- if (nsnull != parent) {
- nsIPresShell* shell = aPresContext->GetShell();
- state = (nsBlockReflowState*) shell->GetCachedData(parent);
- NS_RELEASE(shell);
- }
+ nsLineLayout* lineLayoutState = nsnull;
+ nsBlockReflowState* state =
+ nsBlockFrame::FindBlockReflowState(aPresContext, this);
if (nsnull != state) {
- // XXX
- state->breakAfterChild = PR_TRUE;
+ lineLayoutState = state->mCurrentLine;
+ if (nsnull != lineLayoutState) {
+ lineLayoutState->mReflowResult =
+ NS_LINE_LAYOUT_REFLOW_RESULT_BREAK_AFTER;
+ }
}
GetReflowMetrics(aPresContext, aDesiredSize);
diff --git a/layout/html/base/src/nsBlockFrame.cpp b/layout/html/base/src/nsBlockFrame.cpp
index 59d7407ec5b1..0228ca80d413 100644
--- a/layout/html/base/src/nsBlockFrame.cpp
+++ b/layout/html/base/src/nsBlockFrame.cpp
@@ -16,66 +16,37 @@
* Reserved.
*/
#include "nsBlockFrame.h"
-#include "nsSize.h"
-#include "nsIAnchoredItems.h"
-#include "nsIContent.h"
-#include "nsIContentDelegate.h"
-#include "nsISpaceManager.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
-#include "nsIPresContext.h"
-#include "nsMargin.h"
-#include "nsHTMLIIDs.h"
-#include "nsCSSLayout.h"
-#include "nsCRT.h"
-#include "nsIPresShell.h"
-#include "nsReflowCommand.h"
-#include "nsPlaceholderFrame.h"
-#include "nsHTMLAtoms.h"
-#include "nsHTMLValue.h"
#include "nsIHTMLContent.h"
-#include "nsAbsoluteFrame.h"
+#include "nsIPresContext.h"
+#include "nsIPresShell.h"
+#include "nsIAnchoredItems.h"
+#include "nsPlaceholderFrame.h"
#include "nsIPtr.h"
+#include "nsHTMLAtoms.h"
+#include "nsHTMLIIDs.h"
+#include "nsHTMLValue.h"
-#ifdef NS_DEBUG
-#undef NOISY
-#undef NOISY_FLOW
-#else
-#undef NOISY
-#undef NOISY_FLOW
-#endif
+// XXX what do we do with catastrophic errors (rv < 0)? What is the
+// state of the reflow world after such an error?
-static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID);
-static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID);
-static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID);
+#undef NOISY_REFLOW
static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID);
-static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID);
-static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID);
-static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID);
static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID);
-NS_DEF_PTR(nsIStyleContext);
+static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID);
+static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID);
+static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID);
+
NS_DEF_PTR(nsIContent);
+NS_DEF_PTR(nsIStyleContext);
-struct BlockBandData : public nsBandData {
- nsBandTrapezoid data[12];
+//----------------------------------------------------------------------
- // Bounding rect of available space between any left and right floaters
- nsRect availSpace;
- // Constructor
- BlockBandData() {size = 12; trapezoids = data;}
-
- // Computes the bounding rect of the available space, i.e. space between
- // any left and right floaters
- //
- // Uses the current trapezoid data, see nsISpaceManager::GetBandData().
- // Updates member data "availSpace"
- void ComputeAvailSpaceRect();
-};
-
-void BlockBandData::ComputeAvailSpaceRect()
+void nsBlockBandData::ComputeAvailSpaceRect()
{
nsBandTrapezoid* trapezoid = data;
@@ -121,198 +92,60 @@ void BlockBandData::ComputeAvailSpaceRect()
}
}
-// XXX Bugs
-// 1. right to left reflow can generate negative x coordinates.
-
-// XXX Speedup idea (all containers)
-
-// If I reflow a child and it gives back not-complete status then
-// there is no sense in trying to pullup children. For blocks, it's a
-// little more complicated unless the child is a block - if the child
-// is a block, then we must be out of room hence we should stop. If
-// the child is not a block then our line should be flushed (see #2
-// below) if our line is already empty then we must be out of room.
-
-// For inline frames and column frames, if we reflow a child and get
-// back not-complete status then we should bail immediately because we
-// are out of room.
-
-// XXX Speedup ideas:
-// 1. change pullup code to use line information from next in flow
-// 2. we can advance to next line immediately after reflowing something
-// and noticing that it's not complete.
-// 3. pass down last child information in aState so that pullup, etc.,
-// don't need to recompute it
-
-// XXX TODO:
-// 0. Move justification into line flushing code
-
-// 1. To get ebina margins I need "auto" information from the style
-// system margin's. A bottom/top margin of auto will then be computed like
-// ebina computes it [however the heck that is].
-
-// 2. kicking out floaters and talking with floater container to adjust
-// left and right margins
+//----------------------------------------------------------------------
nsBlockReflowState::nsBlockReflowState()
{
}
-void nsBlockReflowState::Init(const nsSize& aMaxSize,
- nsSize* aMaxElementSize,
- nsIStyleContext* aBlockSC,
- nsISpaceManager* aSpaceManager)
+nsBlockReflowState::~nsBlockReflowState()
{
- firstLine = PR_TRUE;
- allowLeadingWhitespace = PR_FALSE;
- breakAfterChild = PR_FALSE;
- breakBeforeChild = PR_FALSE;
- firstChildIsInsideBullet = PR_FALSE;
- nextListOrdinal = -1;
- column = 0;
+}
- spaceManager = aSpaceManager;
- currentBand = new BlockBandData;
+nsresult
+nsBlockReflowState::Initialize(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockFrame* aBlock)
+{
+ nsresult rv = NS_OK;
- styleContext = aBlockSC;
- styleText = (nsStyleText*) aBlockSC->GetData(kStyleTextSID);
- styleFont = (nsStyleFont*) aBlockSC->GetData(kStyleFontSID);
- styleDisplay = (nsStyleDisplay*) aBlockSC->GetData(kStyleDisplaySID);
+ mPresContext = aPresContext;
+ mBlock = aBlock;
+ mSpaceManager = aSpaceManager;
+ mBlockIsPseudo = aBlock->IsPseudoFrame();
+ mCurrentLine = nsnull;
+ mPrevKidFrame = nsnull;
- justifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
- (NS_STYLE_WHITESPACE_PRE != styleText->mWhiteSpace);
-
- availSize.width = aMaxSize.width;
- availSize.height = aMaxSize.height;
- maxElementSize = aMaxElementSize;
+ mX = 0;
+ mY = 0;
+ mAvailSize = aMaxSize;
+ mUnconstrainedWidth = PRBool(mAvailSize.width == NS_UNCONSTRAINEDSIZE);
+ mUnconstrainedHeight = PRBool(mAvailSize.height == NS_UNCONSTRAINEDSIZE);
+ mMaxElementSizePointer = aMaxElementSize;
if (nsnull != aMaxElementSize) {
aMaxElementSize->width = 0;
aMaxElementSize->height = 0;
}
+ mKidXMost = 0;
- kidXMost = 0;
- x = 0;
- y = 0;
+ mPrevMaxPosBottomMargin = 0;
+ mPrevMaxNegBottomMargin = 0;
- isInline = PR_FALSE;
- currentLineNumber = 0;
- lineStart = nsnull;
- lineLength = 0;
- ascents = ascentBuf;
- maxAscent = 0;
- maxDescent = 0;
- lineWidth = 0;
- maxPosBottomMargin = 0;
- maxNegBottomMargin = 0;
- lineMaxElementSize.width = 0;
- lineMaxElementSize.height = 0;
- lastContentIsComplete = PR_TRUE;
+ mNextListOrdinal = -1;
+ mFirstChildIsInsideBullet = PR_FALSE;
- maxAscents = sizeof(ascentBuf) / sizeof(ascentBuf[0]);
- needRelativePos = PR_FALSE;
-
- prevLineLastFrame = nsnull;
- prevLineHeight = 0;
- topMargin = 0;
- prevMaxPosBottomMargin = 0;
- prevMaxNegBottomMargin = 0;
- prevLineLastContentIsComplete = PR_TRUE;
-
- unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE);
- unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE);
-
- reflowStatus = nsIFrame::frNotComplete;
+ return rv;
}
-nsBlockReflowState::~nsBlockReflowState()
-{
- if (ascents != ascentBuf) {
- delete ascents;
- }
- delete currentBand;
-}
-
-void nsBlockReflowState::AddAscent(nscoord aAscent)
-{
- NS_PRECONDITION(lineLength <= maxAscents, "bad line length");
- if (lineLength == maxAscents) {
- maxAscents *= 2;
- nscoord* newAscents = new nscoord[maxAscents];
- if (nsnull != newAscents) {
- nsCRT::memcpy(newAscents, ascents, sizeof(nscoord) * lineLength);
- if (ascents != ascentBuf) {
- delete ascents;
- }
- ascents = newAscents;
- } else {
- // Yikes! Out of memory!
- return;
- }
- }
- ascents[lineLength] = aAscent;
-}
-
-void nsBlockReflowState::AdvanceToNextLine(nsIFrame* aPrevLineLastFrame,
- nscoord aPrevLineHeight)
-{
- firstLine = PR_FALSE;
- allowLeadingWhitespace = PR_FALSE;
- column = 0;
- breakAfterChild = PR_FALSE;
- breakBeforeChild = PR_FALSE;
- lineStart = nsnull;
- lineLength = 0;
- currentLineNumber++;
- maxAscent = 0;
- maxDescent = 0;
- lineWidth = 0;
- needRelativePos = PR_FALSE;
-
- prevLineLastFrame = aPrevLineLastFrame;
- prevLineHeight = aPrevLineHeight;
- prevMaxPosBottomMargin = maxPosBottomMargin;
- prevMaxNegBottomMargin = maxNegBottomMargin;
-
- // Remember previous line's lastContentIsComplete
- prevLineLastContentIsComplete = lastContentIsComplete;
- lastContentIsComplete = PR_TRUE;
-
- topMargin = 0;
- maxPosBottomMargin = 0;
- maxNegBottomMargin = 0;
-}
-
-#ifdef NS_DEBUG
-void nsBlockReflowState::DumpLine()
-{
- nsIFrame* f = lineStart;
- PRInt32 ll = lineLength;
- while (--ll >= 0) {
- printf(" ");
- ((nsFrame*)f)->ListTag(stdout);/* XXX */
- printf("\n");
- f->GetNextSibling(f);
- }
-}
-
-void nsBlockReflowState::DumpList()
-{
- nsIFrame* f = lineStart;
- while (nsnull != f) {
- printf(" ");
- ((nsFrame*)f)->ListTag(stdout);/* XXX */
- printf("\n");
- f->GetNextSibling(f);
- }
-}
-#endif
-
//----------------------------------------------------------------------
-nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
- nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent)
+nsresult
+nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
+ nsIContent* aContent,
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
if (nsnull == aInstancePtrResult) {
@@ -327,963 +160,818 @@ nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
}
nsBlockFrame::nsBlockFrame(nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent)
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent)
: nsHTMLContainerFrame(aContent, aIndexInParent, aParent)
{
}
nsBlockFrame::~nsBlockFrame()
{
- if (nsnull != mLines) {
- delete mLines;
- }
+ DestroyLines();
}
-nsresult
+void nsBlockFrame::DestroyLines()
+{
+}
+
+NS_METHOD
nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
NS_PRECONDITION(0 != aInstancePtr, "null ptr");
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
}
- if (aIID.Equals(kIHTMLFrameTypeIID)) {
- *aInstancePtr = (void*) ((nsIHTMLFrameType*) this);
+ if (aIID.Equals(kBlockFrameCID)) {
+ *aInstancePtr = (void*) (this);
return NS_OK;
- } else if (aIID.Equals(kIRunaroundIID)) {
+ }
+ else if (aIID.Equals(kIRunaroundIID)) {
*aInstancePtr = (void*) ((nsIRunaround*) this);
return NS_OK;
- } else if (aIID.Equals(kIFloaterContainerIID)) {
+ }
+ else if (aIID.Equals(kIFloaterContainerIID)) {
*aInstancePtr = (void*) ((nsIFloaterContainer*) this);
return NS_OK;
}
return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
}
-// Computes the top margin to use for this child frames based on its display
-// type and the display type of the previous child frame.
-//
-// Adjacent vertical margins between block-level elements are collapsed.
-nscoord nsBlockFrame::GetTopMarginFor(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC,
- PRBool aIsInline)
+NS_METHOD
+nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const
{
- if (aIsInline) {
- // Just use whatever the previous bottom margin was
- return aState.prevMaxPosBottomMargin - aState.prevMaxNegBottomMargin;
+ aIsSplittable = frSplittableNonRectangular;
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX,
+ nsIFrame* aParent,
+ nsIFrame*& aContinuingFrame)
+{
+ nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent);
+ PrepareContinuingFrame(aCX, aParent, cf);
+ aContinuingFrame = cf;
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::ListTag(FILE* out) const
+{
+ if ((nsnull != mGeometricParent) && IsPseudoFrame()) {
+ fprintf(out, "*block<");
+ nsIAtom* atom = mContent->GetTag();
+ if (nsnull != atom) {
+ nsAutoString tmp;
+ atom->ToString(tmp);
+ fputs(tmp, out);
+ }
+ fprintf(out, ">(%d)@%p", mIndexInParent, this);
} else {
- // Does the frame have a prev-in-flow?
- nsIFrame* kidPrevInFlow;
+ nsHTMLContainerFrame::ListTag(out);
+ }
+ return NS_OK;
+}
- aKidFrame->GetPrevInFlow(kidPrevInFlow);
+NS_METHOD
+nsBlockFrame::List(FILE* out, PRInt32 aIndent) const
+{
+ // Indent
+ for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
- if (nsnull == kidPrevInFlow) {
+ // Output the tag
+ ListTag(out);
+
+ // Output the first/last content offset
+ fprintf(out, "[%d,%d,%c] pif=%p nif=%p",
+ mFirstContentOffset, mLastContentOffset,
+ (mLastContentIsComplete ? 'T' : 'F'),
+ mPrevInFlow, mNextInFlow);
+
+ // Output the rect
+ out << mRect;
+
+ // Output the children, one line at a time
+ if (nsnull != mLines) {
+ fputs("<\n", out);
+ aIndent++;
+
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ line->List(out, aIndent);
+ line = line->mNextLine;
+ }
+
+ aIndent--;
+ for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
+ fputs(">\n", out);
+ } else {
+ fputs("<>\n", out);
+ }
+
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::VerifyTree() const
+{
+ nsresult rv = nsHTMLContainerFrame::VerifyTree();
+ if (NS_OK != rv) {
+ return rv;
+ }
+ rv = VerifyLines(PR_TRUE);
+ return rv;
+}
+
+nsresult
+nsBlockFrame::VerifyLines(PRBool aFinalCheck) const
+{
+ nsresult rv = NS_OK;
+
+ // Make sure that the list of children agrees with our child count.
+ // If this is not the case then the child list and the line list are
+ // not properly arranged.
+ PRInt32 len = LengthOf(mFirstChild);
+ NS_ASSERTION(mChildCount == len, "bad child list");
+
+ // Verify that our lines are correctly setup
+ PRInt32 offset = mFirstContentOffset;
+ PRInt32 lineChildCount = 0;
+ nsLineData* line = mLines;
+ nsLineData* prevLine = nsnull;
+ while (nsnull != line) {
+ if (aFinalCheck) {
+ NS_ASSERTION(((offset == line->mFirstContentOffset) &&
+ (line->mFirstContentOffset <= line->mLastContentOffset)),
+ "bad line mFirstContentOffset");
+ NS_ASSERTION(line->mLastContentOffset <= mLastContentOffset,
+ "bad line mLastContentOffset");
+ offset = line->mLastContentOffset;
+ if (line->mLastContentIsComplete) {
+ offset++;
+ }
+ }
+ lineChildCount += line->mChildCount;
+ rv = line->Verify(aFinalCheck);
+ if (NS_OK != rv) {
+ return rv;
+ }
+ prevLine = line;
+ line = line->mNextLine;
+ }
+ if (aFinalCheck && (nsnull != prevLine)) {
+ NS_ASSERTION(prevLine->mLastContentOffset == mLastContentOffset,
+ "bad mLastContentOffset");
+ NS_ASSERTION(prevLine->mLastContentIsComplete == mLastContentIsComplete,
+ "bad mLastContentIsComplete");
+ }
+ NS_ASSERTION(lineChildCount == mChildCount, "bad line counts");
+
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+// Remove a next-in-flow from from this block's list of lines
+
+// XXX problems here:
+// 1. we always have to start from the first line: slow!
+// 2. we can avoid this when the child is not the last child in a line
+
+void
+nsBlockFrame::WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow)
+{
+ // When a reflow indicates completion it's possible that
+ // next-in-flows were just removed. We have to remove them from any
+ // nsLineData's that follow the current line.
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ if (line->mFirstChild == aNextInFlow) {
+ // Remove child from line.
+ if (0 == --line->mChildCount) {
+ line->mFirstChild = nsnull;
+ }
+ else {
+ // Fixup the line
+ nsIFrame* nextKid;
+ aNextInFlow->GetNextSibling(nextKid);
+ line->mFirstChild = nextKid;
+ nextKid->GetIndexInParent(line->mFirstContentOffset);
+ }
+ break;
+ }
+ line = line->mNextLine;
+ }
+}
+
+nsresult
+nsBlockFrame::ReflowInlineChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ aStatus = ReflowChild(aKidFrame, aPresContext, aDesiredSize, aMaxSize,
+ aMaxElementSize);
+ return NS_OK;
+}
+
+nsresult
+nsBlockFrame::ReflowBlockChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ aStatus = ReflowChild(aKidFrame, aPresContext, aSpaceManager,
+ aMaxSize, aDesiredRect, aMaxElementSize);
+ return NS_OK;
+}
+
+nsLineData*
+nsBlockFrame::CreateLineForOverflowList(nsIFrame* aOverflowList)
+{
+ nsLineData* newLine = new nsLineData();
+ if (nsnull != newLine) {
+ nsIFrame* kid = aOverflowList;
+ newLine->mFirstChild = kid;
+ kid->GetIndexInParent(newLine->mFirstContentOffset);
+ newLine->mLastContentOffset = -1;
+ newLine->mLastContentIsComplete = PRPackedBool(0x255);
+ PRInt32 kids = 0;
+ while (nsnull != kid) {
+ kids++;
+ kid->GetNextSibling(kid);
+ }
+ newLine->mChildCount = kids;
+ }
+ return newLine;
+}
+
+void
+nsBlockFrame::DrainOverflowList()
+{
+ nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow;
+ if (nsnull != prevBlock) {
+ nsIFrame* overflowList = prevBlock->mOverflowList;
+ if (nsnull != overflowList) {
+ NS_ASSERTION(nsnull == mFirstChild, "bad overflow list");
+ NS_ASSERTION(nsnull == mLines, "bad overflow list");
+
+ // Create a line to hold the entire overflow list
+ nsLineData* newLine = CreateLineForOverflowList(overflowList);
+
+ // Place the children on our child list; this also reassigns
+ // their geometric parent and updates our mChildCount.
+ AppendChildren(overflowList);
+ prevBlock->mOverflowList = nsnull;
+
+ // The new line is the first line
+ mLines = newLine;
+ }
+ }
+
+ if (nsnull != mOverflowList) {
+ NS_ASSERTION(nsnull != mFirstChild,
+ "overflow list but no mapped children");
+
+ // Create a line to hold the overflow list
+ nsLineData* newLine = CreateLineForOverflowList(mOverflowList);
+
+ // Place the children on our child list; this also reassigns
+ // their geometric parent and updates our mChildCount.
+ AppendChildren(mOverflowList, PR_FALSE);
+ mOverflowList = nsnull;
+
+ // The new line is appended after our other lines
+ nsLineData* prevLine = nsnull;
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ prevLine = line;
+ line = line->mNextLine;
+ }
+ if (nsnull == prevLine) {
+ mLines = newLine;
+ }
+ else {
+ prevLine->mNextLine = newLine;
+ newLine->mPrevLine = prevLine;
+ }
+ }
+#ifdef NS_DEBUG
+ VerifyLines(PR_FALSE);
+#endif
+}
+
+// XXX add in code here that notices if margin's were not provided by
+// the style system and when that is the case to apply the old layout
+// engines margin calculations.
+
+nsresult
+nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
+ nsLineLayout& aLineLayout,
+ nsLineData* aLine)
+{
+ nsresult rv = NS_LINE_LAYOUT_COMPLETE;
+
+ nscoord topMargin = 0;
+ nscoord bottomMargin = 0;
+ nscoord maxNegBottomMargin = 0;
+ nscoord maxPosBottomMargin = 0;
+
+ // See if block margins apply to this line or not
+ PRBool isBlockLine = PR_FALSE;
+ if (1 == aLine->mChildCount) {
+ nsIStyleContextPtr kidSC;
+ nsIFrame* kid = aLine->mFirstChild;
+ rv = kid->GetStyleContext(aState.mPresContext, kidSC.AssignRef());
+ if (NS_OK != rv) return rv;
+
+ nsStyleDisplay* display = (nsStyleDisplay*)
+ kidSC->GetData(kStyleDisplaySID);
+ switch (display->mDisplay) {
+ case NS_STYLE_DISPLAY_BLOCK:
+ case NS_STYLE_DISPLAY_LIST_ITEM:
+ isBlockLine = PR_TRUE;
+ nsStyleSpacing* spacing = (nsStyleSpacing*)
+ kidSC->GetData(kStyleSpacingSID);
+
+ // Calculate top margin by collapsing with previous bottom margin
+ // if any.
nscoord maxNegTopMargin = 0;
nscoord maxPosTopMargin = 0;
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- if (ss->mMargin.top < 0) {
- maxNegTopMargin = -ss->mMargin.top;
+ if (spacing->mMargin.top < 0) {
+ maxNegTopMargin = -spacing->mMargin.top;
} else {
- maxPosTopMargin = ss->mMargin.top;
+ maxPosTopMargin = spacing->mMargin.top;
}
-
- nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin);
- nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin);
- return maxPos - maxNeg;
- } else {
- return 0;
+ nscoord maxPos = PR_MAX(aState.mPrevMaxPosBottomMargin, maxPosTopMargin);
+ nscoord maxNeg = PR_MAX(aState.mPrevMaxNegBottomMargin, maxNegTopMargin);
+ topMargin = maxPos - maxNeg;
+
+ // Save away bottom information for later promotion into aState
+ if (spacing->mMargin.bottom < 0) {
+ maxNegBottomMargin = -spacing->mMargin.bottom;
+ } else {
+ maxPosBottomMargin = spacing->mMargin.bottom;
+ }
+ break;
}
}
+
+ // Before we move the line, make sure that it will fit in it's new
+ // location. It always fits if the height isn't constrained or it's
+ // the first line.
+ nscoord totalHeight = topMargin + aLine->mBounds.height;
+ if (!aState.mUnconstrainedHeight && (aLine != mLines)) {
+ if (aState.mY + totalHeight > aState.mAvailSize.height) {
+ // The line will not fit
+ rv = PushLines(aState, aLine);
+ goto done;
+ }
+ }
+ if (isBlockLine) {
+ if (0 != topMargin) {
+ // We have to move the line now that we know the top margin
+ // for it.
+ aLine->MoveLineBy(0, topMargin);
+ aState.mY += topMargin;
+ }
+ }
+ else {
+ // Apply previous line's bottom margin before the inline-line.
+ nscoord bottomMargin = aState.mPrevMaxPosBottomMargin -
+ aState.mPrevMaxNegBottomMargin;
+ if (0 != bottomMargin) {
+ aLine->MoveLineBy(0, bottomMargin);
+ aState.mY += topMargin;
+ }
+ }
+
+ // Consume space and advance running values
+ aState.mY += aLine->mBounds.height;
+ aState.mPrevMaxNegBottomMargin = maxNegBottomMargin;
+ aState.mPrevMaxPosBottomMargin = maxPosBottomMargin;
+ if (nsnull != aState.mMaxElementSizePointer) {
+ nsSize* maxSize = aState.mMaxElementSizePointer;
+ if (aLineLayout.mReflowData.mMaxElementSize.width > maxSize->width) {
+ maxSize->width = aLineLayout.mReflowData.mMaxElementSize.width;
+ }
+ if (aLineLayout.mReflowData.mMaxElementSize.height > maxSize->height) {
+ maxSize->height = aLineLayout.mReflowData.mMaxElementSize.height;
+ }
+ }
+ {
+ nscoord xmost = aLine->mBounds.XMost();
+ if (xmost > aState.mKidXMost) {
+ aState.mKidXMost = xmost;
+ }
+ }
+
+ // Any below current line floaters to place?
+ if (aState.mPendingFloaters.Count() > 0) {
+ PlaceBelowCurrentLineFloaters(aState, aState.mY);
+ // XXX Factor in the height of the floaters as well when considering
+ // whether the line fits.
+ // The default policy is that if there isn't room for the floaters then
+ // both the line and the floaters are pushed to the next-in-flow...
+ }
+
+ if (aState.mY >= aState.mCurrentBand.availSpace.YMost()) {
+ // The current y coordinate is now past our available space
+ // rectangle. Get a new band of space.
+ GetAvailableSpace(aState, aState.mY);
+ }
+
+done:
+ return rv;
}
-void nsBlockFrame::PlaceBelowCurrentLineFloaters(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nscoord aY)
+// aY has borderpadding.top already factored in
+// aResult is relative to left,aY
+nsresult
+nsBlockFrame::GetAvailableSpace(nsBlockReflowState& aState, nscoord aY)
{
- NS_PRECONDITION(aState.floaterToDo.Count() > 0, "no floaters");
+ nsresult rv = NS_OK;
- // XXX Factor this code with PlaceFloater()...
- PRInt32 numFloaters = aState.floaterToDo.Count();
-
- for (PRInt32 i = 0; i < numFloaters; i++) {
- nsIFrame* floater = (nsIFrame*)aState.floaterToDo[i];
- nsRect region;
+ nsISpaceManager* sm = aState.mSpaceManager;
- // Get the band of available space
- // XXX This is inefficient to do this inside the loop...
- GetAvailableSpaceBand(aState, aY);
+ // Fill in band data for the specific Y coordinate
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->GetBandData(aY, aState.mAvailSize, aState.mCurrentBand);
+ sm->Translate(-aState.mBorderPadding.left, 0);
- // Get the type of floater
- nsIStyleContextPtr styleContext;
-
- floater->GetStyleContext(aCX, styleContext.AssignRef());
- nsStyleDisplay* sd = (nsStyleDisplay*)
- styleContext->GetData(kStyleDisplaySID);
-
- floater->GetRect(region);
- region.y = mCurrentState->currentBand->availSpace.y;
+ // Compute the bounding rect of the available space, i.e. space
+ // between any left and right floaters
+ aState.mCurrentBand.ComputeAvailSpaceRect();
- if (NS_STYLE_FLOAT_LEFT == sd->mFloats) {
- region.x = mCurrentState->currentBand->availSpace.x;
- } else {
- NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type");
- region.x = mCurrentState->currentBand->availSpace.XMost() - region.width;
- }
-
- // XXX Don't forget the floater's margins...
- mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left,
- 0);
- mCurrentState->spaceManager->AddRectRegion(region, floater);
-
- // Set the origin of the floater in world coordinates
- nscoord worldX, worldY;
-
- mCurrentState->spaceManager->GetTranslation(worldX, worldY);
- floater->MoveTo(region.x + worldX, region.y + worldY);
- mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0);
- }
- aState.floaterToDo.Clear();
-}
-
-/**
- * Flush a line out. Return true if the line fits in our available
- * height. If the line does not fit then return false. When the line
- * fits we advance the y coordinate, reset the x coordinate and
- * prepare the nsBlockReflowState for the next line.
- */
-PRBool nsBlockFrame::AdvanceToNextLine(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
- NS_PRECONDITION(aState.lineLength > 0, "bad line");
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
-
- nscoord y = aState.y + aState.topMargin;
- nscoord lineHeight;
-
- if (aState.isInline) {
- // Vertically align the children on this line, returning the height of
- // the line upon completion.
- lineHeight = nsCSSLayout::VerticallyAlignChildren(aCX, this,
- aState.styleFont,
- y,
- aState.lineStart,
- aState.lineLength,
- aState.ascents,
- aState.maxAscent);
-
- // Any below current line floaters to place?
- if (aState.floaterToDo.Count() > 0) {
- PlaceBelowCurrentLineFloaters(aCX, aState, y + lineHeight);
- // XXX Factor in the height of the floaters as well when considering
- // whether the line fits.
- // The default policy is that if there isn't room for the floaters then
- // both the line and the floaters are pushed to the next-in-flow...
- }
- } else {
- nsSize size;
-
- aState.lineStart->GetSize(size);
- lineHeight = size.height;
- }
-
- // The first line always fits
- if (aState.currentLineNumber > 0) {
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- if (y + lineHeight > yb) {
- // After vertical alignment of the children and factoring in the
- // proper margin, the line doesn't fit.
- return PR_FALSE;
- }
- }
-
- if (aState.isInline) {
- // Check if the right-edge of the line exceeds our running x-most
- nscoord xMost = aState.borderPadding.left + aState.lineWidth;
- if (xMost > aState.kidXMost) {
- aState.kidXMost = xMost;
- }
- }
-
- // Advance the y coordinate to the new position where the next
- // line or block element will go.
- aState.y = y + lineHeight;
- aState.x = 0;
-
- // Now that the vertical alignment is done we can perform horizontal
- // alignment and relative positioning. Skip all of these if we are
- // doing an unconstrained (in x) reflow. There's no point in doing
- // the work if we *know* we are going to reflowed again.
- if (!aState.unconstrainedWidth) {
- nsCSSLayout::HorizontallyPlaceChildren(aCX, this, aState.styleText,
- aState.lineStart, aState.lineLength,
- aState.lineWidth,
- aState.availSize.width);
-
- // Finally, now that the in-flow positions of the line's frames are
- // known we can apply relative positioning if any of them need it.
- if (!aState.justifying) {
- nsCSSLayout::RelativePositionChildren(aCX, this,
- aState.lineStart,
- aState.lineLength);
- }
- }
-
- // Record line length
- aState.lineLengths.AppendElement((void*)aState.lineLength);
-
- // Find the last frame in the line
- // XXX keep this as running state in the nsBlockReflowState
- nsIFrame* lastFrame = aState.lineStart;
- PRInt32 lineLen = aState.lineLength - 1;
- while (--lineLen >= 0) {
- lastFrame->GetNextSibling(lastFrame);
- }
-
- // Update maxElementSize
- if (nsnull != aState.maxElementSize) {
- nsSize& lineMax = aState.lineMaxElementSize;
- nsSize* maxMax = aState.maxElementSize;
- if (lineMax.width > maxMax->width) {
- maxMax->width = lineMax.width;
- }
- if (lineMax.height > maxMax->height) {
- maxMax->height = lineMax.height;
- }
- aState.lineMaxElementSize.width = 0;
- aState.lineMaxElementSize.height = 0;
- }
-
- // Advance to the next line
- aState.AdvanceToNextLine(lastFrame, lineHeight);
-
- return PR_TRUE;
-}
-
-/**
- * Add an inline child to the current line. Advance various running
- * values after placement.
- */
-void nsBlockFrame::AddInlineChildToLine(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsReflowMetrics& aKidSize,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC)
-{
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
-
- nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID);
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID);
-
- if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) {
- aState.needRelativePos = PR_TRUE;
- }
-
- // Place and size the child
- // XXX add in left margin from kid
- nsRect r;
- r.y = aState.y;
- r.width = aKidSize.width;
- r.height = aKidSize.height;
- if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) {
- // Left to right positioning.
- r.x = aState.borderPadding.left + aState.x + ss->mMargin.left;
- aState.x += aKidSize.width + ss->mMargin.left + ss->mMargin.right;
- } else {
- // Right to left positioning
- // XXX what should we do when aState.x goes negative???
- r.x = aState.x - aState.borderPadding.right - ss->mMargin.right -
- aKidSize.width;
- aState.x -= aKidSize.width + ss->mMargin.right + ss->mMargin.left;
- }
- aKidFrame->SetRect(r);
- aState.AddAscent(aKidSize.ascent);
- aState.lineWidth += aKidSize.width;
- aState.lineLength++;
-
- // Update maximums for the line
- if (aKidSize.ascent > aState.maxAscent) {
- aState.maxAscent = aKidSize.ascent;
- }
- if (aKidSize.descent > aState.maxDescent) {
- aState.maxDescent = aKidSize.descent;
- }
- // Update running margin maximums
- if (aState.firstChildIsInsideBullet && (aKidFrame == mFirstChild)) {
- // XXX temporary code. Since the molecule for the bullet frame
- // is the same as the LI frame, we get bad style information.
- // ignore it.
- } else {
- nscoord margin;
#if 0
- // XXX CSS2 spec says that top/bottom margin don't affect line height
- // calculation. We're waiting for clarification on this issue...
- if ((margin = ss->mMargin.top) < 0) {
- margin = -margin;
- if (margin > aState.maxNegTopMargin) {
- aState.maxNegTopMargin = margin;
- }
- } else {
- if (margin > aState.maxPosTopMargin) {
- aState.maxPosTopMargin = margin;
- }
- }
+ // XXX For now we assume that there are no height restrictions
+ // (e.g. no "float to bottom of column/page")
+ nsRect& availSpace = aState.mCurrentBand.availSpace;
+ aResult.x = availSpace.x;
+ aResult.y = availSpace.y;
+ aResult.width = availSpace.width;
+ aResult.height = aState.mAvailSize.height;
+#else
+ aState.mCurrentBand.availSpace.MoveBy(aState.mBorderPadding.left,
+ aState.mY);
#endif
- if ((margin = ss->mMargin.bottom) < 0) {
- margin = -margin;
- if (margin > aState.maxNegBottomMargin) {
- aState.maxNegBottomMargin = margin;
- }
- } else {
- if (margin > aState.maxPosBottomMargin) {
- aState.maxPosBottomMargin = margin;
- }
- }
- }
- // Update line max element size
- nsSize& mes = aState.lineMaxElementSize;
- if (nsnull != aKidMaxElementSize) {
- if (aKidMaxElementSize->width > mes.width) {
- mes.width = aKidMaxElementSize->width;
- }
- if (aKidMaxElementSize->height > mes.height) {
- mes.height = aKidMaxElementSize->height;
- }
- }
+ return rv;
}
-// Places and sizes the block-level element, and advances the line.
-// The rect is in the local coordinate space of the kid frame.
-void nsBlockFrame::AddBlockChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsRect& aKidRect,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC)
+// Give aLine and any successive lines to the block's next-in-flow; if
+// we don't have a next-in-flow then push all the children onto our
+// overflow list.
+nsresult
+nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineData* aLine)
{
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
+ PRInt32 i;
- nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID);
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID);
-
- if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) {
- aState.needRelativePos = PR_TRUE;
+ // Split our child-list in two; revert our last content offset and
+ // completion status to the previous line.
+ nsLineData* prevLine = aLine->mPrevLine;
+ NS_PRECONDITION(nsnull != prevLine, "pushing first line");
+ nsIFrame* prevKidFrame = prevLine->mFirstChild;
+ for (i = prevLine->mChildCount - 1; --i >= 0; ) {
+ prevKidFrame->GetNextSibling(prevKidFrame);
}
-
- // Translate from the kid's coordinate space to our coordinate space
- aKidRect.x += aState.borderPadding.left + ss->mMargin.left;
- aKidRect.y += aState.y + aState.topMargin;
-
- // Place and size the child
- aKidFrame->SetRect(aKidRect);
-
- aState.AddAscent(aKidRect.height);
- aState.lineLength++;
-
- // Is this the widest child frame?
- nscoord xMost = aKidRect.XMost() + ss->mMargin.right;
- if (xMost > aState.kidXMost) {
- aState.kidXMost = xMost;
- }
-
- // Update the max element size
- if (nsnull != aKidMaxElementSize) {
- if (aKidMaxElementSize->width > aState.maxElementSize->width) {
- aState.maxElementSize->width = aKidMaxElementSize->width;
- }
- if (aKidMaxElementSize->height > aState.maxElementSize->height) {
- aState.maxElementSize->height = aKidMaxElementSize->height;
- }
- }
-
- // and the bottom line margin information which we'll use when placing
- // the next child
- if (ss->mMargin.bottom < 0) {
- aState.maxNegBottomMargin = -ss->mMargin.bottom;
- } else {
- aState.maxPosBottomMargin = ss->mMargin.bottom;
- }
-
- // Update the running y-offset
- aState.y += aKidRect.height + aState.topMargin;
-
- // Apply relative positioning if necessary
- nsCSSLayout::RelativePositionChildren(aCX, this, aKidFrame, 1);
-
- // Advance to the next line
- aState.AdvanceToNextLine(aKidFrame, aKidRect.height);
-}
-
-/**
- * Compute the available size for reflowing the given child at the
- * current x,y position in the state. Note that this may return
- * negative or zero width/height's if we are out of room.
- */
-void nsBlockFrame::GetAvailSize(nsSize& aResult,
- nsBlockReflowState& aState,
- nsIStyleContext* aKidSC,
- PRBool aIsInline)
-{
- // Determine the maximum available reflow height for the child
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- aResult.height = aState.unconstrainedHeight ? NS_UNCONSTRAINEDSIZE :
- yb - aState.y - aState.topMargin;
-
- // Determine the maximum available reflow width for the child
- if (aState.unconstrainedWidth) {
- aResult.width = NS_UNCONSTRAINEDSIZE;
- } else if (aIsInline) {
- if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) {
- aResult.width = aState.currentBand->availSpace.XMost() - aState.x;
- } else {
- aResult.width = aState.x - aState.currentBand->availSpace.x;
- }
- } else {
- // It's a block. Don't adjust for the left/right margin here. That happens
- // later on once we know the current left/right edge
- aResult.width = aState.availSize.width;
- }
-}
-
-/**
- * Push all of the kids that we have not reflowed, starting at
- * aState.lineStart. aPrevKid is the kid previous to aState.lineStart
- * and is also our last child. Note that line length is NOT a
- * reflection of the number of children we are actually pushing
- * (because we don't break the sibling list as we add children to the
- * line).
- */
-void nsBlockFrame::PushKids(nsBlockReflowState& aState)
-{
- nsIFrame* prevFrame = aState.prevLineLastFrame;
- NS_PRECONDITION(nsnull != prevFrame, "pushing all kids");
#ifdef NS_DEBUG
- nsIFrame* nextSibling;
-
- prevFrame->GetNextSibling(nextSibling);
- NS_PRECONDITION(nextSibling == aState.lineStart, "bad prev line");
+ nsIFrame* nextFrame;
+ prevKidFrame->GetNextSibling(nextFrame);
+ NS_ASSERTION(nextFrame == aLine->mFirstChild, "bad line list");
#endif
+ prevKidFrame->SetNextSibling(nsnull);
+ prevLine->mNextLine = nsnull;
+ mLastContentOffset = prevLine->mLastContentOffset;
+ mLastContentIsComplete = prevLine->mLastContentIsComplete;
+
+ // Push children to our next-in-flow if we have, or to our overflow list
+ nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
+ PRInt32 pushCount = 0;
+ if (nsnull == nextInFlow) {
+ // Place children on the overflow list
+ mOverflowList = aLine->mFirstChild;
+
+ // Destroy the lines
+ nsLineData* line = aLine;
+ while (nsnull != line) {
+ pushCount += line->mChildCount;
+ nsLineData* next = line->mNextLine;
+ delete line;
+ line = next;
+ }
+ }
+ else {
+ aLine->mPrevLine = nsnull;
+
+ // Pass on the children to our next-in-flow
+ nsLineData* line = aLine;
+ prevLine = line;
+ nsIFrame* lastKid = aLine->mFirstChild;
+ nsIFrame* kid = lastKid;
+ while (nsnull != line) {
+ i = line->mChildCount;
+ pushCount += i;
+ NS_ASSERTION(kid == line->mFirstChild, "bad line list");
+ while (--i >= 0) {
+ kid->SetGeometricParent(nextInFlow);
+ nsIFrame* contentParent;
+ kid->GetContentParent(contentParent);
+ if (this == contentParent) {
+ kid->SetContentParent(nextInFlow);
+ }
+ lastKid = kid;
+ kid->GetNextSibling(kid);
+ }
+ prevLine = line;
+ line = line->mNextLine;
+ }
+
+ // Join the two line lists
+ nsLineData* nextInFlowLine = nextInFlow->mLines;
+ if (nsnull != nextInFlowLine) {
+ lastKid->SetNextSibling(nextInFlowLine->mFirstChild);
+ nextInFlowLine->mPrevLine = prevLine;
+ }
+ nsIFrame* firstKid = aLine->mFirstChild;
+ prevLine->mNextLine = nextInFlowLine;
+ nextInFlow->mLines = aLine;
+ nextInFlow->mFirstChild = firstKid;
+ nextInFlow->mChildCount += pushCount;
+ firstKid->GetIndexInParent(nextInFlow->mFirstContentOffset);
#ifdef NS_DEBUG
- PRInt32 numKids = LengthOf(mFirstChild);
- NS_ASSERTION(numKids == mChildCount, "bad child count");
+ nextInFlow->VerifyLines(PR_FALSE);
+#endif
+ }
+ mChildCount -= pushCount;
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return NS_LINE_LAYOUT_NOT_COMPLETE;
+}
+
+nsresult
+nsBlockFrame::ReflowMapped(nsBlockReflowState& aState)
+{
+ nsresult rv = NS_OK;
+
+ // Get some space to start reflowing with
+ GetAvailableSpace(aState, aState.mY);
+
+ nsLineData* prevLine = nsnull;
+ nsLineData* line = mLines;
+ nsLineLayout lineLayout(aState);
+ aState.mCurrentLine = &lineLayout;
+ while (nsnull != line) {
+ // Initialize the line layout for this line
+ rv = lineLayout.Initialize(aState, line);
+ if (NS_OK != rv) {
+ goto done;
+ }
+ lineLayout.mPrevKidFrame = aState.mPrevKidFrame;
+
+ // Reflow the line
+ nsresult lineReflowStatus = lineLayout.ReflowLine();
+ if (lineReflowStatus < 0) {
+ // Some kind of hard error
+ rv = lineReflowStatus;
+ goto done;
+ }
+ mChildCount += lineLayout.mNewFrames;
+
+ // Now place it. It's possible it won't fit.
+ rv = PlaceLine(aState, lineLayout, line);
+ if (NS_LINE_LAYOUT_COMPLETE != rv) {
+ goto done;
+ }
+
+ mLastContentOffset = line->mLastContentOffset;
+ mLastContentIsComplete = PRBool(line->mLastContentIsComplete);
+ prevLine = line;
+ line = line->mNextLine;
+ aState.mPrevKidFrame = lineLayout.mPrevKidFrame;
+ }
+
+done:
+ aState.mCurrentLine = nsnull;
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return rv;
+}
+
+nsresult
+nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState)
+{
+ nsresult rv = NS_OK;
+
+ // If we have no children and we have a prev-in-flow then we need to
+ // pick up where it left off. If we have children, e.g. we're being
+ // resized, then our content offset will have already been set
+ // correctly.
+ nsIFrame* kidPrevInFlow = nsnull;
+ if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
+ nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow;
+ mFirstContentOffset = prev->NextChildOffset();// XXX Is this necessary?
+ if (PR_FALSE == prev->mLastContentIsComplete) {
+ // Our prev-in-flow's last child is not complete
+ prev->LastChild(kidPrevInFlow);
+ }
+ }
+
+ // Get to the last line where the new content may be added
+ nsLineData* line = nsnull;
+ nsLineData* prevLine = nsnull;
+ if (nsnull != mLines) {
+ line = mLines;
+ while (nsnull != line->mNextLine) {
+ line = line->mNextLine;
+ }
+ prevLine = line;
+
+ // If the last line is not complete then kidPrevInFlow should be
+ // set to the last-line's last child.
+ if (!prevLine->mLastContentIsComplete) {
+ kidPrevInFlow = prevLine->GetLastChild();
+ }
+ }
+
+ // Get some space to start reflowing with
+ GetAvailableSpace(aState, aState.mY);
+
+ // Now reflow the new content until we are out of new content or out
+ // of vertical space.
+ PRInt32 kidIndex = NextChildOffset();
+ nsLineLayout lineLayout(aState);
+ aState.mCurrentLine = &lineLayout;
+ lineLayout.mKidPrevInFlow = kidPrevInFlow;
+ PRInt32 contentChildCount = mContent->ChildCount();
+ while (kidIndex < contentChildCount) {
+ if (nsnull == line) {
+ if (!MoreToReflow(aState)) {
+ break;
+ }
+ line = new nsLineData();
+ if (nsnull == line) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+ line->mFirstContentOffset = kidIndex;
+ }
+
+ // Initialize the line layout for this line
+ rv = lineLayout.Initialize(aState, line);
+ if (NS_OK != rv) {
+ goto done;
+ }
+ lineLayout.mKidPrevInFlow = kidPrevInFlow;
+ lineLayout.mPrevKidFrame = aState.mPrevKidFrame;
+
+ // Reflow the line
+ nsresult lineReflowStatus = lineLayout.ReflowLine();
+ if (lineReflowStatus < 0) {
+ // Some kind of hard error
+ rv = lineReflowStatus;
+ goto done;
+ }
+ mChildCount += lineLayout.mNewFrames;
+
+ // Add line to the block; do this before placing the line in case
+ // PushLines is needed.
+ if (nsnull == prevLine) {
+ // For the first line, initialize mFirstContentOffset
+ mFirstContentOffset = line->mFirstContentOffset;
+ mFirstChild = line->mFirstChild;
+ mLines = line;
+ }
+ else {
+ prevLine->mNextLine = line;
+ line->mPrevLine = prevLine;
+ }
+
+ // Now place it. It's possible it won't fit.
+ rv = PlaceLine(aState, lineLayout, line);
+ if (NS_LINE_LAYOUT_COMPLETE != rv) {
+ goto done;
+ }
+ kidIndex = lineLayout.mKidIndex;
+ kidPrevInFlow = lineLayout.mKidPrevInFlow;
+
+ mLastContentOffset = line->mLastContentOffset;
+ mLastContentIsComplete = PRBool(line->mLastContentIsComplete);
+ prevLine = line;
+ line = line->mNextLine;
+ aState.mPrevKidFrame = lineLayout.mPrevKidFrame;
+ }
+
+done:
+ aState.mCurrentLine = nsnull;
+ if (aState.mBlockIsPseudo) {
+ PropagateContentOffsets();
+ }
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return rv;
+}
+
+nsresult
+nsBlockFrame::InitializeState(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockReflowState& aState)
+{
+ nsresult rv;
+ rv = aState.Initialize(aPresContext, aSpaceManager,
+ aMaxSize, aMaxElementSize, this);
+
+ nsStyleSpacing* mySpacing = (nsStyleSpacing*)
+ mStyleContext->GetData(kStyleSpacingSID);
+
+ // Apply border and padding adjustments for regular frames only
+ if (!aState.mBlockIsPseudo) {
+ aState.mY = mySpacing->mBorderPadding.top;
+ aState.mX = mySpacing->mBorderPadding.left;
+ aState.mAvailSize.width -=
+ (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right);
+ aState.mAvailSize.height -=
+ (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom);
+ aState.mBorderPadding = mySpacing->mBorderPadding;
+ }
+ else {
+ aState.mBorderPadding.SizeTo(0, 0, 0, 0);
+ }
+
+ // Setup initial list ordinal value
+ nsIAtom* tag = mContent->GetTag();
+ if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
+ (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
+ nsHTMLValue value;
+ if (eContentAttr_HasValue ==
+ ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) {
+ if (eHTMLUnit_Integer == value.GetUnit()) {
+ aState.mNextListOrdinal = value.GetIntValue();
+ }
+ }
+ }
+ NS_RELEASE(tag);
+
+ return rv;
+}
+
+#if XXX
+NS_METHOD
+nsBlockFrame::SizeTo(nscoord aWidth, nscoord aHeight)
+{
+ printf("size to %g,%g\n", NS_TWIPS_TO_POINTS_FLOAT(aWidth),
+ NS_TWIPS_TO_POINTS_FLOAT(aHeight));
+ return nsHTMLContainerFrame::SizeTo(aWidth, aHeight);
+}
+
+NS_METHOD
+nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ aStatus = frComplete;
+ nsBlockReflowState state;
+ rv = InitializeState(aPresContext, aMaxSize, aMaxElementSize, state);
+ if (NS_OK == rv) {
+ nsRect desiredRect;
+ rv = DoResizeReflow(state, desiredRect, aStatus);
+ aDesiredSize.width = desiredRect.width;
+ aDesiredSize.height = desiredRect.height;
+ aDesiredSize.ascent = aDesiredSize.height;
+ aDesiredSize.descent = 0;
+ }
+ return rv;
+}
#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": push kids (childCount=%d)\n", mChildCount);
- DumpFlow();
-#endif
-
- PushChildren(aState.lineStart, prevFrame, mLastContentIsComplete);
- SetLastContentOffset(prevFrame);
-
- // Set mLastContentIsComplete to the previous lines last content is
- // complete now that the previous line's last child is our last
- // child.
- mLastContentIsComplete = aState.prevLineLastContentIsComplete;
-
- // Fix up child count
- // XXX is there a better way? aState.lineLength doesn't work because
- // we might be pushing more than just the pending line.
- nsIFrame* kid = mFirstChild;
- PRInt32 kids = 0;
- while (nsnull != kid) {
- kids++;
- kid->GetNextSibling(kid);
- }
- mChildCount = kids;
-
- // Make sure we have no lingering line data
- aState.lineLength = 0;
- aState.lineStart = nsnull;
-
-#ifdef NOISY
- ListTag(stdout);
- printf(": push kids done (childCount=%d) [%c]\n", mChildCount,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-}
-
-/**
- * Gets a band of available space starting at the specified y-offset. Assumes
- * the local coordinate space is currently set to the upper-left origin of the
- * bounding rect
- *
- * Updates "currentBand" and "x" member data of the block reflow state
- */
-void nsBlockFrame::GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY)
-{
- // Gets a band of available space.
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
- aState.spaceManager->GetBandData(aY, aState.availSize, *aState.currentBand);
-
- // Compute the bounding rect of the available space, i.e. space between any
- // left and right floaters
- aState.currentBand->ComputeAvailSpaceRect();
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
- aState.x = aState.currentBand->availSpace.x;
-}
-
-void nsBlockFrame::ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear)
-{
- // Translate the coordinate space
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
-
-getBand:
- nscoord y = aState.y + aState.topMargin;
- PRBool isLeftFloater = PR_FALSE;
- PRBool isRightFloater = PR_FALSE;
-
- // Get a band of available space
- aState.spaceManager->GetBandData(y, aState.availSize, *aState.currentBand);
-
- // Scan the trapezoids looking for left and right floaters
- nsBandTrapezoid* trapezoid = aState.currentBand->trapezoids;
- for (PRInt32 i = 0; i < aState.currentBand->count; i++) {
- // XXX Handle multiply occupied
- if (nsBandTrapezoid::smOccupied == trapezoid->state) {
- nsStyleDisplay* display;
-
- trapezoid->frame->GetStyleData(kStylePositionSID, (nsStyleStruct*&)display);
- if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
- isLeftFloater = PR_TRUE;
- } else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) {
- isRightFloater = PR_TRUE;
- }
- }
-
- trapezoid++;
- }
-
- if (isLeftFloater) {
- if ((aClear == NS_STYLE_CLEAR_LEFT) ||
- (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
- aState.y += aState.currentBand->trapezoids[0].GetHeight();
- goto getBand;
- }
- }
- if (isRightFloater) {
- if ((aClear == NS_STYLE_CLEAR_RIGHT) ||
- (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
- aState.y += aState.currentBand->trapezoids[0].GetHeight();
- goto getBand;
- }
- }
-
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
-}
-
-// Bit's for PlaceAndReflowChild return value
-#define PLACE_FIT 0x1
-#define PLACE_FLOWED 0x2
-
-PRIntn
-nsBlockFrame::PlaceAndReflowChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC)
-{
- nsSize kidMaxElementSize;
- nsSize* pKidMaxElementSize =
- (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull;
-
- // Get line start setup if we are at the start of a new line
- if (nsnull == aState.lineStart) {
- NS_ASSERTION(0 == aState.lineLength, "bad line length");
- aState.lineStart = aKidFrame;
- }
-
- // Get kid and its style
- nsStyleDisplay* styleDisplay = (nsStyleDisplay*)
- aKidSC->GetData(kStyleDisplaySID);
-
- // Figure out if kid is a block element or not
- PRBool isInline = PR_TRUE;
- PRIntn display = styleDisplay->mDisplay;
- if (aState.firstChildIsInsideBullet && (mFirstChild == aKidFrame)) {
- // XXX Special hack for properly reflowing bullets that have the
- // inside value for list-style-position.
- display = NS_STYLE_DISPLAY_INLINE;
- }
- if ((NS_STYLE_DISPLAY_BLOCK == display) ||
- (NS_STYLE_DISPLAY_LIST_ITEM == display)) {
- // Block elements always end up on the next line (unless they are
- // already at the start of the line).
- isInline = PR_FALSE;
- if (aState.lineLength > 0) {
- aState.breakAfterChild = PR_TRUE;
- }
- }
-
- // Handle forced break first
- if (aState.breakAfterChild) {
- NS_ASSERTION(aState.lineStart != aKidFrame, "bad line");
-
- // Get the last child in the current line
- nsIFrame* lastFrame = aState.lineStart;
- PRInt32 lineLen = aState.lineLength - 1;
- while (--lineLen >= 0) {
- lastFrame->GetNextSibling(lastFrame);
- }
-
- if (!AdvanceToNextLine(aCX, aState)) {
- // The previous line didn't fit.
- return 0;
- }
- aState.lineStart = aKidFrame;
-
- // Get the style for the last child, and see if it wanted to clear
- // floaters. This handles the BR tag, which is the only inline
- // element for which clear applies
- nsIStyleContextPtr lastChildSC;
-
- lastFrame->GetStyleContext(aCX, lastChildSC.AssignRef());
- nsStyleDisplay* lastChildDisplay = (nsStyleDisplay*)
- lastChildSC->GetData(kStyleDisplaySID);
- switch (lastChildDisplay->mBreakType) {
- case NS_STYLE_CLEAR_LEFT:
- case NS_STYLE_CLEAR_RIGHT:
- case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
- ClearFloaters(aState, lastChildDisplay->mBreakType);
- break;
- }
- }
-
- // Now that we've handled force breaks (and maybe called AdvanceToNextLine()
- // which checks), remember whether it's an inline frame
- aState.isInline = isInline;
-
- // If we're at the beginning of a line then compute the top margin that we
- // should use
- if (aState.lineStart == aKidFrame) {
- // Compute the top margin to use for this line
- aState.topMargin = GetTopMarginFor(aCX, aState, aKidFrame, aKidSC,
- aState.isInline);
-
- // If it's an inline element then get a band of available space
- //
- // XXX If we have a current band and there's unused space in that band
- // then avoid this call to get a band...
- if (aState.isInline) {
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
- }
- }
-
- // Compute the available space to reflow the child into and then
- // reflow it into that space.
- nsSize kidAvailSize;
- GetAvailSize(kidAvailSize, aState, aKidSC, aState.isInline);
- if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) {
- // No more room
- return 0;
- }
-
- ReflowStatus status;
-
- if (aState.isInline) {
- nsReflowMetrics kidSize;
-
- // Inline elements are never passed the space manager
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
-
- // For first children, we skip all the fit checks because we must
- // fit at least one child for a parent to figure what to do with us.
- if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) {
- NS_ASSERTION(nsnull != aState.lineStart, "bad line start");
-
- if (aKidFrame == aState.lineStart) {
- // Width always fits when we are at the logical left margin.
- // Just check the height.
- //
- // XXX This height check isn't correct now that we have bands of
- // available space...
- if (kidSize.height > kidAvailSize.height) {
- // It's too tall
- return PLACE_FLOWED;
- }
- } else {
- // Examine state and if the breakBeforeChild is set and we
- // aren't already on the new line, do the forcing now.
- // XXX Why aren't we doing this check BEFORE we resize reflow the child?
- if (aState.breakBeforeChild) {
- aState.breakBeforeChild = PR_FALSE;
- if (aKidFrame != aState.lineStart) {
- if (!AdvanceToNextLine(aCX, aState)) {
- // Flushing out the line failed.
- return PLACE_FLOWED;
- }
- aState.lineStart = aKidFrame;
-
- // Get a band of available space
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
-
- // Reflow child now that it has the line to itself
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE);
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
- }
- }
-
- // When we are not at the logical left margin then we need
- // to check the width first. If we are too wide then advance
- // to the next line and try reflowing again.
- if (kidSize.width > kidAvailSize.width) {
- // Too wide. Try next line
- if (!AdvanceToNextLine(aCX, aState)) {
- // Flushing out the line failed.
- return PLACE_FLOWED;
- }
- aState.lineStart = aKidFrame;
-
- // Get a band of available space
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
-
- // Reflow splittable children
- SplittableType isSplittable;
-
- aKidFrame->IsSplittable(isSplittable);
- if (isSplittable != frNotSplittable) {
- // Update size info now that we are on the next line. Then
- // reflow the child into the new available space.
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE);
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
-
- // If we just reflowed our last child then update the
- // mLastContentIsComplete state.
- nsIFrame* nextSibling;
-
- aKidFrame->GetNextSibling(nextSibling);
- if (nsnull == nextSibling) {
- // Use state from the reflow we just did
- mLastContentIsComplete = PRBool(status == frComplete);
- }
- }
-
- // XXX This height check isn't correct now that we have bands of
- // available space...
- if (kidSize.height > kidAvailSize.height) {
- // It's too tall on the next line
- return PLACE_FLOWED;
- }
- // It's ok if it's too wide on the next line.
- }
- }
- }
-
- // Add child to the line
- AddInlineChildToLine(aCX, aState, aKidFrame, kidSize,
- pKidMaxElementSize, aKidSC);
- } else {
- nsRect kidRect;
-
- // Does the block-level element want to clear any floaters that impact
- // it? Note that the clear property only applies to block-level elements
- // and the BR tag
- nsStyleDisplay* styleDisplay = (nsStyleDisplay*)
- aKidSC->GetData(kStyleDisplaySID);
- switch (styleDisplay->mBreakType) {
- case NS_STYLE_CLEAR_LEFT:
- case NS_STYLE_CLEAR_RIGHT:
- case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
- ClearFloaters(aState, styleDisplay->mBreakType);
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_FALSE);
- if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) {
- // No more room
- return 0;
- }
- break;
- }
-
- // Give the block its own local coordinate space.. Note: ReflowChild()
- // will adjust for the child's left/right margin after determining the
- // current left/right edge
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
- // Give the block-level element the opportunity to use the space manager
- status = ReflowChild(aKidFrame, aCX, aState.spaceManager,
- kidAvailSize, kidRect, pKidMaxElementSize);
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
-
- // For first children, we skip all the fit checks because we must
- // fit at least one child for a parent to figure what to do with us.
- if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) {
- // Block elements always fit horizontally (because they are
- // always placed at the logical left margin). Check to see if
- // the block fits vertically
- if (kidRect.YMost() > kidAvailSize.height) {
- // Nope
- return PLACE_FLOWED;
- }
- }
-
- // Add block child
- // XXX We need to set lastContentIsComplete here, because AddBlockChild()
- // calls AdvaneceToNextLine(). We need to restructure the flow of control,
- // and use a state machine...
- aState.lastContentIsComplete = PRBool(status == frComplete);
- AddBlockChild(aCX, aState, aKidFrame, kidRect, pKidMaxElementSize, aKidSC);
- }
-
- // If we just reflowed our last child then update the
- // mLastContentIsComplete state.
- nsIFrame* nextSibling;
-
- aKidFrame->GetNextSibling(nextSibling);
- if (nsnull == nextSibling) {
- // Use state from the reflow we just did
- mLastContentIsComplete = PRBool(status == frComplete);
- }
-
- aState.lastContentIsComplete = PRBool(status == frComplete);
- if (aState.isInline && (frNotComplete == status)) {
- // Since the inline child didn't complete its reflow we *know*
- // that a continuation of it can't possibly fit on the current
- // line. Therefore, set a flag in the state that will cause the
- // a line break before the next frame is placed.
- aState.breakAfterChild = PR_TRUE;
- }
-
- aState.reflowStatus = status;
- return PLACE_FLOWED | PLACE_FIT;
-}
-
-/**
- * Reflow the existing frames.
- *
- * @param aCX presentation context to use
- * @param aState in out parameter which tracks the state of
- * reflow for the block frame.
- * @return true if we successfully reflowed all the mapped children and false
- * otherwise, e.g. we pushed children to the next in flow
- */
PRBool
-nsBlockFrame::ReflowMappedChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n",
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-
- PRBool result = PR_TRUE;
- nsIFrame* kidFrame;
-
- for (kidFrame = mFirstChild; nsnull != kidFrame; ) {
-
- /* we get the kid's style from kidFrame's cached style context */
- nsIStyleContextPtr kidSC;
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
-
- // Attempt to place and reflow the child
-
- // XXX if child is not splittable and it fits just place it where
- // it is, otherwise advance to the next line and place it there if
- // possible
-
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- ReflowStatus status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // The child doesn't fit. Push it and any remaining children.
- PushKids(aState);
- result = PR_FALSE;
- goto push_done;
- }
-
- // Is the child complete?
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- if (frComplete == status) {
- // Yes, the child is complete
- NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
- } else {
- // No the child isn't complete
- if (nsnull == kidNextInFlow) {
- // The child doesn't have a next-in-flow so create a continuing
- // frame. This hooks the child into the flow
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
- NS_ASSERTION(nsnull != continuingFrame, "frame creation failed");
-
- // Add the continuing frame to the sibling list
- nsIFrame* nextSib;
-
- kidFrame->GetNextSibling(nextSib);
- continuingFrame->SetNextSibling(nextSib);
- kidFrame->SetNextSibling(continuingFrame);
- mChildCount++;
- }
-
- // Unlike the inline frame code we can't assume that we used
- // up all of our space because the child's reflow status is
- // frNotComplete. Instead, the child is probably split and
- // we need to reflow the continuations as well.
- }
-
- // Get the next child frame
- kidFrame->GetNextSibling(kidFrame);
- }
-
- push_done:
-#ifdef NS_DEBUG
- nsIFrame* lastChild;
- PRInt32 lastIndexInParent;
-
- LastChild(lastChild);
- lastChild->GetIndexInParent(lastIndexInParent);
- NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset");
-
- PRInt32 len = LengthOf(mFirstChild);
- NS_POSTCONDITION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
-
-#ifdef NOISY
- ListTag(stdout);
- printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n",
- (result ? "" : "NOT "),
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
- return result;
-}
-
-/* XXX:
- * In this method, we seem to be getting the style for the next child,
- * and checking that child's style for the child's display type.
- * The problem is that it's very inefficient to ResolveStyleFor(kid) here
- * and then just throw it away, when we end up doing a ResolveStyleFor(kid)
- * again when we actually create a frame for the content.
- * It would be nice to cache the resolved style, maybe in the reflow state?
- */
-PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX)
+nsBlockFrame::MoreToReflow(nsBlockReflowState& aState)
{
PRBool rv = PR_FALSE;
- if (IsPseudoFrame()) {
+ if (aState.mBlockIsPseudo) {
// Get the next content object that we would like to reflow
PRInt32 kidIndex = NextChildOffset();
nsIContentPtr kid = mContent->ChildAt(kidIndex);
if (kid.IsNotNull()) {
// Resolve style for the kid
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
+ nsIStyleContextPtr kidSC =
+ aState.mPresContext->ResolveStyleContextFor(kid, this);
nsStyleDisplay* kidStyleDisplay = (nsStyleDisplay*)
kidSC->GetData(kStyleDisplaySID);
switch (kidStyleDisplay->mDisplay) {
@@ -1305,640 +993,288 @@ PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX)
return rv;
}
-/**
- * Create new frames for content we haven't yet mapped
- *
- * @param aCX presentation context to use
- * @return frComplete if all content has been mapped and frNotComplete
- * if we should be continued
- */
-nsIFrame::ReflowStatus
-nsBlockFrame::ReflowAppendedChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
+nsBlockReflowState*
+nsBlockFrame::FindBlockReflowState(nsIPresContext* aPresContext,
+ nsIFrame* aFrame)
{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
- nsIFrame* kidPrevInFlow = nsnull;
- ReflowStatus result = frNotComplete;
-
- // If we have no children and we have a prev-in-flow then we need to pick
- // up where it left off. If we have children, e.g. we're being resized, then
- // our content offset should already be set correctly...
- if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
- nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow;
- NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow");
- mFirstContentOffset = prev->NextChildOffset();
- if (PR_FALSE == prev->mLastContentIsComplete) {
- // Our prev-in-flow's last child is not complete
- prev->LastChild(kidPrevInFlow);
- }
- }
-
- // Place our children, one at a time until we are out of children
- PRInt32 kidIndex = NextChildOffset();
- nsIFrame* kidFrame = nsnull;
- nsIFrame* prevKidFrame;
-
- LastChild(prevKidFrame);
- for (;;) {
- // Get the next content object
- nsIContentPtr kid = mContent->ChildAt(kidIndex);
- if (kid.IsNull()) {
- result = frComplete;
- break;
- }
-
- // Resolve style for the kid
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
- nsStylePosition* kidPosition = (nsStylePosition*)
- kidSC->GetData(kStylePositionSID);
- nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
- kidSC->GetData(kStyleDisplaySID);
-
- // Check whether it wants to floated or absolutely positioned
- if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) {
- AbsoluteFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- kidFrame->SetStyleContext(aCX,kidSC);
- } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
- PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- kidFrame->SetStyleContext(aCX,kidSC);
- } else if (nsnull == kidPrevInFlow) {
- // Create initial frame for the child
- nsIContentDelegate* kidDel;
- switch (kidDisplay->mDisplay) {
- case NS_STYLE_DISPLAY_BLOCK:
- case NS_STYLE_DISPLAY_LIST_ITEM:
- // Pseudo block frames do not contain other block elements
- // unless the block element would be the first child.
- if (IsPseudoFrame()) {
- // If we're being used as a pseudo frame, i.e. we map the same
- // content as our parent then we want to indicate we're complete;
- // otherwise we'll be continued and go on mapping children...
-
- // It better be true that we are not being asked to flow a
- // block element as our first child. That means the body
- // decided it needed a pseudo-frame when it shouldn't have.
- NS_ASSERTION(nsnull != mFirstChild, "bad body");
-
- result = frComplete;
- goto done;
- }
- // FALL THROUGH (and create frame)
-
- case NS_STYLE_DISPLAY_INLINE:
- kidDel = kid->GetDelegate(aCX);
- kidFrame = kidDel->CreateFrame(aCX, kid, kidIndex, this);
- NS_RELEASE(kidDel);
- break;
-
- default:
- NS_ASSERTION(nsnull == kidPrevInFlow, "bad prev in flow");
- nsFrame::NewFrame(&kidFrame, kid, kidIndex, this);
+ nsBlockReflowState* state = nsnull;
+ if (nsnull != aFrame) {
+ nsIFrame* parent;
+ aFrame->GetGeometricParent(parent);
+ while (nsnull != parent) {
+ nsBlockFrame* block;
+ nsresult rv = parent->QueryInterface(kBlockFrameCID, (void**) &block);
+ if (NS_OK == rv) {
+ nsIPresShell* shell = aPresContext->GetShell();
+ state = (nsBlockReflowState*) shell->GetCachedData(block);
+ NS_RELEASE(shell);
break;
}
- kidFrame->SetStyleContext(aCX,kidSC);
- } else {
- // Since kid has a prev-in-flow, use that to create the next
- // frame.
- kidPrevInFlow->CreateContinuingFrame(aCX, this, kidFrame);
+ parent->GetGeometricParent(parent);
}
-
- // Link child frame into the list of children. If the frame ends
- // up not fitting and getting pushed, the PushKids code will fixup
- // the child count for us.
- if (nsnull != prevKidFrame) {
-#ifdef NS_DEBUG
- nsIFrame* nextSibling;
-
- prevKidFrame->GetNextSibling(nextSibling);
- NS_ASSERTION(nsnull == nextSibling, "bad append");
-#endif
- prevKidFrame->SetNextSibling(kidFrame);
- } else {
- NS_ASSERTION(nsnull == mFirstChild, "bad create");
- mFirstChild = kidFrame;
- SetFirstContentOffset(kidFrame);
- }
- prevKidFrame = kidFrame;
- mChildCount++;
-
- // Reflow child frame as many times as necessary until it is
- // complete.
- ReflowStatus status;
- do {
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // We ran out of room.
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- mLastContentIsComplete = PRBool(nsnull == kidNextInFlow);
- PushKids(aState);
-
- goto push_done;
- }
-
- // Did the child complete?
- prevKidFrame = kidFrame;
- if (frNotComplete == status) {
- // Child didn't complete so create a continuing frame
- kidPrevInFlow = kidFrame;
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
-
- // Add the continuing frame to the sibling list
- nsIFrame* kidNextSibling;
-
- kidFrame->GetNextSibling(kidNextSibling);
- continuingFrame->SetNextSibling(kidNextSibling);
- kidFrame->SetNextSibling(continuingFrame);
- kidFrame = continuingFrame;
- mChildCount++;
-
- // Switch to new kid style
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
- }
-#ifdef NS_DEBUG
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- NS_ASSERTION(nsnull == kidNextInFlow, "huh?");
-#endif
- } while (frNotComplete == status);
-
- // The child that we just reflowed is complete
-#ifdef NS_DEBUG
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
-#endif
- kidIndex++;
- kidPrevInFlow = nsnull;
}
-
- done:
- // To get here we either completely reflowed all our appended
- // children OR we are a pseudo-frame and we ran into a block
- // element. In either case our last content MUST be complete.
- NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state");
- NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
- SetLastContentOffset(prevKidFrame);
-
- push_done:
-#ifdef NS_DEBUG
- PRInt32 len = LengthOf(mFirstChild);
- NS_ASSERTION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
- return result;
+ return state;
}
-/**
- * Pullup frames from our next in flow and try to place them. Before
- * this is called our previously mapped children, if any have been
- * reflowed which means that the block reflow state's x and y
- * coordinates and other data are ready to go.
- *
- * Return true if we pulled everything up.
- */
-PRBool
-nsBlockFrame::PullUpChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": pullup (childCount=%d) [%d,%d,%c]\n",
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-
- PRBool result = PR_TRUE;
- nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
- nsIFrame* prevKidFrame;
-
- LastChild(prevKidFrame);
- while (nsnull != nextInFlow) {
- // Get first available frame from the next-in-flow
- nsIFrame* kidFrame = PullUpOneChild(nextInFlow, prevKidFrame);
- if (nsnull == kidFrame) {
- // We've pulled up all the children from that next-in-flow, so
- // move to the next next-in-flow.
- nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
- continue;
- }
-
- // Get style information for the pulled up kid
- nsIContentPtr kid;
-
- kidFrame->GetContent(kid.AssignRef());
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
-
- ReflowStatus status;
- do {
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // Push the kids that didn't fit back down to the next-in-flow
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- mLastContentIsComplete = PRBool(nsnull == kidNextInFlow);
- PushKids(aState);
-
- result = PR_FALSE;
- goto push_done;
- }
-
- if (frNotComplete == status) {
- // Child is not complete
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- if (nsnull == kidNextInFlow) {
- // Create a continuing frame for the incomplete child
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
-
- // Add the continuing frame to our sibling list.
- nsIFrame* nextSibling;
-
- kidFrame->GetNextSibling(nextSibling);
- continuingFrame->SetNextSibling(nextSibling);
- kidFrame->SetNextSibling(continuingFrame);
- prevKidFrame = kidFrame;
- kidFrame = continuingFrame;
- mChildCount++;
-
- // Switch to new kid style
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
- } else {
- // The child has a next-in-flow, but it's not one of ours.
- // It *must* be in one of our next-in-flows. Collect it
- // then.
- NS_ASSERTION(!IsChild(kidNextInFlow), "busted kid next-in-flow");
- break;
- }
- }
- } while (frNotComplete == status);
-
- prevKidFrame = kidFrame;
- }
-
- if (nsnull != prevKidFrame) {
- // The only way we can get here is by pulling up every last child
- // in our next-in-flows (and reflowing any continunations they
- // have). Therefore we KNOW that our last child is complete.
- NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state");
- NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
- SetLastContentOffset(prevKidFrame);
- }
-
- push_done:;
-
- if (result == PR_FALSE) {
- // If our next-in-flow is empty OR our next next-in-flow is empty
- // then adjust the offsets of all of the empty next-in-flows.
- nextInFlow = (nsBlockFrame*) mNextInFlow;
- if ((0 == nextInFlow->mChildCount) ||
- ((nsnull != nextInFlow->mNextInFlow) &&
- (0 == ((nsBlockFrame*)(nextInFlow->mNextInFlow))->mChildCount))) {
- // We didn't pullup everything and we need to fixup one of our
- // next-in-flows content offsets.
- AdjustOffsetOfEmptyNextInFlows();
- }
- }
-
-
-#ifdef NS_DEBUG
- PRInt32 len = LengthOf(mFirstChild);
- NS_ASSERTION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n",
- (result ? "" : "NOT "),
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
- return result;
-}
-
-void nsBlockFrame::SetupState(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- const nsSize& aMaxSize,
- nsSize* aMaxElementSize,
- nsISpaceManager* aSpaceManager)
-{
- // Setup reflow state
- aState.Init(aMaxSize, aMaxElementSize, mStyleContext, aSpaceManager);
-
- nsStyleSpacing* mySpacing = (nsStyleSpacing*)
- mStyleContext->GetData(kStyleSpacingSID);
-
- // Apply border and padding adjustments for regular frames only
- if (PR_FALSE == IsPseudoFrame()) {
- aState.borderPadding = mySpacing->mBorderPadding;
- aState.y = mySpacing->mBorderPadding.top;
- aState.availSize.width -=
- (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right);
- aState.availSize.height -=
- (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom);
- } else {
- aState.borderPadding.SizeTo(0, 0, 0, 0);
- }
-
- // Setup initial list ordinal value
- nsIAtom* tag = mContent->GetTag();
- if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
- (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
- nsHTMLValue value;
- if (eContentAttr_HasValue ==
- ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) {
- if (eHTMLUnit_Integer == value.GetUnit()) {
- aState.nextListOrdinal = value.GetIntValue();
- }
- }
- }
- NS_RELEASE(tag);
-
- mCurrentState = &aState;
-}
-
-#include "nsUnitConversion.h"/* XXX */
-NS_METHOD nsBlockFrame::ResizeReflow(nsIPresContext* aCX,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsSize* aMaxElementSize,
- ReflowStatus& aStatus)
-{
- nsBlockReflowState state;
- SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager);
- return DoResizeReflow(aCX, state, aDesiredRect, aStatus);
-}
-
-nsresult nsBlockFrame::DoResizeReflow(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsRect& aDesiredRect,
- ReflowStatus& aStatus)
+nsresult
+nsBlockFrame::DoResizeReflow(nsBlockReflowState& aState,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ ReflowStatus& aStatus)
{
#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
PreReflowCheck();
#endif
-#ifdef NOISY
+#ifdef NOISY_REFLOW
ListTag(stdout);
- printf(": resize reflow %g,%g\n",
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width),
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height));
- DumpFlow();
+ printf(": Before:\n");
+ List(stdout, 1);
#endif
- // Zap old line data
- if (nsnull != mLines) {
- delete mLines;
- mLines = nsnull;
- }
- mNumLines = 0;
+ nsresult rv = NS_OK;
- // Check for an overflow list
- MoveOverflowToChildList();
-
- // Before we start reflowing, cache a pointer to our state structure
- // so that inline frames can find it.
- nsIPresShell* shell = aCX->GetShell();
+ nsIPresShell* shell = aState.mPresContext->GetShell();
shell->PutCachedData(this, &aState);
- // First reflow any existing frames
- PRBool reflowMappedOK = PR_TRUE;
- aStatus = frComplete;
- if (nsnull != mFirstChild) {
- reflowMappedOK = ReflowMappedChildren(aCX, aState);
- if (!reflowMappedOK) {
- aStatus = frNotComplete;
- }
+ // Check for an overflow list
+ DrainOverflowList();
+
+ if (nsnull != mLines) {
+ rv = ReflowMapped(aState);
}
- if (reflowMappedOK) {
- // Any space left?
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- if ((nsnull != mFirstChild) && (aState.y >= yb)) {
- // No space left. Don't try to pull-up children or reflow
- // unmapped. We need to return the correct completion status,
- // so see if there is more to reflow.
- if (MoreToReflow(aCX)) {
- aStatus = frNotComplete;
- }
- } else if (MoreToReflow(aCX)) {
- // Try and pull-up some children from a next-in-flow
- if ((nsnull == mNextInFlow) || PullUpChildren(aCX, aState)) {
- // If we still have unmapped children then create some new frames
- if (MoreToReflow(aCX)) {
- aStatus = ReflowAppendedChildren(aCX, aState);
- }
- } else {
- // We were unable to pull-up all the existing frames from the
- // next in flow
- aStatus = frNotComplete;
- }
+ if (NS_OK == rv) {
+ if ((nsnull != mLines) && (aState.mAvailSize.height <= 0)) {
+ // We are out of space
}
- }
-
- // Deal with last line - make sure it gets vertically and
- // horizontally aligned. This also updates the state's y coordinate
- // which is good because that's how we size ourselves.
- if (0 != aState.lineLength) {
- if (!AdvanceToNextLine(aCX, aState)) {
- // The last line of output doesn't fit. Push all of the kids to
- // the next in flow and change our reflow status to not complete
- // so that we are continued.
-#ifdef NOISY
- ListTag(stdout);
- printf(": pushing kids since last line doesn't fit\n");
-#endif
-
- PushKids(aState);
- aStatus = frNotComplete;
+ if (MoreToReflow(aState)) {
+ rv = ReflowUnmapped(aState);
}
}
- if (frComplete == aStatus) {
- // Don't forget to add in the bottom margin from our last child.
- // Only add it in if there is room for it.
- nscoord margin = aState.prevMaxPosBottomMargin -
- aState.prevMaxNegBottomMargin;
- nscoord y = aState.y + margin;
- if (y <= aState.borderPadding.top + aState.availSize.height) {
- aState.y = y;
- }
- }
-
- // Now that reflow has finished, remove the cached pointer
- shell->RemoveCachedData(this);
- NS_RELEASE(shell);
-
- // Translate state.lineLengths into an integer array
- mNumLines = aState.lineLengths.Count();
- if (mNumLines > 0) {
- mLines = new PRInt32[mNumLines];
- for (PRInt32 i = 0; i < mNumLines; i++) {
- PRInt32 ll = (PRInt32) aState.lineLengths.ElementAt(i);
- mLines[i] = ll;
- }
- }
-
- if (!aState.unconstrainedWidth && aState.justifying) {
- // Perform justification now that we know how many lines we have.
- JustifyLines(aCX, aState);
- }
-
// Return our desired rect and our status
aDesiredRect.x = 0;
aDesiredRect.y = 0;
- aDesiredRect.width = aState.kidXMost + aState.borderPadding.right;
- if (!aState.unconstrainedWidth) {
+ aDesiredRect.width = aState.mKidXMost + aState.mBorderPadding.right;
+ if (!aState.mUnconstrainedWidth) {
// Make sure we're at least as wide as the max size we were given
- nscoord maxWidth = aState.availSize.width + aState.borderPadding.left +
- aState.borderPadding.right;
-
+ nscoord maxWidth = aState.mAvailSize.width + aState.mBorderPadding.left +
+ aState.mBorderPadding.right;
if (aDesiredRect.width < maxWidth) {
aDesiredRect.width = maxWidth;
}
}
- aState.y += aState.borderPadding.bottom;
- aDesiredRect.height = aState.y;
-
-#ifdef NS_DEBUG
- PostReflowCheck(aStatus);
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": resize reflow %g,%g %scomplete [%d,%d,%c]\n",
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width),
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height),
- ((status == frNotComplete) ? "NOT " : ""),
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F')
- );
- DumpFlow();
-#endif
- mCurrentState = nsnull;
- return NS_OK;
-}
-
-void nsBlockFrame::JustifyLines(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
- // XXX we don't justify the last line; what if we are continued,
- // should we do it then?
- nsIFrame* kid = mFirstChild;
- for (PRInt32 i = 0; i < mNumLines; i++) {
- nsIFrame* lineStart = kid;
- PRInt32 lineLength = mLines[i];
- if (i < mNumLines - 1) {
- if (1 == lineLength) {
- // For lines with one element on them we can take a shortcut and
- // let them do the justification. Note that we still call
- // JustifyReflow even if the available space is zero in case the
- // child is a hunk of text that ends in whitespace. The whitespace
- // will be distributed elsewhere causing a proper flush right look
- // for the last word.
- nsRect r;
- kid->GetRect(r);
- nscoord maxWidth = aState.availSize.width;
- nscoord availableSpace = maxWidth - r.width;
- nscoord maxAvailSpace = nscoord(maxWidth * 0.1f);
- if ((availableSpace >= 0) && (availableSpace < maxAvailSpace)) {
- kid->JustifyReflow(aCX, availableSpace);
- kid->SizeTo(r.width + availableSpace, r.height);
- }
- kid->GetNextSibling(kid);
- } else {
- // XXX Get justification of multiple elements working
- while (--lineLength >= 0) {
- kid->GetNextSibling(kid);
- }
+ aState.mY += aState.mBorderPadding.bottom;
+ nscoord lastBottomMargin = aState.mPrevMaxPosBottomMargin -
+ aState.mPrevMaxNegBottomMargin;
+ if (!aState.mUnconstrainedHeight && (lastBottomMargin > 0)) {
+ // It's possible that we don't have room for the last bottom
+ // margin (the last bottom margin is the margin following a block
+ // element that we contain; it isn't applied immediately because
+ // of the margin collapsing logic). This can happen when we are
+ // reflowed in a limited amount of space because we don't know in
+ // advance what the last bottom margin will be.
+ nscoord maxY = aMaxSize.height;
+ if (aState.mY + lastBottomMargin > maxY) {
+ lastBottomMargin = maxY - aState.mY;
+ if (lastBottomMargin < 0) {
+ lastBottomMargin = 0;
}
}
-
- // Finally, now that the in-flow positions of the line's frames are
- // known we can apply relative positioning if any of them need it.
- nsCSSLayout::RelativePositionChildren(aCX, this, lineStart, mLines[i]);
+ aState.mY += lastBottomMargin;
}
-}
+ aDesiredRect.height = aState.mY;
-NS_METHOD nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const
-{
- aIsSplittable = frSplittableNonRectangular;
- return NS_OK;
-}
-
-NS_METHOD nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX,
- nsIFrame* aParent,
- nsIFrame*& aContinuingFrame)
-{
- nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent);
- PrepareContinuingFrame(aCX, aParent, cf);
- aContinuingFrame = cf;
- return NS_OK;
-}
-
-NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aCX,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsReflowCommand& aReflowCommand,
- ReflowStatus& aStatus)
-{
+ // Set return status
aStatus = frComplete;
-
- if (aReflowCommand.GetTarget() == this) {
- // XXX for now, just do a complete reflow mapped (it'll kinda
- // work, but it's slow)
-
- nsBlockReflowState state;
- SetupState(aCX, state, aMaxSize, nsnull, aSpaceManager);
- PRBool reflowMappedOK = ReflowMappedChildren(aCX, state);
- if (!reflowMappedOK) {
- aStatus = frNotComplete;
- }
- } else {
- // XXX not yet implemented
- NS_ABORT();
- // XXX work to compute initial state goes *HERE*
- aDesiredRect.x = 0;
- aDesiredRect.y = 0;
- aDesiredRect.width = 0;
- aDesiredRect.height = 0;
+ if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) {
+ rv = NS_OK;
+ aStatus = frNotComplete;
}
+#ifdef NS_DEBUG
+ // Verify that the line layout code pulled everything up when it
+ // indicates a complete reflow.
+ if (frComplete == aStatus) {
+ nsBlockFrame* nextBlock = (nsBlockFrame*) mNextInFlow;
+ while (nsnull != nextBlock) {
+ NS_ASSERTION((nsnull == nextBlock->mLines) &&
+ (nextBlock->mChildCount == 0) &&
+ (nsnull == nextBlock->mFirstChild),
+ "bad completion status");
+ nextBlock = (nsBlockFrame*) nextBlock->mNextInFlow;
+ }
- mCurrentState = nsnull;
- return NS_OK;
+#if XXX
+ // We better not be in the same parent frame as our prev-in-flow.
+ // If we are it means that we were continued instead of pulling up
+ // children.
+ if (nsnull != mPrevInFlow) {
+ nsIFrame* ourParent = mGeometricParent;
+ nsIFrame* prevParent = ((nsBlockFrame*)mPrevInFlow)->mGeometricParent;
+ NS_ASSERTION(ourParent != prevParent, "bad continuation");
+ }
+#endif
+ }
+#endif
+
+ // Now that reflow has finished, remove the cached pointer
+ shell->RemoveCachedData(this);
+ NS_RELEASE(shell);
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+ PostReflowCheck(aStatus);
+#endif
+#ifdef NOISY_REFLOW
+ ListTag(stdout);
+ printf(": After:\n");
+ List(stdout, 1);
+#endif
+ return rv;
}
-PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
+//----------------------------------------------------------------------
+
+NS_METHOD
+nsBlockFrame::ContentAppended(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentInserted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentReplaced(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aOldChild,
+ nsIContent* aNewChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentDeleted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::GetReflowMetrics(nsIPresContext* aPresContext,
+ nsReflowMetrics& aMetrics)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+NS_METHOD
+nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ nsIFrame::ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ aStatus = frComplete;
+ nsBlockReflowState state;
+ rv = InitializeState(aPresContext, aSpaceManager, aMaxSize,
+ aMaxElementSize, state);
+ if (NS_OK == rv) {
+ nsRect desiredRect;
+ rv = DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus);
+ }
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsReflowCommand& aReflowCommand,
+ nsIFrame::ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+PRBool
+nsBlockFrame::AddFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder)
+{
+ nsIPresShell* shell = aPresContext->GetShell();
+ nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this);
+ NS_RELEASE(shell);
+
+ if (nsnull != state) {
+ // Get the frame associated with the space manager, and get its
+ // nsIAnchoredItems interface
+ nsIFrame* frame = state->mSpaceManager->GetFrame();
+ nsIAnchoredItems* anchoredItems = nsnull;
+
+ frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems);
+ NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface");
+ if (nsnull != anchoredItems) {
+ anchoredItems->AddAnchoredItem(aFloater,
+ nsIAnchoredItems::anHTMLFloater,
+ this);
+ PlaceFloater(aPresContext, aFloater, aPlaceholder, *state);
+ return PR_TRUE;
+ }
+ }
+
+ return PR_FALSE;
+}
+
+void
+nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder)
+{
+ nsIPresShell* shell = aPresContext->GetShell();
+ nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this);
+ NS_RELEASE(shell);
+ if (nsnull != state) {
+ PlaceFloater(aPresContext, aFloater, aPlaceholder, *state);
+ }
+}
+
+PRBool
+nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
{
do {
nsIFrame* parent;
-
aFrame->GetGeometricParent(parent);
- // See if there are any non-zero sized child frames that precede aFrame
- // in the child list
+ // See if there are any non-zero sized child frames that precede
+ // aFrame in the child list
nsIFrame* child;
-
parent->FirstChild(child);
while ((nsnull != child) && (aFrame != child)) {
nsSize size;
@@ -1949,7 +1285,6 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
// We found a non-zero sized child frame that precedes aFrame
return PR_FALSE;
}
-
child->GetNextSibling(child);
}
@@ -1957,184 +1292,127 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
// Walk up one level and check that its parent is left-most as well
aFrame = parent;
} while (aFrame != this);
-
return PR_TRUE;
}
-PRBool nsBlockFrame::AddFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
- PlaceholderFrame* aPlaceholder)
-{
- // Get the frame associated with the space manager, and get its nsIAnchoredItems
- // interface
- nsIFrame* frame = mCurrentState->spaceManager->GetFrame();
- nsIAnchoredItems* anchoredItems = nsnull;
-
- frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems);
- NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface");
-
- if (nsnull != anchoredItems) {
- anchoredItems->AddAnchoredItem(aFloater, nsIAnchoredItems::anHTMLFloater, this);
- PlaceFloater(aCX, aFloater, aPlaceholder);
- return PR_TRUE;
- }
-
- return PR_FALSE;
-}
-
-// XXX The size of the floater needs to be taken into consideration if we're
-// computing a maximum element size
-void nsBlockFrame::PlaceFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
- PlaceholderFrame* aPlaceholder)
+void
+nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder,
+ nsBlockReflowState& aState)
{
// If the floater is the left-most non-zero size child frame then insert
// it before the current line; otherwise add it to the below-current-line
// todo list and we'll handle it when we flush out the line
if (IsLeftMostChild(aPlaceholder)) {
- // Get the type of floater
- nsIStyleContextPtr styleContext;
+ nsISpaceManager* sm = aState.mSpaceManager;
- aFloater->GetStyleContext(aCX, styleContext.AssignRef());
+ // Get the type of floater
+ nsIStyleContextPtr styleContext;
+ aFloater->GetStyleContext(aPresContext, styleContext.AssignRef());
nsStyleDisplay* floaterDisplay = (nsStyleDisplay*)
styleContext->GetData(kStyleDisplaySID);
- if (!mCurrentState->isInline) {
- // Get the current band for this line
- GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin);
- }
-
- // Commit some space in the space manager
- nsRect region;
-
+ // Commit some space in the space manager and adjust our current
+ // band of available space.
+ nsRect region;
aFloater->GetRect(region);
- region.y = mCurrentState->currentBand->availSpace.y;
-
+ region.y = aState.mY;
if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) {
- region.x = mCurrentState->currentBand->availSpace.x;
+ region.x = aState.mX;
} else {
- NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, "bad float type");
- region.x = mCurrentState->currentBand->availSpace.XMost() - region.width;
+ NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats,
+ "bad float type");
+ region.x = aState.mCurrentBand.availSpace.XMost() - region.width;
}
// XXX Don't forget the floater's margins...
- mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, 0);
- mCurrentState->spaceManager->AddRectRegion(region, aFloater);
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->AddRectRegion(region, aFloater);
// Set the origin of the floater in world coordinates
nscoord worldX, worldY;
-
- mCurrentState->spaceManager->GetTranslation(worldX, worldY);
+ sm->GetTranslation(worldX, worldY);
aFloater->MoveTo(region.x + worldX, region.y + worldY);
- mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0);
+ sm->Translate(-aState.mBorderPadding.left, 0);
- // Update the band of available space to reflect space taken up by the floater
- GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin);
+ // Update the band of available space to reflect space taken up by
+ // the floater
+ GetAvailableSpace(aState, aState.mY);
+ if (nsnull != aState.mCurrentLine) {
+ nsLineLayout& lineLayout = *aState.mCurrentLine;
+ lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace);
+ }
} else {
// Add the floater to our to-do list
- mCurrentState->floaterToDo.AppendElement(aFloater);
+ aState.mPendingFloaters.AppendElement(aFloater);
}
}
-NS_METHOD nsBlockFrame::ContentAppended(nsIPresShell* aShell,
- nsIPresContext* aPresContext,
- nsIContent* aContainer)
+void
+nsBlockFrame::PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState,
+ nscoord aY)
{
- // Get the last-in-flow
- nsBlockFrame* flow = (nsBlockFrame*)GetLastInFlow();
- PRInt32 kidIndex = flow->NextChildOffset();
- PRInt32 startIndex = kidIndex;
+ NS_PRECONDITION(aState.mPendingFloaters.Count() > 0, "no floaters");
-#if 0
- nsIFrame* kidFrame = nsnull;
- nsIFrame* prevKidFrame;
-
- flow->LastChild(prevKidFrame);
- for (;;) {
- // Get the next content object
- nsIContentPtr kid = mContent->ChildAt(kidIndex);
- if (nsnull == kid) {
- break;
- }
+ nsISpaceManager* sm = aState.mSpaceManager;
+ nsBlockBandData* bd = &aState.mCurrentBand;
- // Resolve style for the kid
- nsIStyleContextPtr kidSC = aPresContext->ResolveStyleContextFor(kid, this);
- nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
- kidSC->GetData(kStyleDisplaySID);
+ // XXX Factor this code with PlaceFloater()...
+ PRInt32 numFloaters = aState.mPendingFloaters.Count();
+ for (PRInt32 i = 0; i < numFloaters; i++) {
+ nsIFrame* floater = (nsIFrame*) aState.mPendingFloaters[i];
+ nsRect region;
- // Is it a floater?
- if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
- PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this);
+ // Get the band of available space
+ // XXX This is inefficient to do this inside the loop...
+ GetAvailableSpace(aState, aY);
+
+ // Get the type of floater
+ nsIStyleContextPtr styleContext;
+ floater->GetStyleContext(aState.mPresContext, styleContext.AssignRef());
+ nsStyleDisplay* sd = (nsStyleDisplay*)
+ styleContext->GetData(kStyleDisplaySID);
+
+ floater->GetRect(region);
+ region.y = bd->availSpace.y;
+ if (NS_STYLE_FLOAT_LEFT == sd->mFloats) {
+ region.x = bd->availSpace.x;
} else {
- // Create initial frame for the child
- nsIContentDelegate* kidDel;
- nsresult fr;
- switch (kidDisplay->mDisplay) {
- case NS_STYLE_DISPLAY_BLOCK:
- case NS_STYLE_DISPLAY_LIST_ITEM:
- // Pseudo block frames do not contain other block elements
- // unless the block element would be the first child.
- if (IsPseudoFrame()) {
- // Update last content offset now that we are done drawing
- // children from our parent.
- SetLastContentOffset(prevKidFrame);
-
- // It better be true that we are not being asked to flow a
- // block element as our first child. That means the body
- // decided it needed a pseudo-frame when it shouldn't have.
- NS_ASSERTION(nsnull != mFirstChild, "bad body");
-
- return NS_OK;
- }
- // FALL THROUGH (and create frame)
-
- case NS_STYLE_DISPLAY_INLINE:
- kidDel = kid->GetDelegate(aPresContext);
- kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this);
- NS_RELEASE(kidDel);
- break;
-
- default:
- fr = nsFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- break;
- }
+ NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type");
+ region.x = bd->availSpace.XMost() - region.width;
}
- kidFrame->SetStyleContext(aCX,kidSC);
- // Link child frame into the list of children
- if (nsnull != prevKidFrame) {
-#ifdef NS_DEBUG
- nsIFrame* nextSibling;
+ // XXX Don't forget the floater's margins...
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->AddRectRegion(region, floater);
- prevKidFrame->GetNextSibling(nextSibling);
- NS_ASSERTION(nsnull == nextSibling, "bad append");
-#endif
- prevKidFrame->SetNextSibling(kidFrame);
- } else {
- NS_ASSERTION(nsnull == mFirstChild, "bad create");
- mFirstChild = kidFrame;
- SetFirstContentOffset(kidFrame);
- }
- prevKidFrame = kidFrame;
- kidIndex++;
- mChildCount++;
+ // Set the origin of the floater in world coordinates
+ nscoord worldX, worldY;
+ sm->GetTranslation(worldX, worldY);
+ floater->MoveTo(region.x + worldX, region.y + worldY);
+ sm->Translate(-aState.mBorderPadding.left, 0);
}
- SetLastContentOffset(prevKidFrame);
-#endif
- // If this is a pseudo-frame then our parent will generate the
- // reflow command. Otherwise, if the container is us then we should
- // generate the reflow command because we were directly called.
- if (!IsPseudoFrame() && (aContainer == mContent)) {
- nsReflowCommand* rc =
- new nsReflowCommand(aPresContext, flow, nsReflowCommand::FrameAppended,
- startIndex);
- aShell->AppendReflowCommand(rc);
+ aState.mPendingFloaters.Clear();
+
+ // Pass on updated available space to the current line
+ if (nsnull != aState.mCurrentLine) {
+ nsLineLayout& lineLayout = *aState.mCurrentLine;
+ lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace);
}
- return NS_OK;
}
-PRIntn nsBlockFrame::GetSkipSides() const
+//----------------------------------------------------------------------
+
+nsLineData*
+nsBlockFrame::GetFirstLine()
+{
+ return mLines;
+}
+
+PRIntn
+nsBlockFrame::GetSkipSides() const
{
PRIntn skip = 0;
if (nsnull != mPrevInFlow) {
@@ -2145,40 +1423,3 @@ PRIntn nsBlockFrame::GetSkipSides() const
}
return skip;
}
-
-nsHTMLFrameType nsBlockFrame::GetFrameType() const
-{
- return eHTMLFrame_Block;
-}
-
-NS_METHOD nsBlockFrame::ListTag(FILE* out) const
-{
- if ((nsnull != mGeometricParent) && IsPseudoFrame()) {
- fprintf(out, "*block<");
- nsIAtom* atom = mContent->GetTag();
- if (nsnull != atom) {
- nsAutoString tmp;
- atom->ToString(tmp);
- fputs(tmp, out);
- }
- fprintf(out, ">(%d)@%p", mIndexInParent, this);
- } else {
- nsHTMLContainerFrame::ListTag(out);
- }
- return NS_OK;
-}
-
-#ifdef NS_DEBUG
-void nsBlockFrame::DumpFlow() const
-{
-#ifdef NOISY_FLOW
- nsBlockFrame* flow = (nsBlockFrame*) mNextInFlow;
- while (flow != 0) {
- printf(" %p: [%d,%d,%c]\n",
- flow, flow->mFirstContentOffset, flow->mLastContentOffset,
- (flow->mLastContentIsComplete ? 'T' : 'F'));
- flow = (nsBlockFrame*) flow->mNextInFlow;
- }
-#endif
-}
-#endif
diff --git a/layout/html/base/src/nsBlockFrame.h b/layout/html/base/src/nsBlockFrame.h
index f11bba52ca10..f3fa810539b5 100644
--- a/layout/html/base/src/nsBlockFrame.h
+++ b/layout/html/base/src/nsBlockFrame.h
@@ -20,158 +20,101 @@
#include "nsHTMLContainerFrame.h"
#include "nsIFloaterContainer.h"
-#include "nsIHTMLFrameType.h"
#include "nsIRunaround.h"
+#include "nsISpaceManager.h"
+#include "nsLineLayout.h"
#include "nsVoidArray.h"
-struct BlockBandData;
+
struct nsMargin;
struct nsStyleDisplay;
struct nsStyleFont;
struct nsStyleText;
+class nsBlockFrame;
+struct nsBandData;
+
+struct nsBlockBandData : public nsBandData {
+ // Trapezoid's used during band processing
+ nsBandTrapezoid data[12];
+
+ // Bounding rect of available space between any left and right floaters
+ nsRect availSpace;
+
+ nsBlockBandData() {
+ size = 12;
+ trapezoids = data;
+ }
+
+ /**
+ * Computes the bounding rect of the available space, i.e. space
+ * between any left and right floaters Uses the current trapezoid
+ * data, see nsISpaceManager::GetBandData(). Also updates member
+ * data "availSpace".
+ */
+ void ComputeAvailSpaceRect();
+};
-/**
- * Block frames have some state which needs to be made
- * available to child frames for proper reflow. This structure
- * describes that state.
- */
struct nsBlockReflowState {
- // True if this is the first line of text in a block container
- PRPackedBool firstLine;
-
- // True if leading whitespace is allowed to show
- PRPackedBool allowLeadingWhitespace;
-
- // This is set when some child frame needs a break after it's placed
- PRPackedBool breakAfterChild;
-
- // This is set when a child needs a break before it's placed. Note
- // that this value is set by a child AFTER we have called it's
- // ResizeReflow method.
- PRPackedBool breakBeforeChild;
-
- // This is set when the first child should be treated specially
- // because it's an inside list item bullet.
- // XXX this can go away once we have a way for the bullet's style
- // molecule to *not* be the same as it's parent's
- PRPackedBool firstChildIsInsideBullet;
-
- // For pre-formatted text, this is our current column
- PRIntn column;
-
- // The next list ordinal value
- PRInt32 nextListOrdinal;
-
- //----------------------------------------------------------------------
- // State from here on down is not to be used by block child frames!
-
- // Space manager to use
- nsISpaceManager* spaceManager;
-
- // Block's style context
- nsIStyleContext* styleContext;
- nsStyleText* styleText;
- nsStyleFont* styleFont;
- nsStyleDisplay* styleDisplay;
-
- // Block's available size (computed from the block's parent)
- nsSize availSize;
-
- // Current band of available space. Used for doing runaround
- BlockBandData* currentBand;
-
- // Pointer to a max-element-size (nsnull if none required)
- nsSize* maxElementSize;
-
- // The maximum x-most of our lines and block-level elements. This is used to
- // compute our desired size, and includes our left border/padding. For block-
- // level elements this also includes the block's right margin.
- nscoord kidXMost;
-
- // Current reflow position
- nscoord y;
-
- // Current line state
- nscoord x; // inline elements only. not include border/padding
- PRBool isInline; // whether the current is inline or block
- nsVoidArray lineLengths; // line length temporary storage
- PRIntn currentLineNumber; // index into mLines
- nsIFrame* lineStart; // frame starting the line
- PRInt32 lineLength; // length of line
- nscoord* ascents; // ascent information for each child
- nscoord maxAscent; // max ascent for this line
- nscoord maxDescent; // max descent for this line
- nscoord lineWidth; // current width of line
-#if 0
- nscoord maxPosTopMargin; // maximum positive top margin
- nscoord maxNegTopMargin; // maximum negative top margin
-#else
- nscoord topMargin; // current top margin
-#endif
- nscoord maxPosBottomMargin; // maximum positive bottom margin
- nscoord maxNegBottomMargin; // maximum negative bottom margin
- nsSize lineMaxElementSize; // max element size for current line
- PRBool lastContentIsComplete; // reflow status of last child on line
- nsVoidArray floaterToDo; // list of floaters to place below current line
-
- PRInt32 maxAscents; // size of ascent buffer
- nscoord ascentBuf[20];
-
- PRPackedBool needRelativePos; // some kid in line needs relative pos
-
- // Previous line state that we carry forward to the next line
- nsIFrame* prevLineLastFrame;
- nscoord prevLineHeight; // height of the previous line
- nscoord prevMaxPosBottomMargin; // maximum posative bottom margin
- nscoord prevMaxNegBottomMargin; // maximum negative bottom margin
- PRBool prevLineLastContentIsComplete;
-
- // Sanitized version of mol->borderPadding; if this block frame is a
- // pseudo-frame then the margin will be zero'd.
- nsMargin borderPadding;
-
- PRPackedBool justifying; // we are justifying
-
- // Status from last PlaceAndReflowChild
- nsIFrame::ReflowStatus reflowStatus;
-
- // Flags for whether the max size is unconstrained
- PRBool unconstrainedWidth;
- PRBool unconstrainedHeight;
-
nsBlockReflowState();
-
~nsBlockReflowState();
- void Init(const nsSize& aMaxSize, nsSize* aMaxElementSize,
- nsIStyleContext* aStyleContext, nsISpaceManager* aSpaceManager);
+ nsresult Initialize(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockFrame* aBlock);
- void AddAscent(nscoord aAscent);
- void AdvanceToNextLine(nsIFrame* aPrevLineLastFrame, nscoord aPrevLineHeight);
+ nsIPresContext* mPresContext;
-#ifdef NS_DEBUG
- void DumpLine();
- void DumpList();
-#endif
+ nsBlockFrame* mBlock;
+ PRBool mBlockIsPseudo;
+
+ // Current line being reflowed
+ nsLineLayout* mCurrentLine;
+
+ // Previous line's last child frame
+ nsIFrame* mPrevKidFrame;
+
+ // Layout position information
+ nscoord mX;
+ nscoord mY;
+ nsSize mAvailSize;
+ PRPackedBool mUnconstrainedWidth;
+ PRPackedBool mUnconstrainedHeight;
+ nsSize* mMaxElementSizePointer;
+ nscoord mKidXMost;
+
+ // Bottom margin information from the previous line
+ nscoord mPrevMaxNegBottomMargin;
+ nscoord mPrevMaxPosBottomMargin;
+
+ // Block frame border+padding information
+ nsMargin mBorderPadding;
+
+ // Space manager and current band information
+ nsISpaceManager* mSpaceManager;
+ nsBlockBandData mCurrentBand;
+
+ // Array of floaters to place below current line
+ nsVoidArray mPendingFloaters;
+
+ PRInt32 mNextListOrdinal;
+ PRPackedBool mFirstChildIsInsideBullet;
};
//----------------------------------------------------------------------
+/* 94e8e410-de21-11d1-89bf-006008911b81 */
+#define NS_BLOCKFRAME_CID \
+ {0x94e8e410, 0xde21, 0x11d1, {0x89, 0xbf, 0x00, 0x60, 0x08, 0x91, 0x1b, 0x81}}
+
/**
* Block Reflow
*
* The block frame reflow machinery performs "2D" layout. Inline
* elements are flowed into logical lines (left to right or right to
- * left) and the lines are stacked vertically. Block elements are
- * flowed onto their own line after flushing out any preceeding line.
- *
- * After a line is ready to be flushed out, vertical alignment is
- * performed. Vertical alignment may require the line to consume more
- * vertical space than is available thus causing the entire line to
- * be pushed.
- *
- * After vertical alignment is done, horizontal alignment (including
- * justification) is performed. Finally, relative positioning is done
- * on any elements that require it.
+ * left) and the lines are stacked vertically. nsLineLayout is used
+ * for this part of the process. Block elements are flowed directly by
+ * the block reflow logic after flushing out any preceeding line.
*
* During reflow, the block frame will make available to child frames
* it's reflow state using the presentation shell's cached data
@@ -186,144 +129,155 @@ struct nsBlockReflowState {
*
*
Assertions
* mLastContentIsComplete always reflects the state of the last
- * child frame on our chlid list.
+ * child frame on our chlid list.
*/
-class nsBlockFrame : public nsHTMLContainerFrame, public nsIHTMLFrameType,
- public nsIRunaround, public nsIFloaterContainer
+
+// XXX we don't use nsContainerFrame mOverFlowList!!! wasted memory
+
+class nsBlockFrame : public nsHTMLContainerFrame,
+ public nsIRunaround,
+ public nsIFloaterContainer
{
public:
/**
* Create a new block frame that maps the given piece of content.
*/
- static nsresult NewFrame(nsIFrame** aInstancePtrResult,
+ static nsresult NewFrame(nsIFrame** aInstancePtrResult,
nsIContent* aContent,
PRInt32 aIndexInParent,
nsIFrame* aParent);
+ // nsISupports
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
- NS_IMETHOD ResizeReflow(nsIPresContext* aPresContext,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsSize* aMaxElementSize,
- ReflowStatus& aStatus);
-
- NS_IMETHOD IncrementalReflow(nsIPresContext* aPresContext,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsReflowCommand& aReflowCommand,
- ReflowStatus& aStatus);
-
- NS_IMETHOD ContentAppended(nsIPresShell* aShell,
+ // nsIFrame
+ NS_IMETHOD ContentAppended(nsIPresShell* aShell,
nsIPresContext* aPresContext,
- nsIContent* aContainer);
-
+ nsIContent* aContainer);
+ NS_IMETHOD ContentInserted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent);
+ NS_IMETHOD ContentReplaced(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aOldChild,
+ nsIContent* aNewChild,
+ PRInt32 aIndexInParent);
+ NS_IMETHOD ContentDeleted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent);
+ NS_IMETHOD GetReflowMetrics(nsIPresContext* aPresContext,
+ nsReflowMetrics& aMetrics);
NS_IMETHOD IsSplittable(SplittableType& aIsSplittable) const;
-
NS_IMETHOD CreateContinuingFrame(nsIPresContext* aPresContext,
- nsIFrame* aParent,
- nsIFrame*& aContinuingFrame);
+ nsIFrame* aParent,
+ nsIFrame*& aContinuingFrame);
+ NS_IMETHOD List(FILE* out = stdout, PRInt32 aIndent = 0) const;
+ NS_IMETHOD ListTag(FILE* out) const;
+ NS_IMETHOD VerifyTree() const;
- virtual PRBool AddFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
+ // nsIRunaround
+ NS_IMETHOD ResizeReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ nsIFrame::ReflowStatus& aStatus);
+ NS_IMETHOD IncrementalReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsReflowCommand& aReflowCommand,
+ nsIFrame::ReflowStatus& aStatus);
+
+ // nsIFloaterContainer
+ virtual PRBool AddFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
PlaceholderFrame* aPlaceholder);
- virtual void PlaceFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
+ virtual void PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
PlaceholderFrame* aPlaceholder);
- NS_IMETHOD ListTag(FILE* out = stdout) const;
+ // nsBlockFrame
+ nsresult ReflowInlineChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus);
- virtual nsHTMLFrameType GetFrameType() const;
+ nsresult ReflowBlockChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus);
+
+ nsLineData* GetFirstLine();
+
+ static nsBlockReflowState* FindBlockReflowState(nsIPresContext* aPresContext,
+ nsIFrame* aFrame);
protected:
nsBlockFrame(nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent);
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent);
virtual ~nsBlockFrame();
virtual PRIntn GetSkipSides() const;
- PRBool MoreToReflow(nsIPresContext* aCX);
+ virtual void WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow);
- nscoord GetTopMarginFor(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC,
- PRBool aIsInline);
-
- PRBool AdvanceToNextLine(nsIPresContext* aPresContext,
+ nsresult InitializeState(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
nsBlockReflowState& aState);
- void AddInlineChildToLine(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsReflowMetrics& aKidSize,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC);
+ nsresult DoResizeReflow(nsBlockReflowState& aState,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ ReflowStatus& aStatus);
- void AddBlockChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsRect& aKidRect,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC);
+ void DestroyLines();
- void GetAvailSize(nsSize& aResult,
- nsBlockReflowState& aState,
- nsIStyleContext* aKidSC,
- PRBool aIsInline);
+ void DrainOverflowList();
- PRIntn PlaceAndReflowChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* kidFrame,
- nsIStyleContext* aKidSC);
+ nsLineData* CreateLineForOverflowList(nsIFrame* aOverflowList);
- void PushKids(nsBlockReflowState& aState);
+ nsresult VerifyLines(PRBool aFinalCheck) const;
- void SetupState(nsIPresContext* aCX, nsBlockReflowState& aState,
- const nsSize& aMaxSize, nsSize* aMaxElementSize,
- nsISpaceManager* aSpaceManager);
-
- nsresult DoResizeReflow(nsIPresContext* aPresContext,
- nsBlockReflowState& aState,
- nsRect& aDesiredRect,
- ReflowStatus& aStatus);
-
- PRBool ReflowMappedChildren(nsIPresContext* aPresContext,
- nsBlockReflowState& aState);
-
- PRBool PullUpChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState);
-
- ReflowStatus ReflowAppendedChildren(nsIPresContext* aPresContext,
- nsBlockReflowState& aState);
-
- void JustifyLines(nsIPresContext* aPresContext, nsBlockReflowState& aState);
+ nsresult PlaceLine(nsBlockReflowState& aState,
+ nsLineLayout& aLineLayout,
+ nsLineData* aLine);
PRBool IsLeftMostChild(nsIFrame* aFrame);
- void GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY);
-
- void PlaceBelowCurrentLineFloaters(nsIPresContext* aCX,
- nsBlockReflowState& aState,
+ void PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder,
+ nsBlockReflowState& aState);
+ void PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState,
nscoord aY);
- void ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear);
+ nsresult GetAvailableSpace(nsBlockReflowState& aState, nscoord aY);
-#ifdef NS_DEBUG
- void DumpFlow() const;
-#endif
+ PRBool MoreToReflow(nsBlockReflowState& aState);
- /**
- * Array of lines lengths. For each logical line of children, this array
- * contains a count of the number of children on the line.
- */
- PRInt32* mLines;
- PRInt32 mNumLines;
- nsBlockReflowState* mCurrentState;
+ nsresult PushLines(nsBlockReflowState& aState,
+ nsLineData* aLine);
+
+ nsresult ReflowMapped(nsBlockReflowState& aState);
+
+ nsresult ReflowUnmapped(nsBlockReflowState& aState);
+
+ nsLineData* mLines;
};
#endif /* nsBlockFrame_h___ */
diff --git a/layout/html/base/src/nsBlockReflowState.cpp b/layout/html/base/src/nsBlockReflowState.cpp
index 59d7407ec5b1..0228ca80d413 100644
--- a/layout/html/base/src/nsBlockReflowState.cpp
+++ b/layout/html/base/src/nsBlockReflowState.cpp
@@ -16,66 +16,37 @@
* Reserved.
*/
#include "nsBlockFrame.h"
-#include "nsSize.h"
-#include "nsIAnchoredItems.h"
-#include "nsIContent.h"
-#include "nsIContentDelegate.h"
-#include "nsISpaceManager.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
-#include "nsIPresContext.h"
-#include "nsMargin.h"
-#include "nsHTMLIIDs.h"
-#include "nsCSSLayout.h"
-#include "nsCRT.h"
-#include "nsIPresShell.h"
-#include "nsReflowCommand.h"
-#include "nsPlaceholderFrame.h"
-#include "nsHTMLAtoms.h"
-#include "nsHTMLValue.h"
#include "nsIHTMLContent.h"
-#include "nsAbsoluteFrame.h"
+#include "nsIPresContext.h"
+#include "nsIPresShell.h"
+#include "nsIAnchoredItems.h"
+#include "nsPlaceholderFrame.h"
#include "nsIPtr.h"
+#include "nsHTMLAtoms.h"
+#include "nsHTMLIIDs.h"
+#include "nsHTMLValue.h"
-#ifdef NS_DEBUG
-#undef NOISY
-#undef NOISY_FLOW
-#else
-#undef NOISY
-#undef NOISY_FLOW
-#endif
+// XXX what do we do with catastrophic errors (rv < 0)? What is the
+// state of the reflow world after such an error?
-static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID);
-static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID);
-static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID);
+#undef NOISY_REFLOW
static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID);
-static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID);
-static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID);
-static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID);
static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID);
-NS_DEF_PTR(nsIStyleContext);
+static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID);
+static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID);
+static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID);
+
NS_DEF_PTR(nsIContent);
+NS_DEF_PTR(nsIStyleContext);
-struct BlockBandData : public nsBandData {
- nsBandTrapezoid data[12];
+//----------------------------------------------------------------------
- // Bounding rect of available space between any left and right floaters
- nsRect availSpace;
- // Constructor
- BlockBandData() {size = 12; trapezoids = data;}
-
- // Computes the bounding rect of the available space, i.e. space between
- // any left and right floaters
- //
- // Uses the current trapezoid data, see nsISpaceManager::GetBandData().
- // Updates member data "availSpace"
- void ComputeAvailSpaceRect();
-};
-
-void BlockBandData::ComputeAvailSpaceRect()
+void nsBlockBandData::ComputeAvailSpaceRect()
{
nsBandTrapezoid* trapezoid = data;
@@ -121,198 +92,60 @@ void BlockBandData::ComputeAvailSpaceRect()
}
}
-// XXX Bugs
-// 1. right to left reflow can generate negative x coordinates.
-
-// XXX Speedup idea (all containers)
-
-// If I reflow a child and it gives back not-complete status then
-// there is no sense in trying to pullup children. For blocks, it's a
-// little more complicated unless the child is a block - if the child
-// is a block, then we must be out of room hence we should stop. If
-// the child is not a block then our line should be flushed (see #2
-// below) if our line is already empty then we must be out of room.
-
-// For inline frames and column frames, if we reflow a child and get
-// back not-complete status then we should bail immediately because we
-// are out of room.
-
-// XXX Speedup ideas:
-// 1. change pullup code to use line information from next in flow
-// 2. we can advance to next line immediately after reflowing something
-// and noticing that it's not complete.
-// 3. pass down last child information in aState so that pullup, etc.,
-// don't need to recompute it
-
-// XXX TODO:
-// 0. Move justification into line flushing code
-
-// 1. To get ebina margins I need "auto" information from the style
-// system margin's. A bottom/top margin of auto will then be computed like
-// ebina computes it [however the heck that is].
-
-// 2. kicking out floaters and talking with floater container to adjust
-// left and right margins
+//----------------------------------------------------------------------
nsBlockReflowState::nsBlockReflowState()
{
}
-void nsBlockReflowState::Init(const nsSize& aMaxSize,
- nsSize* aMaxElementSize,
- nsIStyleContext* aBlockSC,
- nsISpaceManager* aSpaceManager)
+nsBlockReflowState::~nsBlockReflowState()
{
- firstLine = PR_TRUE;
- allowLeadingWhitespace = PR_FALSE;
- breakAfterChild = PR_FALSE;
- breakBeforeChild = PR_FALSE;
- firstChildIsInsideBullet = PR_FALSE;
- nextListOrdinal = -1;
- column = 0;
+}
- spaceManager = aSpaceManager;
- currentBand = new BlockBandData;
+nsresult
+nsBlockReflowState::Initialize(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockFrame* aBlock)
+{
+ nsresult rv = NS_OK;
- styleContext = aBlockSC;
- styleText = (nsStyleText*) aBlockSC->GetData(kStyleTextSID);
- styleFont = (nsStyleFont*) aBlockSC->GetData(kStyleFontSID);
- styleDisplay = (nsStyleDisplay*) aBlockSC->GetData(kStyleDisplaySID);
+ mPresContext = aPresContext;
+ mBlock = aBlock;
+ mSpaceManager = aSpaceManager;
+ mBlockIsPseudo = aBlock->IsPseudoFrame();
+ mCurrentLine = nsnull;
+ mPrevKidFrame = nsnull;
- justifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
- (NS_STYLE_WHITESPACE_PRE != styleText->mWhiteSpace);
-
- availSize.width = aMaxSize.width;
- availSize.height = aMaxSize.height;
- maxElementSize = aMaxElementSize;
+ mX = 0;
+ mY = 0;
+ mAvailSize = aMaxSize;
+ mUnconstrainedWidth = PRBool(mAvailSize.width == NS_UNCONSTRAINEDSIZE);
+ mUnconstrainedHeight = PRBool(mAvailSize.height == NS_UNCONSTRAINEDSIZE);
+ mMaxElementSizePointer = aMaxElementSize;
if (nsnull != aMaxElementSize) {
aMaxElementSize->width = 0;
aMaxElementSize->height = 0;
}
+ mKidXMost = 0;
- kidXMost = 0;
- x = 0;
- y = 0;
+ mPrevMaxPosBottomMargin = 0;
+ mPrevMaxNegBottomMargin = 0;
- isInline = PR_FALSE;
- currentLineNumber = 0;
- lineStart = nsnull;
- lineLength = 0;
- ascents = ascentBuf;
- maxAscent = 0;
- maxDescent = 0;
- lineWidth = 0;
- maxPosBottomMargin = 0;
- maxNegBottomMargin = 0;
- lineMaxElementSize.width = 0;
- lineMaxElementSize.height = 0;
- lastContentIsComplete = PR_TRUE;
+ mNextListOrdinal = -1;
+ mFirstChildIsInsideBullet = PR_FALSE;
- maxAscents = sizeof(ascentBuf) / sizeof(ascentBuf[0]);
- needRelativePos = PR_FALSE;
-
- prevLineLastFrame = nsnull;
- prevLineHeight = 0;
- topMargin = 0;
- prevMaxPosBottomMargin = 0;
- prevMaxNegBottomMargin = 0;
- prevLineLastContentIsComplete = PR_TRUE;
-
- unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE);
- unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE);
-
- reflowStatus = nsIFrame::frNotComplete;
+ return rv;
}
-nsBlockReflowState::~nsBlockReflowState()
-{
- if (ascents != ascentBuf) {
- delete ascents;
- }
- delete currentBand;
-}
-
-void nsBlockReflowState::AddAscent(nscoord aAscent)
-{
- NS_PRECONDITION(lineLength <= maxAscents, "bad line length");
- if (lineLength == maxAscents) {
- maxAscents *= 2;
- nscoord* newAscents = new nscoord[maxAscents];
- if (nsnull != newAscents) {
- nsCRT::memcpy(newAscents, ascents, sizeof(nscoord) * lineLength);
- if (ascents != ascentBuf) {
- delete ascents;
- }
- ascents = newAscents;
- } else {
- // Yikes! Out of memory!
- return;
- }
- }
- ascents[lineLength] = aAscent;
-}
-
-void nsBlockReflowState::AdvanceToNextLine(nsIFrame* aPrevLineLastFrame,
- nscoord aPrevLineHeight)
-{
- firstLine = PR_FALSE;
- allowLeadingWhitespace = PR_FALSE;
- column = 0;
- breakAfterChild = PR_FALSE;
- breakBeforeChild = PR_FALSE;
- lineStart = nsnull;
- lineLength = 0;
- currentLineNumber++;
- maxAscent = 0;
- maxDescent = 0;
- lineWidth = 0;
- needRelativePos = PR_FALSE;
-
- prevLineLastFrame = aPrevLineLastFrame;
- prevLineHeight = aPrevLineHeight;
- prevMaxPosBottomMargin = maxPosBottomMargin;
- prevMaxNegBottomMargin = maxNegBottomMargin;
-
- // Remember previous line's lastContentIsComplete
- prevLineLastContentIsComplete = lastContentIsComplete;
- lastContentIsComplete = PR_TRUE;
-
- topMargin = 0;
- maxPosBottomMargin = 0;
- maxNegBottomMargin = 0;
-}
-
-#ifdef NS_DEBUG
-void nsBlockReflowState::DumpLine()
-{
- nsIFrame* f = lineStart;
- PRInt32 ll = lineLength;
- while (--ll >= 0) {
- printf(" ");
- ((nsFrame*)f)->ListTag(stdout);/* XXX */
- printf("\n");
- f->GetNextSibling(f);
- }
-}
-
-void nsBlockReflowState::DumpList()
-{
- nsIFrame* f = lineStart;
- while (nsnull != f) {
- printf(" ");
- ((nsFrame*)f)->ListTag(stdout);/* XXX */
- printf("\n");
- f->GetNextSibling(f);
- }
-}
-#endif
-
//----------------------------------------------------------------------
-nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
- nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent)
+nsresult
+nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
+ nsIContent* aContent,
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
if (nsnull == aInstancePtrResult) {
@@ -327,963 +160,818 @@ nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
}
nsBlockFrame::nsBlockFrame(nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent)
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent)
: nsHTMLContainerFrame(aContent, aIndexInParent, aParent)
{
}
nsBlockFrame::~nsBlockFrame()
{
- if (nsnull != mLines) {
- delete mLines;
- }
+ DestroyLines();
}
-nsresult
+void nsBlockFrame::DestroyLines()
+{
+}
+
+NS_METHOD
nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
NS_PRECONDITION(0 != aInstancePtr, "null ptr");
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
}
- if (aIID.Equals(kIHTMLFrameTypeIID)) {
- *aInstancePtr = (void*) ((nsIHTMLFrameType*) this);
+ if (aIID.Equals(kBlockFrameCID)) {
+ *aInstancePtr = (void*) (this);
return NS_OK;
- } else if (aIID.Equals(kIRunaroundIID)) {
+ }
+ else if (aIID.Equals(kIRunaroundIID)) {
*aInstancePtr = (void*) ((nsIRunaround*) this);
return NS_OK;
- } else if (aIID.Equals(kIFloaterContainerIID)) {
+ }
+ else if (aIID.Equals(kIFloaterContainerIID)) {
*aInstancePtr = (void*) ((nsIFloaterContainer*) this);
return NS_OK;
}
return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
}
-// Computes the top margin to use for this child frames based on its display
-// type and the display type of the previous child frame.
-//
-// Adjacent vertical margins between block-level elements are collapsed.
-nscoord nsBlockFrame::GetTopMarginFor(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC,
- PRBool aIsInline)
+NS_METHOD
+nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const
{
- if (aIsInline) {
- // Just use whatever the previous bottom margin was
- return aState.prevMaxPosBottomMargin - aState.prevMaxNegBottomMargin;
+ aIsSplittable = frSplittableNonRectangular;
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX,
+ nsIFrame* aParent,
+ nsIFrame*& aContinuingFrame)
+{
+ nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent);
+ PrepareContinuingFrame(aCX, aParent, cf);
+ aContinuingFrame = cf;
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::ListTag(FILE* out) const
+{
+ if ((nsnull != mGeometricParent) && IsPseudoFrame()) {
+ fprintf(out, "*block<");
+ nsIAtom* atom = mContent->GetTag();
+ if (nsnull != atom) {
+ nsAutoString tmp;
+ atom->ToString(tmp);
+ fputs(tmp, out);
+ }
+ fprintf(out, ">(%d)@%p", mIndexInParent, this);
} else {
- // Does the frame have a prev-in-flow?
- nsIFrame* kidPrevInFlow;
+ nsHTMLContainerFrame::ListTag(out);
+ }
+ return NS_OK;
+}
- aKidFrame->GetPrevInFlow(kidPrevInFlow);
+NS_METHOD
+nsBlockFrame::List(FILE* out, PRInt32 aIndent) const
+{
+ // Indent
+ for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
- if (nsnull == kidPrevInFlow) {
+ // Output the tag
+ ListTag(out);
+
+ // Output the first/last content offset
+ fprintf(out, "[%d,%d,%c] pif=%p nif=%p",
+ mFirstContentOffset, mLastContentOffset,
+ (mLastContentIsComplete ? 'T' : 'F'),
+ mPrevInFlow, mNextInFlow);
+
+ // Output the rect
+ out << mRect;
+
+ // Output the children, one line at a time
+ if (nsnull != mLines) {
+ fputs("<\n", out);
+ aIndent++;
+
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ line->List(out, aIndent);
+ line = line->mNextLine;
+ }
+
+ aIndent--;
+ for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
+ fputs(">\n", out);
+ } else {
+ fputs("<>\n", out);
+ }
+
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::VerifyTree() const
+{
+ nsresult rv = nsHTMLContainerFrame::VerifyTree();
+ if (NS_OK != rv) {
+ return rv;
+ }
+ rv = VerifyLines(PR_TRUE);
+ return rv;
+}
+
+nsresult
+nsBlockFrame::VerifyLines(PRBool aFinalCheck) const
+{
+ nsresult rv = NS_OK;
+
+ // Make sure that the list of children agrees with our child count.
+ // If this is not the case then the child list and the line list are
+ // not properly arranged.
+ PRInt32 len = LengthOf(mFirstChild);
+ NS_ASSERTION(mChildCount == len, "bad child list");
+
+ // Verify that our lines are correctly setup
+ PRInt32 offset = mFirstContentOffset;
+ PRInt32 lineChildCount = 0;
+ nsLineData* line = mLines;
+ nsLineData* prevLine = nsnull;
+ while (nsnull != line) {
+ if (aFinalCheck) {
+ NS_ASSERTION(((offset == line->mFirstContentOffset) &&
+ (line->mFirstContentOffset <= line->mLastContentOffset)),
+ "bad line mFirstContentOffset");
+ NS_ASSERTION(line->mLastContentOffset <= mLastContentOffset,
+ "bad line mLastContentOffset");
+ offset = line->mLastContentOffset;
+ if (line->mLastContentIsComplete) {
+ offset++;
+ }
+ }
+ lineChildCount += line->mChildCount;
+ rv = line->Verify(aFinalCheck);
+ if (NS_OK != rv) {
+ return rv;
+ }
+ prevLine = line;
+ line = line->mNextLine;
+ }
+ if (aFinalCheck && (nsnull != prevLine)) {
+ NS_ASSERTION(prevLine->mLastContentOffset == mLastContentOffset,
+ "bad mLastContentOffset");
+ NS_ASSERTION(prevLine->mLastContentIsComplete == mLastContentIsComplete,
+ "bad mLastContentIsComplete");
+ }
+ NS_ASSERTION(lineChildCount == mChildCount, "bad line counts");
+
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+// Remove a next-in-flow from from this block's list of lines
+
+// XXX problems here:
+// 1. we always have to start from the first line: slow!
+// 2. we can avoid this when the child is not the last child in a line
+
+void
+nsBlockFrame::WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow)
+{
+ // When a reflow indicates completion it's possible that
+ // next-in-flows were just removed. We have to remove them from any
+ // nsLineData's that follow the current line.
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ if (line->mFirstChild == aNextInFlow) {
+ // Remove child from line.
+ if (0 == --line->mChildCount) {
+ line->mFirstChild = nsnull;
+ }
+ else {
+ // Fixup the line
+ nsIFrame* nextKid;
+ aNextInFlow->GetNextSibling(nextKid);
+ line->mFirstChild = nextKid;
+ nextKid->GetIndexInParent(line->mFirstContentOffset);
+ }
+ break;
+ }
+ line = line->mNextLine;
+ }
+}
+
+nsresult
+nsBlockFrame::ReflowInlineChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ aStatus = ReflowChild(aKidFrame, aPresContext, aDesiredSize, aMaxSize,
+ aMaxElementSize);
+ return NS_OK;
+}
+
+nsresult
+nsBlockFrame::ReflowBlockChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ aStatus = ReflowChild(aKidFrame, aPresContext, aSpaceManager,
+ aMaxSize, aDesiredRect, aMaxElementSize);
+ return NS_OK;
+}
+
+nsLineData*
+nsBlockFrame::CreateLineForOverflowList(nsIFrame* aOverflowList)
+{
+ nsLineData* newLine = new nsLineData();
+ if (nsnull != newLine) {
+ nsIFrame* kid = aOverflowList;
+ newLine->mFirstChild = kid;
+ kid->GetIndexInParent(newLine->mFirstContentOffset);
+ newLine->mLastContentOffset = -1;
+ newLine->mLastContentIsComplete = PRPackedBool(0x255);
+ PRInt32 kids = 0;
+ while (nsnull != kid) {
+ kids++;
+ kid->GetNextSibling(kid);
+ }
+ newLine->mChildCount = kids;
+ }
+ return newLine;
+}
+
+void
+nsBlockFrame::DrainOverflowList()
+{
+ nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow;
+ if (nsnull != prevBlock) {
+ nsIFrame* overflowList = prevBlock->mOverflowList;
+ if (nsnull != overflowList) {
+ NS_ASSERTION(nsnull == mFirstChild, "bad overflow list");
+ NS_ASSERTION(nsnull == mLines, "bad overflow list");
+
+ // Create a line to hold the entire overflow list
+ nsLineData* newLine = CreateLineForOverflowList(overflowList);
+
+ // Place the children on our child list; this also reassigns
+ // their geometric parent and updates our mChildCount.
+ AppendChildren(overflowList);
+ prevBlock->mOverflowList = nsnull;
+
+ // The new line is the first line
+ mLines = newLine;
+ }
+ }
+
+ if (nsnull != mOverflowList) {
+ NS_ASSERTION(nsnull != mFirstChild,
+ "overflow list but no mapped children");
+
+ // Create a line to hold the overflow list
+ nsLineData* newLine = CreateLineForOverflowList(mOverflowList);
+
+ // Place the children on our child list; this also reassigns
+ // their geometric parent and updates our mChildCount.
+ AppendChildren(mOverflowList, PR_FALSE);
+ mOverflowList = nsnull;
+
+ // The new line is appended after our other lines
+ nsLineData* prevLine = nsnull;
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ prevLine = line;
+ line = line->mNextLine;
+ }
+ if (nsnull == prevLine) {
+ mLines = newLine;
+ }
+ else {
+ prevLine->mNextLine = newLine;
+ newLine->mPrevLine = prevLine;
+ }
+ }
+#ifdef NS_DEBUG
+ VerifyLines(PR_FALSE);
+#endif
+}
+
+// XXX add in code here that notices if margin's were not provided by
+// the style system and when that is the case to apply the old layout
+// engines margin calculations.
+
+nsresult
+nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
+ nsLineLayout& aLineLayout,
+ nsLineData* aLine)
+{
+ nsresult rv = NS_LINE_LAYOUT_COMPLETE;
+
+ nscoord topMargin = 0;
+ nscoord bottomMargin = 0;
+ nscoord maxNegBottomMargin = 0;
+ nscoord maxPosBottomMargin = 0;
+
+ // See if block margins apply to this line or not
+ PRBool isBlockLine = PR_FALSE;
+ if (1 == aLine->mChildCount) {
+ nsIStyleContextPtr kidSC;
+ nsIFrame* kid = aLine->mFirstChild;
+ rv = kid->GetStyleContext(aState.mPresContext, kidSC.AssignRef());
+ if (NS_OK != rv) return rv;
+
+ nsStyleDisplay* display = (nsStyleDisplay*)
+ kidSC->GetData(kStyleDisplaySID);
+ switch (display->mDisplay) {
+ case NS_STYLE_DISPLAY_BLOCK:
+ case NS_STYLE_DISPLAY_LIST_ITEM:
+ isBlockLine = PR_TRUE;
+ nsStyleSpacing* spacing = (nsStyleSpacing*)
+ kidSC->GetData(kStyleSpacingSID);
+
+ // Calculate top margin by collapsing with previous bottom margin
+ // if any.
nscoord maxNegTopMargin = 0;
nscoord maxPosTopMargin = 0;
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- if (ss->mMargin.top < 0) {
- maxNegTopMargin = -ss->mMargin.top;
+ if (spacing->mMargin.top < 0) {
+ maxNegTopMargin = -spacing->mMargin.top;
} else {
- maxPosTopMargin = ss->mMargin.top;
+ maxPosTopMargin = spacing->mMargin.top;
}
-
- nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin);
- nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin);
- return maxPos - maxNeg;
- } else {
- return 0;
+ nscoord maxPos = PR_MAX(aState.mPrevMaxPosBottomMargin, maxPosTopMargin);
+ nscoord maxNeg = PR_MAX(aState.mPrevMaxNegBottomMargin, maxNegTopMargin);
+ topMargin = maxPos - maxNeg;
+
+ // Save away bottom information for later promotion into aState
+ if (spacing->mMargin.bottom < 0) {
+ maxNegBottomMargin = -spacing->mMargin.bottom;
+ } else {
+ maxPosBottomMargin = spacing->mMargin.bottom;
+ }
+ break;
}
}
+
+ // Before we move the line, make sure that it will fit in it's new
+ // location. It always fits if the height isn't constrained or it's
+ // the first line.
+ nscoord totalHeight = topMargin + aLine->mBounds.height;
+ if (!aState.mUnconstrainedHeight && (aLine != mLines)) {
+ if (aState.mY + totalHeight > aState.mAvailSize.height) {
+ // The line will not fit
+ rv = PushLines(aState, aLine);
+ goto done;
+ }
+ }
+ if (isBlockLine) {
+ if (0 != topMargin) {
+ // We have to move the line now that we know the top margin
+ // for it.
+ aLine->MoveLineBy(0, topMargin);
+ aState.mY += topMargin;
+ }
+ }
+ else {
+ // Apply previous line's bottom margin before the inline-line.
+ nscoord bottomMargin = aState.mPrevMaxPosBottomMargin -
+ aState.mPrevMaxNegBottomMargin;
+ if (0 != bottomMargin) {
+ aLine->MoveLineBy(0, bottomMargin);
+ aState.mY += topMargin;
+ }
+ }
+
+ // Consume space and advance running values
+ aState.mY += aLine->mBounds.height;
+ aState.mPrevMaxNegBottomMargin = maxNegBottomMargin;
+ aState.mPrevMaxPosBottomMargin = maxPosBottomMargin;
+ if (nsnull != aState.mMaxElementSizePointer) {
+ nsSize* maxSize = aState.mMaxElementSizePointer;
+ if (aLineLayout.mReflowData.mMaxElementSize.width > maxSize->width) {
+ maxSize->width = aLineLayout.mReflowData.mMaxElementSize.width;
+ }
+ if (aLineLayout.mReflowData.mMaxElementSize.height > maxSize->height) {
+ maxSize->height = aLineLayout.mReflowData.mMaxElementSize.height;
+ }
+ }
+ {
+ nscoord xmost = aLine->mBounds.XMost();
+ if (xmost > aState.mKidXMost) {
+ aState.mKidXMost = xmost;
+ }
+ }
+
+ // Any below current line floaters to place?
+ if (aState.mPendingFloaters.Count() > 0) {
+ PlaceBelowCurrentLineFloaters(aState, aState.mY);
+ // XXX Factor in the height of the floaters as well when considering
+ // whether the line fits.
+ // The default policy is that if there isn't room for the floaters then
+ // both the line and the floaters are pushed to the next-in-flow...
+ }
+
+ if (aState.mY >= aState.mCurrentBand.availSpace.YMost()) {
+ // The current y coordinate is now past our available space
+ // rectangle. Get a new band of space.
+ GetAvailableSpace(aState, aState.mY);
+ }
+
+done:
+ return rv;
}
-void nsBlockFrame::PlaceBelowCurrentLineFloaters(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nscoord aY)
+// aY has borderpadding.top already factored in
+// aResult is relative to left,aY
+nsresult
+nsBlockFrame::GetAvailableSpace(nsBlockReflowState& aState, nscoord aY)
{
- NS_PRECONDITION(aState.floaterToDo.Count() > 0, "no floaters");
+ nsresult rv = NS_OK;
- // XXX Factor this code with PlaceFloater()...
- PRInt32 numFloaters = aState.floaterToDo.Count();
-
- for (PRInt32 i = 0; i < numFloaters; i++) {
- nsIFrame* floater = (nsIFrame*)aState.floaterToDo[i];
- nsRect region;
+ nsISpaceManager* sm = aState.mSpaceManager;
- // Get the band of available space
- // XXX This is inefficient to do this inside the loop...
- GetAvailableSpaceBand(aState, aY);
+ // Fill in band data for the specific Y coordinate
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->GetBandData(aY, aState.mAvailSize, aState.mCurrentBand);
+ sm->Translate(-aState.mBorderPadding.left, 0);
- // Get the type of floater
- nsIStyleContextPtr styleContext;
-
- floater->GetStyleContext(aCX, styleContext.AssignRef());
- nsStyleDisplay* sd = (nsStyleDisplay*)
- styleContext->GetData(kStyleDisplaySID);
-
- floater->GetRect(region);
- region.y = mCurrentState->currentBand->availSpace.y;
+ // Compute the bounding rect of the available space, i.e. space
+ // between any left and right floaters
+ aState.mCurrentBand.ComputeAvailSpaceRect();
- if (NS_STYLE_FLOAT_LEFT == sd->mFloats) {
- region.x = mCurrentState->currentBand->availSpace.x;
- } else {
- NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type");
- region.x = mCurrentState->currentBand->availSpace.XMost() - region.width;
- }
-
- // XXX Don't forget the floater's margins...
- mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left,
- 0);
- mCurrentState->spaceManager->AddRectRegion(region, floater);
-
- // Set the origin of the floater in world coordinates
- nscoord worldX, worldY;
-
- mCurrentState->spaceManager->GetTranslation(worldX, worldY);
- floater->MoveTo(region.x + worldX, region.y + worldY);
- mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0);
- }
- aState.floaterToDo.Clear();
-}
-
-/**
- * Flush a line out. Return true if the line fits in our available
- * height. If the line does not fit then return false. When the line
- * fits we advance the y coordinate, reset the x coordinate and
- * prepare the nsBlockReflowState for the next line.
- */
-PRBool nsBlockFrame::AdvanceToNextLine(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
- NS_PRECONDITION(aState.lineLength > 0, "bad line");
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
-
- nscoord y = aState.y + aState.topMargin;
- nscoord lineHeight;
-
- if (aState.isInline) {
- // Vertically align the children on this line, returning the height of
- // the line upon completion.
- lineHeight = nsCSSLayout::VerticallyAlignChildren(aCX, this,
- aState.styleFont,
- y,
- aState.lineStart,
- aState.lineLength,
- aState.ascents,
- aState.maxAscent);
-
- // Any below current line floaters to place?
- if (aState.floaterToDo.Count() > 0) {
- PlaceBelowCurrentLineFloaters(aCX, aState, y + lineHeight);
- // XXX Factor in the height of the floaters as well when considering
- // whether the line fits.
- // The default policy is that if there isn't room for the floaters then
- // both the line and the floaters are pushed to the next-in-flow...
- }
- } else {
- nsSize size;
-
- aState.lineStart->GetSize(size);
- lineHeight = size.height;
- }
-
- // The first line always fits
- if (aState.currentLineNumber > 0) {
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- if (y + lineHeight > yb) {
- // After vertical alignment of the children and factoring in the
- // proper margin, the line doesn't fit.
- return PR_FALSE;
- }
- }
-
- if (aState.isInline) {
- // Check if the right-edge of the line exceeds our running x-most
- nscoord xMost = aState.borderPadding.left + aState.lineWidth;
- if (xMost > aState.kidXMost) {
- aState.kidXMost = xMost;
- }
- }
-
- // Advance the y coordinate to the new position where the next
- // line or block element will go.
- aState.y = y + lineHeight;
- aState.x = 0;
-
- // Now that the vertical alignment is done we can perform horizontal
- // alignment and relative positioning. Skip all of these if we are
- // doing an unconstrained (in x) reflow. There's no point in doing
- // the work if we *know* we are going to reflowed again.
- if (!aState.unconstrainedWidth) {
- nsCSSLayout::HorizontallyPlaceChildren(aCX, this, aState.styleText,
- aState.lineStart, aState.lineLength,
- aState.lineWidth,
- aState.availSize.width);
-
- // Finally, now that the in-flow positions of the line's frames are
- // known we can apply relative positioning if any of them need it.
- if (!aState.justifying) {
- nsCSSLayout::RelativePositionChildren(aCX, this,
- aState.lineStart,
- aState.lineLength);
- }
- }
-
- // Record line length
- aState.lineLengths.AppendElement((void*)aState.lineLength);
-
- // Find the last frame in the line
- // XXX keep this as running state in the nsBlockReflowState
- nsIFrame* lastFrame = aState.lineStart;
- PRInt32 lineLen = aState.lineLength - 1;
- while (--lineLen >= 0) {
- lastFrame->GetNextSibling(lastFrame);
- }
-
- // Update maxElementSize
- if (nsnull != aState.maxElementSize) {
- nsSize& lineMax = aState.lineMaxElementSize;
- nsSize* maxMax = aState.maxElementSize;
- if (lineMax.width > maxMax->width) {
- maxMax->width = lineMax.width;
- }
- if (lineMax.height > maxMax->height) {
- maxMax->height = lineMax.height;
- }
- aState.lineMaxElementSize.width = 0;
- aState.lineMaxElementSize.height = 0;
- }
-
- // Advance to the next line
- aState.AdvanceToNextLine(lastFrame, lineHeight);
-
- return PR_TRUE;
-}
-
-/**
- * Add an inline child to the current line. Advance various running
- * values after placement.
- */
-void nsBlockFrame::AddInlineChildToLine(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsReflowMetrics& aKidSize,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC)
-{
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
-
- nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID);
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID);
-
- if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) {
- aState.needRelativePos = PR_TRUE;
- }
-
- // Place and size the child
- // XXX add in left margin from kid
- nsRect r;
- r.y = aState.y;
- r.width = aKidSize.width;
- r.height = aKidSize.height;
- if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) {
- // Left to right positioning.
- r.x = aState.borderPadding.left + aState.x + ss->mMargin.left;
- aState.x += aKidSize.width + ss->mMargin.left + ss->mMargin.right;
- } else {
- // Right to left positioning
- // XXX what should we do when aState.x goes negative???
- r.x = aState.x - aState.borderPadding.right - ss->mMargin.right -
- aKidSize.width;
- aState.x -= aKidSize.width + ss->mMargin.right + ss->mMargin.left;
- }
- aKidFrame->SetRect(r);
- aState.AddAscent(aKidSize.ascent);
- aState.lineWidth += aKidSize.width;
- aState.lineLength++;
-
- // Update maximums for the line
- if (aKidSize.ascent > aState.maxAscent) {
- aState.maxAscent = aKidSize.ascent;
- }
- if (aKidSize.descent > aState.maxDescent) {
- aState.maxDescent = aKidSize.descent;
- }
- // Update running margin maximums
- if (aState.firstChildIsInsideBullet && (aKidFrame == mFirstChild)) {
- // XXX temporary code. Since the molecule for the bullet frame
- // is the same as the LI frame, we get bad style information.
- // ignore it.
- } else {
- nscoord margin;
#if 0
- // XXX CSS2 spec says that top/bottom margin don't affect line height
- // calculation. We're waiting for clarification on this issue...
- if ((margin = ss->mMargin.top) < 0) {
- margin = -margin;
- if (margin > aState.maxNegTopMargin) {
- aState.maxNegTopMargin = margin;
- }
- } else {
- if (margin > aState.maxPosTopMargin) {
- aState.maxPosTopMargin = margin;
- }
- }
+ // XXX For now we assume that there are no height restrictions
+ // (e.g. no "float to bottom of column/page")
+ nsRect& availSpace = aState.mCurrentBand.availSpace;
+ aResult.x = availSpace.x;
+ aResult.y = availSpace.y;
+ aResult.width = availSpace.width;
+ aResult.height = aState.mAvailSize.height;
+#else
+ aState.mCurrentBand.availSpace.MoveBy(aState.mBorderPadding.left,
+ aState.mY);
#endif
- if ((margin = ss->mMargin.bottom) < 0) {
- margin = -margin;
- if (margin > aState.maxNegBottomMargin) {
- aState.maxNegBottomMargin = margin;
- }
- } else {
- if (margin > aState.maxPosBottomMargin) {
- aState.maxPosBottomMargin = margin;
- }
- }
- }
- // Update line max element size
- nsSize& mes = aState.lineMaxElementSize;
- if (nsnull != aKidMaxElementSize) {
- if (aKidMaxElementSize->width > mes.width) {
- mes.width = aKidMaxElementSize->width;
- }
- if (aKidMaxElementSize->height > mes.height) {
- mes.height = aKidMaxElementSize->height;
- }
- }
+ return rv;
}
-// Places and sizes the block-level element, and advances the line.
-// The rect is in the local coordinate space of the kid frame.
-void nsBlockFrame::AddBlockChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsRect& aKidRect,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC)
+// Give aLine and any successive lines to the block's next-in-flow; if
+// we don't have a next-in-flow then push all the children onto our
+// overflow list.
+nsresult
+nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineData* aLine)
{
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
+ PRInt32 i;
- nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID);
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID);
-
- if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) {
- aState.needRelativePos = PR_TRUE;
+ // Split our child-list in two; revert our last content offset and
+ // completion status to the previous line.
+ nsLineData* prevLine = aLine->mPrevLine;
+ NS_PRECONDITION(nsnull != prevLine, "pushing first line");
+ nsIFrame* prevKidFrame = prevLine->mFirstChild;
+ for (i = prevLine->mChildCount - 1; --i >= 0; ) {
+ prevKidFrame->GetNextSibling(prevKidFrame);
}
-
- // Translate from the kid's coordinate space to our coordinate space
- aKidRect.x += aState.borderPadding.left + ss->mMargin.left;
- aKidRect.y += aState.y + aState.topMargin;
-
- // Place and size the child
- aKidFrame->SetRect(aKidRect);
-
- aState.AddAscent(aKidRect.height);
- aState.lineLength++;
-
- // Is this the widest child frame?
- nscoord xMost = aKidRect.XMost() + ss->mMargin.right;
- if (xMost > aState.kidXMost) {
- aState.kidXMost = xMost;
- }
-
- // Update the max element size
- if (nsnull != aKidMaxElementSize) {
- if (aKidMaxElementSize->width > aState.maxElementSize->width) {
- aState.maxElementSize->width = aKidMaxElementSize->width;
- }
- if (aKidMaxElementSize->height > aState.maxElementSize->height) {
- aState.maxElementSize->height = aKidMaxElementSize->height;
- }
- }
-
- // and the bottom line margin information which we'll use when placing
- // the next child
- if (ss->mMargin.bottom < 0) {
- aState.maxNegBottomMargin = -ss->mMargin.bottom;
- } else {
- aState.maxPosBottomMargin = ss->mMargin.bottom;
- }
-
- // Update the running y-offset
- aState.y += aKidRect.height + aState.topMargin;
-
- // Apply relative positioning if necessary
- nsCSSLayout::RelativePositionChildren(aCX, this, aKidFrame, 1);
-
- // Advance to the next line
- aState.AdvanceToNextLine(aKidFrame, aKidRect.height);
-}
-
-/**
- * Compute the available size for reflowing the given child at the
- * current x,y position in the state. Note that this may return
- * negative or zero width/height's if we are out of room.
- */
-void nsBlockFrame::GetAvailSize(nsSize& aResult,
- nsBlockReflowState& aState,
- nsIStyleContext* aKidSC,
- PRBool aIsInline)
-{
- // Determine the maximum available reflow height for the child
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- aResult.height = aState.unconstrainedHeight ? NS_UNCONSTRAINEDSIZE :
- yb - aState.y - aState.topMargin;
-
- // Determine the maximum available reflow width for the child
- if (aState.unconstrainedWidth) {
- aResult.width = NS_UNCONSTRAINEDSIZE;
- } else if (aIsInline) {
- if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) {
- aResult.width = aState.currentBand->availSpace.XMost() - aState.x;
- } else {
- aResult.width = aState.x - aState.currentBand->availSpace.x;
- }
- } else {
- // It's a block. Don't adjust for the left/right margin here. That happens
- // later on once we know the current left/right edge
- aResult.width = aState.availSize.width;
- }
-}
-
-/**
- * Push all of the kids that we have not reflowed, starting at
- * aState.lineStart. aPrevKid is the kid previous to aState.lineStart
- * and is also our last child. Note that line length is NOT a
- * reflection of the number of children we are actually pushing
- * (because we don't break the sibling list as we add children to the
- * line).
- */
-void nsBlockFrame::PushKids(nsBlockReflowState& aState)
-{
- nsIFrame* prevFrame = aState.prevLineLastFrame;
- NS_PRECONDITION(nsnull != prevFrame, "pushing all kids");
#ifdef NS_DEBUG
- nsIFrame* nextSibling;
-
- prevFrame->GetNextSibling(nextSibling);
- NS_PRECONDITION(nextSibling == aState.lineStart, "bad prev line");
+ nsIFrame* nextFrame;
+ prevKidFrame->GetNextSibling(nextFrame);
+ NS_ASSERTION(nextFrame == aLine->mFirstChild, "bad line list");
#endif
+ prevKidFrame->SetNextSibling(nsnull);
+ prevLine->mNextLine = nsnull;
+ mLastContentOffset = prevLine->mLastContentOffset;
+ mLastContentIsComplete = prevLine->mLastContentIsComplete;
+
+ // Push children to our next-in-flow if we have, or to our overflow list
+ nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
+ PRInt32 pushCount = 0;
+ if (nsnull == nextInFlow) {
+ // Place children on the overflow list
+ mOverflowList = aLine->mFirstChild;
+
+ // Destroy the lines
+ nsLineData* line = aLine;
+ while (nsnull != line) {
+ pushCount += line->mChildCount;
+ nsLineData* next = line->mNextLine;
+ delete line;
+ line = next;
+ }
+ }
+ else {
+ aLine->mPrevLine = nsnull;
+
+ // Pass on the children to our next-in-flow
+ nsLineData* line = aLine;
+ prevLine = line;
+ nsIFrame* lastKid = aLine->mFirstChild;
+ nsIFrame* kid = lastKid;
+ while (nsnull != line) {
+ i = line->mChildCount;
+ pushCount += i;
+ NS_ASSERTION(kid == line->mFirstChild, "bad line list");
+ while (--i >= 0) {
+ kid->SetGeometricParent(nextInFlow);
+ nsIFrame* contentParent;
+ kid->GetContentParent(contentParent);
+ if (this == contentParent) {
+ kid->SetContentParent(nextInFlow);
+ }
+ lastKid = kid;
+ kid->GetNextSibling(kid);
+ }
+ prevLine = line;
+ line = line->mNextLine;
+ }
+
+ // Join the two line lists
+ nsLineData* nextInFlowLine = nextInFlow->mLines;
+ if (nsnull != nextInFlowLine) {
+ lastKid->SetNextSibling(nextInFlowLine->mFirstChild);
+ nextInFlowLine->mPrevLine = prevLine;
+ }
+ nsIFrame* firstKid = aLine->mFirstChild;
+ prevLine->mNextLine = nextInFlowLine;
+ nextInFlow->mLines = aLine;
+ nextInFlow->mFirstChild = firstKid;
+ nextInFlow->mChildCount += pushCount;
+ firstKid->GetIndexInParent(nextInFlow->mFirstContentOffset);
#ifdef NS_DEBUG
- PRInt32 numKids = LengthOf(mFirstChild);
- NS_ASSERTION(numKids == mChildCount, "bad child count");
+ nextInFlow->VerifyLines(PR_FALSE);
+#endif
+ }
+ mChildCount -= pushCount;
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return NS_LINE_LAYOUT_NOT_COMPLETE;
+}
+
+nsresult
+nsBlockFrame::ReflowMapped(nsBlockReflowState& aState)
+{
+ nsresult rv = NS_OK;
+
+ // Get some space to start reflowing with
+ GetAvailableSpace(aState, aState.mY);
+
+ nsLineData* prevLine = nsnull;
+ nsLineData* line = mLines;
+ nsLineLayout lineLayout(aState);
+ aState.mCurrentLine = &lineLayout;
+ while (nsnull != line) {
+ // Initialize the line layout for this line
+ rv = lineLayout.Initialize(aState, line);
+ if (NS_OK != rv) {
+ goto done;
+ }
+ lineLayout.mPrevKidFrame = aState.mPrevKidFrame;
+
+ // Reflow the line
+ nsresult lineReflowStatus = lineLayout.ReflowLine();
+ if (lineReflowStatus < 0) {
+ // Some kind of hard error
+ rv = lineReflowStatus;
+ goto done;
+ }
+ mChildCount += lineLayout.mNewFrames;
+
+ // Now place it. It's possible it won't fit.
+ rv = PlaceLine(aState, lineLayout, line);
+ if (NS_LINE_LAYOUT_COMPLETE != rv) {
+ goto done;
+ }
+
+ mLastContentOffset = line->mLastContentOffset;
+ mLastContentIsComplete = PRBool(line->mLastContentIsComplete);
+ prevLine = line;
+ line = line->mNextLine;
+ aState.mPrevKidFrame = lineLayout.mPrevKidFrame;
+ }
+
+done:
+ aState.mCurrentLine = nsnull;
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return rv;
+}
+
+nsresult
+nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState)
+{
+ nsresult rv = NS_OK;
+
+ // If we have no children and we have a prev-in-flow then we need to
+ // pick up where it left off. If we have children, e.g. we're being
+ // resized, then our content offset will have already been set
+ // correctly.
+ nsIFrame* kidPrevInFlow = nsnull;
+ if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
+ nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow;
+ mFirstContentOffset = prev->NextChildOffset();// XXX Is this necessary?
+ if (PR_FALSE == prev->mLastContentIsComplete) {
+ // Our prev-in-flow's last child is not complete
+ prev->LastChild(kidPrevInFlow);
+ }
+ }
+
+ // Get to the last line where the new content may be added
+ nsLineData* line = nsnull;
+ nsLineData* prevLine = nsnull;
+ if (nsnull != mLines) {
+ line = mLines;
+ while (nsnull != line->mNextLine) {
+ line = line->mNextLine;
+ }
+ prevLine = line;
+
+ // If the last line is not complete then kidPrevInFlow should be
+ // set to the last-line's last child.
+ if (!prevLine->mLastContentIsComplete) {
+ kidPrevInFlow = prevLine->GetLastChild();
+ }
+ }
+
+ // Get some space to start reflowing with
+ GetAvailableSpace(aState, aState.mY);
+
+ // Now reflow the new content until we are out of new content or out
+ // of vertical space.
+ PRInt32 kidIndex = NextChildOffset();
+ nsLineLayout lineLayout(aState);
+ aState.mCurrentLine = &lineLayout;
+ lineLayout.mKidPrevInFlow = kidPrevInFlow;
+ PRInt32 contentChildCount = mContent->ChildCount();
+ while (kidIndex < contentChildCount) {
+ if (nsnull == line) {
+ if (!MoreToReflow(aState)) {
+ break;
+ }
+ line = new nsLineData();
+ if (nsnull == line) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+ line->mFirstContentOffset = kidIndex;
+ }
+
+ // Initialize the line layout for this line
+ rv = lineLayout.Initialize(aState, line);
+ if (NS_OK != rv) {
+ goto done;
+ }
+ lineLayout.mKidPrevInFlow = kidPrevInFlow;
+ lineLayout.mPrevKidFrame = aState.mPrevKidFrame;
+
+ // Reflow the line
+ nsresult lineReflowStatus = lineLayout.ReflowLine();
+ if (lineReflowStatus < 0) {
+ // Some kind of hard error
+ rv = lineReflowStatus;
+ goto done;
+ }
+ mChildCount += lineLayout.mNewFrames;
+
+ // Add line to the block; do this before placing the line in case
+ // PushLines is needed.
+ if (nsnull == prevLine) {
+ // For the first line, initialize mFirstContentOffset
+ mFirstContentOffset = line->mFirstContentOffset;
+ mFirstChild = line->mFirstChild;
+ mLines = line;
+ }
+ else {
+ prevLine->mNextLine = line;
+ line->mPrevLine = prevLine;
+ }
+
+ // Now place it. It's possible it won't fit.
+ rv = PlaceLine(aState, lineLayout, line);
+ if (NS_LINE_LAYOUT_COMPLETE != rv) {
+ goto done;
+ }
+ kidIndex = lineLayout.mKidIndex;
+ kidPrevInFlow = lineLayout.mKidPrevInFlow;
+
+ mLastContentOffset = line->mLastContentOffset;
+ mLastContentIsComplete = PRBool(line->mLastContentIsComplete);
+ prevLine = line;
+ line = line->mNextLine;
+ aState.mPrevKidFrame = lineLayout.mPrevKidFrame;
+ }
+
+done:
+ aState.mCurrentLine = nsnull;
+ if (aState.mBlockIsPseudo) {
+ PropagateContentOffsets();
+ }
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return rv;
+}
+
+nsresult
+nsBlockFrame::InitializeState(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockReflowState& aState)
+{
+ nsresult rv;
+ rv = aState.Initialize(aPresContext, aSpaceManager,
+ aMaxSize, aMaxElementSize, this);
+
+ nsStyleSpacing* mySpacing = (nsStyleSpacing*)
+ mStyleContext->GetData(kStyleSpacingSID);
+
+ // Apply border and padding adjustments for regular frames only
+ if (!aState.mBlockIsPseudo) {
+ aState.mY = mySpacing->mBorderPadding.top;
+ aState.mX = mySpacing->mBorderPadding.left;
+ aState.mAvailSize.width -=
+ (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right);
+ aState.mAvailSize.height -=
+ (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom);
+ aState.mBorderPadding = mySpacing->mBorderPadding;
+ }
+ else {
+ aState.mBorderPadding.SizeTo(0, 0, 0, 0);
+ }
+
+ // Setup initial list ordinal value
+ nsIAtom* tag = mContent->GetTag();
+ if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
+ (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
+ nsHTMLValue value;
+ if (eContentAttr_HasValue ==
+ ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) {
+ if (eHTMLUnit_Integer == value.GetUnit()) {
+ aState.mNextListOrdinal = value.GetIntValue();
+ }
+ }
+ }
+ NS_RELEASE(tag);
+
+ return rv;
+}
+
+#if XXX
+NS_METHOD
+nsBlockFrame::SizeTo(nscoord aWidth, nscoord aHeight)
+{
+ printf("size to %g,%g\n", NS_TWIPS_TO_POINTS_FLOAT(aWidth),
+ NS_TWIPS_TO_POINTS_FLOAT(aHeight));
+ return nsHTMLContainerFrame::SizeTo(aWidth, aHeight);
+}
+
+NS_METHOD
+nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ aStatus = frComplete;
+ nsBlockReflowState state;
+ rv = InitializeState(aPresContext, aMaxSize, aMaxElementSize, state);
+ if (NS_OK == rv) {
+ nsRect desiredRect;
+ rv = DoResizeReflow(state, desiredRect, aStatus);
+ aDesiredSize.width = desiredRect.width;
+ aDesiredSize.height = desiredRect.height;
+ aDesiredSize.ascent = aDesiredSize.height;
+ aDesiredSize.descent = 0;
+ }
+ return rv;
+}
#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": push kids (childCount=%d)\n", mChildCount);
- DumpFlow();
-#endif
-
- PushChildren(aState.lineStart, prevFrame, mLastContentIsComplete);
- SetLastContentOffset(prevFrame);
-
- // Set mLastContentIsComplete to the previous lines last content is
- // complete now that the previous line's last child is our last
- // child.
- mLastContentIsComplete = aState.prevLineLastContentIsComplete;
-
- // Fix up child count
- // XXX is there a better way? aState.lineLength doesn't work because
- // we might be pushing more than just the pending line.
- nsIFrame* kid = mFirstChild;
- PRInt32 kids = 0;
- while (nsnull != kid) {
- kids++;
- kid->GetNextSibling(kid);
- }
- mChildCount = kids;
-
- // Make sure we have no lingering line data
- aState.lineLength = 0;
- aState.lineStart = nsnull;
-
-#ifdef NOISY
- ListTag(stdout);
- printf(": push kids done (childCount=%d) [%c]\n", mChildCount,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-}
-
-/**
- * Gets a band of available space starting at the specified y-offset. Assumes
- * the local coordinate space is currently set to the upper-left origin of the
- * bounding rect
- *
- * Updates "currentBand" and "x" member data of the block reflow state
- */
-void nsBlockFrame::GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY)
-{
- // Gets a band of available space.
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
- aState.spaceManager->GetBandData(aY, aState.availSize, *aState.currentBand);
-
- // Compute the bounding rect of the available space, i.e. space between any
- // left and right floaters
- aState.currentBand->ComputeAvailSpaceRect();
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
- aState.x = aState.currentBand->availSpace.x;
-}
-
-void nsBlockFrame::ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear)
-{
- // Translate the coordinate space
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
-
-getBand:
- nscoord y = aState.y + aState.topMargin;
- PRBool isLeftFloater = PR_FALSE;
- PRBool isRightFloater = PR_FALSE;
-
- // Get a band of available space
- aState.spaceManager->GetBandData(y, aState.availSize, *aState.currentBand);
-
- // Scan the trapezoids looking for left and right floaters
- nsBandTrapezoid* trapezoid = aState.currentBand->trapezoids;
- for (PRInt32 i = 0; i < aState.currentBand->count; i++) {
- // XXX Handle multiply occupied
- if (nsBandTrapezoid::smOccupied == trapezoid->state) {
- nsStyleDisplay* display;
-
- trapezoid->frame->GetStyleData(kStylePositionSID, (nsStyleStruct*&)display);
- if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
- isLeftFloater = PR_TRUE;
- } else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) {
- isRightFloater = PR_TRUE;
- }
- }
-
- trapezoid++;
- }
-
- if (isLeftFloater) {
- if ((aClear == NS_STYLE_CLEAR_LEFT) ||
- (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
- aState.y += aState.currentBand->trapezoids[0].GetHeight();
- goto getBand;
- }
- }
- if (isRightFloater) {
- if ((aClear == NS_STYLE_CLEAR_RIGHT) ||
- (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
- aState.y += aState.currentBand->trapezoids[0].GetHeight();
- goto getBand;
- }
- }
-
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
-}
-
-// Bit's for PlaceAndReflowChild return value
-#define PLACE_FIT 0x1
-#define PLACE_FLOWED 0x2
-
-PRIntn
-nsBlockFrame::PlaceAndReflowChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC)
-{
- nsSize kidMaxElementSize;
- nsSize* pKidMaxElementSize =
- (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull;
-
- // Get line start setup if we are at the start of a new line
- if (nsnull == aState.lineStart) {
- NS_ASSERTION(0 == aState.lineLength, "bad line length");
- aState.lineStart = aKidFrame;
- }
-
- // Get kid and its style
- nsStyleDisplay* styleDisplay = (nsStyleDisplay*)
- aKidSC->GetData(kStyleDisplaySID);
-
- // Figure out if kid is a block element or not
- PRBool isInline = PR_TRUE;
- PRIntn display = styleDisplay->mDisplay;
- if (aState.firstChildIsInsideBullet && (mFirstChild == aKidFrame)) {
- // XXX Special hack for properly reflowing bullets that have the
- // inside value for list-style-position.
- display = NS_STYLE_DISPLAY_INLINE;
- }
- if ((NS_STYLE_DISPLAY_BLOCK == display) ||
- (NS_STYLE_DISPLAY_LIST_ITEM == display)) {
- // Block elements always end up on the next line (unless they are
- // already at the start of the line).
- isInline = PR_FALSE;
- if (aState.lineLength > 0) {
- aState.breakAfterChild = PR_TRUE;
- }
- }
-
- // Handle forced break first
- if (aState.breakAfterChild) {
- NS_ASSERTION(aState.lineStart != aKidFrame, "bad line");
-
- // Get the last child in the current line
- nsIFrame* lastFrame = aState.lineStart;
- PRInt32 lineLen = aState.lineLength - 1;
- while (--lineLen >= 0) {
- lastFrame->GetNextSibling(lastFrame);
- }
-
- if (!AdvanceToNextLine(aCX, aState)) {
- // The previous line didn't fit.
- return 0;
- }
- aState.lineStart = aKidFrame;
-
- // Get the style for the last child, and see if it wanted to clear
- // floaters. This handles the BR tag, which is the only inline
- // element for which clear applies
- nsIStyleContextPtr lastChildSC;
-
- lastFrame->GetStyleContext(aCX, lastChildSC.AssignRef());
- nsStyleDisplay* lastChildDisplay = (nsStyleDisplay*)
- lastChildSC->GetData(kStyleDisplaySID);
- switch (lastChildDisplay->mBreakType) {
- case NS_STYLE_CLEAR_LEFT:
- case NS_STYLE_CLEAR_RIGHT:
- case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
- ClearFloaters(aState, lastChildDisplay->mBreakType);
- break;
- }
- }
-
- // Now that we've handled force breaks (and maybe called AdvanceToNextLine()
- // which checks), remember whether it's an inline frame
- aState.isInline = isInline;
-
- // If we're at the beginning of a line then compute the top margin that we
- // should use
- if (aState.lineStart == aKidFrame) {
- // Compute the top margin to use for this line
- aState.topMargin = GetTopMarginFor(aCX, aState, aKidFrame, aKidSC,
- aState.isInline);
-
- // If it's an inline element then get a band of available space
- //
- // XXX If we have a current band and there's unused space in that band
- // then avoid this call to get a band...
- if (aState.isInline) {
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
- }
- }
-
- // Compute the available space to reflow the child into and then
- // reflow it into that space.
- nsSize kidAvailSize;
- GetAvailSize(kidAvailSize, aState, aKidSC, aState.isInline);
- if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) {
- // No more room
- return 0;
- }
-
- ReflowStatus status;
-
- if (aState.isInline) {
- nsReflowMetrics kidSize;
-
- // Inline elements are never passed the space manager
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
-
- // For first children, we skip all the fit checks because we must
- // fit at least one child for a parent to figure what to do with us.
- if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) {
- NS_ASSERTION(nsnull != aState.lineStart, "bad line start");
-
- if (aKidFrame == aState.lineStart) {
- // Width always fits when we are at the logical left margin.
- // Just check the height.
- //
- // XXX This height check isn't correct now that we have bands of
- // available space...
- if (kidSize.height > kidAvailSize.height) {
- // It's too tall
- return PLACE_FLOWED;
- }
- } else {
- // Examine state and if the breakBeforeChild is set and we
- // aren't already on the new line, do the forcing now.
- // XXX Why aren't we doing this check BEFORE we resize reflow the child?
- if (aState.breakBeforeChild) {
- aState.breakBeforeChild = PR_FALSE;
- if (aKidFrame != aState.lineStart) {
- if (!AdvanceToNextLine(aCX, aState)) {
- // Flushing out the line failed.
- return PLACE_FLOWED;
- }
- aState.lineStart = aKidFrame;
-
- // Get a band of available space
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
-
- // Reflow child now that it has the line to itself
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE);
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
- }
- }
-
- // When we are not at the logical left margin then we need
- // to check the width first. If we are too wide then advance
- // to the next line and try reflowing again.
- if (kidSize.width > kidAvailSize.width) {
- // Too wide. Try next line
- if (!AdvanceToNextLine(aCX, aState)) {
- // Flushing out the line failed.
- return PLACE_FLOWED;
- }
- aState.lineStart = aKidFrame;
-
- // Get a band of available space
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
-
- // Reflow splittable children
- SplittableType isSplittable;
-
- aKidFrame->IsSplittable(isSplittable);
- if (isSplittable != frNotSplittable) {
- // Update size info now that we are on the next line. Then
- // reflow the child into the new available space.
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE);
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
-
- // If we just reflowed our last child then update the
- // mLastContentIsComplete state.
- nsIFrame* nextSibling;
-
- aKidFrame->GetNextSibling(nextSibling);
- if (nsnull == nextSibling) {
- // Use state from the reflow we just did
- mLastContentIsComplete = PRBool(status == frComplete);
- }
- }
-
- // XXX This height check isn't correct now that we have bands of
- // available space...
- if (kidSize.height > kidAvailSize.height) {
- // It's too tall on the next line
- return PLACE_FLOWED;
- }
- // It's ok if it's too wide on the next line.
- }
- }
- }
-
- // Add child to the line
- AddInlineChildToLine(aCX, aState, aKidFrame, kidSize,
- pKidMaxElementSize, aKidSC);
- } else {
- nsRect kidRect;
-
- // Does the block-level element want to clear any floaters that impact
- // it? Note that the clear property only applies to block-level elements
- // and the BR tag
- nsStyleDisplay* styleDisplay = (nsStyleDisplay*)
- aKidSC->GetData(kStyleDisplaySID);
- switch (styleDisplay->mBreakType) {
- case NS_STYLE_CLEAR_LEFT:
- case NS_STYLE_CLEAR_RIGHT:
- case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
- ClearFloaters(aState, styleDisplay->mBreakType);
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_FALSE);
- if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) {
- // No more room
- return 0;
- }
- break;
- }
-
- // Give the block its own local coordinate space.. Note: ReflowChild()
- // will adjust for the child's left/right margin after determining the
- // current left/right edge
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
- // Give the block-level element the opportunity to use the space manager
- status = ReflowChild(aKidFrame, aCX, aState.spaceManager,
- kidAvailSize, kidRect, pKidMaxElementSize);
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
-
- // For first children, we skip all the fit checks because we must
- // fit at least one child for a parent to figure what to do with us.
- if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) {
- // Block elements always fit horizontally (because they are
- // always placed at the logical left margin). Check to see if
- // the block fits vertically
- if (kidRect.YMost() > kidAvailSize.height) {
- // Nope
- return PLACE_FLOWED;
- }
- }
-
- // Add block child
- // XXX We need to set lastContentIsComplete here, because AddBlockChild()
- // calls AdvaneceToNextLine(). We need to restructure the flow of control,
- // and use a state machine...
- aState.lastContentIsComplete = PRBool(status == frComplete);
- AddBlockChild(aCX, aState, aKidFrame, kidRect, pKidMaxElementSize, aKidSC);
- }
-
- // If we just reflowed our last child then update the
- // mLastContentIsComplete state.
- nsIFrame* nextSibling;
-
- aKidFrame->GetNextSibling(nextSibling);
- if (nsnull == nextSibling) {
- // Use state from the reflow we just did
- mLastContentIsComplete = PRBool(status == frComplete);
- }
-
- aState.lastContentIsComplete = PRBool(status == frComplete);
- if (aState.isInline && (frNotComplete == status)) {
- // Since the inline child didn't complete its reflow we *know*
- // that a continuation of it can't possibly fit on the current
- // line. Therefore, set a flag in the state that will cause the
- // a line break before the next frame is placed.
- aState.breakAfterChild = PR_TRUE;
- }
-
- aState.reflowStatus = status;
- return PLACE_FLOWED | PLACE_FIT;
-}
-
-/**
- * Reflow the existing frames.
- *
- * @param aCX presentation context to use
- * @param aState in out parameter which tracks the state of
- * reflow for the block frame.
- * @return true if we successfully reflowed all the mapped children and false
- * otherwise, e.g. we pushed children to the next in flow
- */
PRBool
-nsBlockFrame::ReflowMappedChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n",
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-
- PRBool result = PR_TRUE;
- nsIFrame* kidFrame;
-
- for (kidFrame = mFirstChild; nsnull != kidFrame; ) {
-
- /* we get the kid's style from kidFrame's cached style context */
- nsIStyleContextPtr kidSC;
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
-
- // Attempt to place and reflow the child
-
- // XXX if child is not splittable and it fits just place it where
- // it is, otherwise advance to the next line and place it there if
- // possible
-
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- ReflowStatus status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // The child doesn't fit. Push it and any remaining children.
- PushKids(aState);
- result = PR_FALSE;
- goto push_done;
- }
-
- // Is the child complete?
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- if (frComplete == status) {
- // Yes, the child is complete
- NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
- } else {
- // No the child isn't complete
- if (nsnull == kidNextInFlow) {
- // The child doesn't have a next-in-flow so create a continuing
- // frame. This hooks the child into the flow
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
- NS_ASSERTION(nsnull != continuingFrame, "frame creation failed");
-
- // Add the continuing frame to the sibling list
- nsIFrame* nextSib;
-
- kidFrame->GetNextSibling(nextSib);
- continuingFrame->SetNextSibling(nextSib);
- kidFrame->SetNextSibling(continuingFrame);
- mChildCount++;
- }
-
- // Unlike the inline frame code we can't assume that we used
- // up all of our space because the child's reflow status is
- // frNotComplete. Instead, the child is probably split and
- // we need to reflow the continuations as well.
- }
-
- // Get the next child frame
- kidFrame->GetNextSibling(kidFrame);
- }
-
- push_done:
-#ifdef NS_DEBUG
- nsIFrame* lastChild;
- PRInt32 lastIndexInParent;
-
- LastChild(lastChild);
- lastChild->GetIndexInParent(lastIndexInParent);
- NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset");
-
- PRInt32 len = LengthOf(mFirstChild);
- NS_POSTCONDITION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
-
-#ifdef NOISY
- ListTag(stdout);
- printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n",
- (result ? "" : "NOT "),
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
- return result;
-}
-
-/* XXX:
- * In this method, we seem to be getting the style for the next child,
- * and checking that child's style for the child's display type.
- * The problem is that it's very inefficient to ResolveStyleFor(kid) here
- * and then just throw it away, when we end up doing a ResolveStyleFor(kid)
- * again when we actually create a frame for the content.
- * It would be nice to cache the resolved style, maybe in the reflow state?
- */
-PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX)
+nsBlockFrame::MoreToReflow(nsBlockReflowState& aState)
{
PRBool rv = PR_FALSE;
- if (IsPseudoFrame()) {
+ if (aState.mBlockIsPseudo) {
// Get the next content object that we would like to reflow
PRInt32 kidIndex = NextChildOffset();
nsIContentPtr kid = mContent->ChildAt(kidIndex);
if (kid.IsNotNull()) {
// Resolve style for the kid
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
+ nsIStyleContextPtr kidSC =
+ aState.mPresContext->ResolveStyleContextFor(kid, this);
nsStyleDisplay* kidStyleDisplay = (nsStyleDisplay*)
kidSC->GetData(kStyleDisplaySID);
switch (kidStyleDisplay->mDisplay) {
@@ -1305,640 +993,288 @@ PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX)
return rv;
}
-/**
- * Create new frames for content we haven't yet mapped
- *
- * @param aCX presentation context to use
- * @return frComplete if all content has been mapped and frNotComplete
- * if we should be continued
- */
-nsIFrame::ReflowStatus
-nsBlockFrame::ReflowAppendedChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
+nsBlockReflowState*
+nsBlockFrame::FindBlockReflowState(nsIPresContext* aPresContext,
+ nsIFrame* aFrame)
{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
- nsIFrame* kidPrevInFlow = nsnull;
- ReflowStatus result = frNotComplete;
-
- // If we have no children and we have a prev-in-flow then we need to pick
- // up where it left off. If we have children, e.g. we're being resized, then
- // our content offset should already be set correctly...
- if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
- nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow;
- NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow");
- mFirstContentOffset = prev->NextChildOffset();
- if (PR_FALSE == prev->mLastContentIsComplete) {
- // Our prev-in-flow's last child is not complete
- prev->LastChild(kidPrevInFlow);
- }
- }
-
- // Place our children, one at a time until we are out of children
- PRInt32 kidIndex = NextChildOffset();
- nsIFrame* kidFrame = nsnull;
- nsIFrame* prevKidFrame;
-
- LastChild(prevKidFrame);
- for (;;) {
- // Get the next content object
- nsIContentPtr kid = mContent->ChildAt(kidIndex);
- if (kid.IsNull()) {
- result = frComplete;
- break;
- }
-
- // Resolve style for the kid
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
- nsStylePosition* kidPosition = (nsStylePosition*)
- kidSC->GetData(kStylePositionSID);
- nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
- kidSC->GetData(kStyleDisplaySID);
-
- // Check whether it wants to floated or absolutely positioned
- if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) {
- AbsoluteFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- kidFrame->SetStyleContext(aCX,kidSC);
- } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
- PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- kidFrame->SetStyleContext(aCX,kidSC);
- } else if (nsnull == kidPrevInFlow) {
- // Create initial frame for the child
- nsIContentDelegate* kidDel;
- switch (kidDisplay->mDisplay) {
- case NS_STYLE_DISPLAY_BLOCK:
- case NS_STYLE_DISPLAY_LIST_ITEM:
- // Pseudo block frames do not contain other block elements
- // unless the block element would be the first child.
- if (IsPseudoFrame()) {
- // If we're being used as a pseudo frame, i.e. we map the same
- // content as our parent then we want to indicate we're complete;
- // otherwise we'll be continued and go on mapping children...
-
- // It better be true that we are not being asked to flow a
- // block element as our first child. That means the body
- // decided it needed a pseudo-frame when it shouldn't have.
- NS_ASSERTION(nsnull != mFirstChild, "bad body");
-
- result = frComplete;
- goto done;
- }
- // FALL THROUGH (and create frame)
-
- case NS_STYLE_DISPLAY_INLINE:
- kidDel = kid->GetDelegate(aCX);
- kidFrame = kidDel->CreateFrame(aCX, kid, kidIndex, this);
- NS_RELEASE(kidDel);
- break;
-
- default:
- NS_ASSERTION(nsnull == kidPrevInFlow, "bad prev in flow");
- nsFrame::NewFrame(&kidFrame, kid, kidIndex, this);
+ nsBlockReflowState* state = nsnull;
+ if (nsnull != aFrame) {
+ nsIFrame* parent;
+ aFrame->GetGeometricParent(parent);
+ while (nsnull != parent) {
+ nsBlockFrame* block;
+ nsresult rv = parent->QueryInterface(kBlockFrameCID, (void**) &block);
+ if (NS_OK == rv) {
+ nsIPresShell* shell = aPresContext->GetShell();
+ state = (nsBlockReflowState*) shell->GetCachedData(block);
+ NS_RELEASE(shell);
break;
}
- kidFrame->SetStyleContext(aCX,kidSC);
- } else {
- // Since kid has a prev-in-flow, use that to create the next
- // frame.
- kidPrevInFlow->CreateContinuingFrame(aCX, this, kidFrame);
+ parent->GetGeometricParent(parent);
}
-
- // Link child frame into the list of children. If the frame ends
- // up not fitting and getting pushed, the PushKids code will fixup
- // the child count for us.
- if (nsnull != prevKidFrame) {
-#ifdef NS_DEBUG
- nsIFrame* nextSibling;
-
- prevKidFrame->GetNextSibling(nextSibling);
- NS_ASSERTION(nsnull == nextSibling, "bad append");
-#endif
- prevKidFrame->SetNextSibling(kidFrame);
- } else {
- NS_ASSERTION(nsnull == mFirstChild, "bad create");
- mFirstChild = kidFrame;
- SetFirstContentOffset(kidFrame);
- }
- prevKidFrame = kidFrame;
- mChildCount++;
-
- // Reflow child frame as many times as necessary until it is
- // complete.
- ReflowStatus status;
- do {
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // We ran out of room.
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- mLastContentIsComplete = PRBool(nsnull == kidNextInFlow);
- PushKids(aState);
-
- goto push_done;
- }
-
- // Did the child complete?
- prevKidFrame = kidFrame;
- if (frNotComplete == status) {
- // Child didn't complete so create a continuing frame
- kidPrevInFlow = kidFrame;
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
-
- // Add the continuing frame to the sibling list
- nsIFrame* kidNextSibling;
-
- kidFrame->GetNextSibling(kidNextSibling);
- continuingFrame->SetNextSibling(kidNextSibling);
- kidFrame->SetNextSibling(continuingFrame);
- kidFrame = continuingFrame;
- mChildCount++;
-
- // Switch to new kid style
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
- }
-#ifdef NS_DEBUG
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- NS_ASSERTION(nsnull == kidNextInFlow, "huh?");
-#endif
- } while (frNotComplete == status);
-
- // The child that we just reflowed is complete
-#ifdef NS_DEBUG
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
-#endif
- kidIndex++;
- kidPrevInFlow = nsnull;
}
-
- done:
- // To get here we either completely reflowed all our appended
- // children OR we are a pseudo-frame and we ran into a block
- // element. In either case our last content MUST be complete.
- NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state");
- NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
- SetLastContentOffset(prevKidFrame);
-
- push_done:
-#ifdef NS_DEBUG
- PRInt32 len = LengthOf(mFirstChild);
- NS_ASSERTION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
- return result;
+ return state;
}
-/**
- * Pullup frames from our next in flow and try to place them. Before
- * this is called our previously mapped children, if any have been
- * reflowed which means that the block reflow state's x and y
- * coordinates and other data are ready to go.
- *
- * Return true if we pulled everything up.
- */
-PRBool
-nsBlockFrame::PullUpChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": pullup (childCount=%d) [%d,%d,%c]\n",
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-
- PRBool result = PR_TRUE;
- nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
- nsIFrame* prevKidFrame;
-
- LastChild(prevKidFrame);
- while (nsnull != nextInFlow) {
- // Get first available frame from the next-in-flow
- nsIFrame* kidFrame = PullUpOneChild(nextInFlow, prevKidFrame);
- if (nsnull == kidFrame) {
- // We've pulled up all the children from that next-in-flow, so
- // move to the next next-in-flow.
- nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
- continue;
- }
-
- // Get style information for the pulled up kid
- nsIContentPtr kid;
-
- kidFrame->GetContent(kid.AssignRef());
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
-
- ReflowStatus status;
- do {
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // Push the kids that didn't fit back down to the next-in-flow
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- mLastContentIsComplete = PRBool(nsnull == kidNextInFlow);
- PushKids(aState);
-
- result = PR_FALSE;
- goto push_done;
- }
-
- if (frNotComplete == status) {
- // Child is not complete
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- if (nsnull == kidNextInFlow) {
- // Create a continuing frame for the incomplete child
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
-
- // Add the continuing frame to our sibling list.
- nsIFrame* nextSibling;
-
- kidFrame->GetNextSibling(nextSibling);
- continuingFrame->SetNextSibling(nextSibling);
- kidFrame->SetNextSibling(continuingFrame);
- prevKidFrame = kidFrame;
- kidFrame = continuingFrame;
- mChildCount++;
-
- // Switch to new kid style
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
- } else {
- // The child has a next-in-flow, but it's not one of ours.
- // It *must* be in one of our next-in-flows. Collect it
- // then.
- NS_ASSERTION(!IsChild(kidNextInFlow), "busted kid next-in-flow");
- break;
- }
- }
- } while (frNotComplete == status);
-
- prevKidFrame = kidFrame;
- }
-
- if (nsnull != prevKidFrame) {
- // The only way we can get here is by pulling up every last child
- // in our next-in-flows (and reflowing any continunations they
- // have). Therefore we KNOW that our last child is complete.
- NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state");
- NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
- SetLastContentOffset(prevKidFrame);
- }
-
- push_done:;
-
- if (result == PR_FALSE) {
- // If our next-in-flow is empty OR our next next-in-flow is empty
- // then adjust the offsets of all of the empty next-in-flows.
- nextInFlow = (nsBlockFrame*) mNextInFlow;
- if ((0 == nextInFlow->mChildCount) ||
- ((nsnull != nextInFlow->mNextInFlow) &&
- (0 == ((nsBlockFrame*)(nextInFlow->mNextInFlow))->mChildCount))) {
- // We didn't pullup everything and we need to fixup one of our
- // next-in-flows content offsets.
- AdjustOffsetOfEmptyNextInFlows();
- }
- }
-
-
-#ifdef NS_DEBUG
- PRInt32 len = LengthOf(mFirstChild);
- NS_ASSERTION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n",
- (result ? "" : "NOT "),
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
- return result;
-}
-
-void nsBlockFrame::SetupState(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- const nsSize& aMaxSize,
- nsSize* aMaxElementSize,
- nsISpaceManager* aSpaceManager)
-{
- // Setup reflow state
- aState.Init(aMaxSize, aMaxElementSize, mStyleContext, aSpaceManager);
-
- nsStyleSpacing* mySpacing = (nsStyleSpacing*)
- mStyleContext->GetData(kStyleSpacingSID);
-
- // Apply border and padding adjustments for regular frames only
- if (PR_FALSE == IsPseudoFrame()) {
- aState.borderPadding = mySpacing->mBorderPadding;
- aState.y = mySpacing->mBorderPadding.top;
- aState.availSize.width -=
- (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right);
- aState.availSize.height -=
- (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom);
- } else {
- aState.borderPadding.SizeTo(0, 0, 0, 0);
- }
-
- // Setup initial list ordinal value
- nsIAtom* tag = mContent->GetTag();
- if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
- (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
- nsHTMLValue value;
- if (eContentAttr_HasValue ==
- ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) {
- if (eHTMLUnit_Integer == value.GetUnit()) {
- aState.nextListOrdinal = value.GetIntValue();
- }
- }
- }
- NS_RELEASE(tag);
-
- mCurrentState = &aState;
-}
-
-#include "nsUnitConversion.h"/* XXX */
-NS_METHOD nsBlockFrame::ResizeReflow(nsIPresContext* aCX,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsSize* aMaxElementSize,
- ReflowStatus& aStatus)
-{
- nsBlockReflowState state;
- SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager);
- return DoResizeReflow(aCX, state, aDesiredRect, aStatus);
-}
-
-nsresult nsBlockFrame::DoResizeReflow(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsRect& aDesiredRect,
- ReflowStatus& aStatus)
+nsresult
+nsBlockFrame::DoResizeReflow(nsBlockReflowState& aState,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ ReflowStatus& aStatus)
{
#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
PreReflowCheck();
#endif
-#ifdef NOISY
+#ifdef NOISY_REFLOW
ListTag(stdout);
- printf(": resize reflow %g,%g\n",
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width),
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height));
- DumpFlow();
+ printf(": Before:\n");
+ List(stdout, 1);
#endif
- // Zap old line data
- if (nsnull != mLines) {
- delete mLines;
- mLines = nsnull;
- }
- mNumLines = 0;
+ nsresult rv = NS_OK;
- // Check for an overflow list
- MoveOverflowToChildList();
-
- // Before we start reflowing, cache a pointer to our state structure
- // so that inline frames can find it.
- nsIPresShell* shell = aCX->GetShell();
+ nsIPresShell* shell = aState.mPresContext->GetShell();
shell->PutCachedData(this, &aState);
- // First reflow any existing frames
- PRBool reflowMappedOK = PR_TRUE;
- aStatus = frComplete;
- if (nsnull != mFirstChild) {
- reflowMappedOK = ReflowMappedChildren(aCX, aState);
- if (!reflowMappedOK) {
- aStatus = frNotComplete;
- }
+ // Check for an overflow list
+ DrainOverflowList();
+
+ if (nsnull != mLines) {
+ rv = ReflowMapped(aState);
}
- if (reflowMappedOK) {
- // Any space left?
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- if ((nsnull != mFirstChild) && (aState.y >= yb)) {
- // No space left. Don't try to pull-up children or reflow
- // unmapped. We need to return the correct completion status,
- // so see if there is more to reflow.
- if (MoreToReflow(aCX)) {
- aStatus = frNotComplete;
- }
- } else if (MoreToReflow(aCX)) {
- // Try and pull-up some children from a next-in-flow
- if ((nsnull == mNextInFlow) || PullUpChildren(aCX, aState)) {
- // If we still have unmapped children then create some new frames
- if (MoreToReflow(aCX)) {
- aStatus = ReflowAppendedChildren(aCX, aState);
- }
- } else {
- // We were unable to pull-up all the existing frames from the
- // next in flow
- aStatus = frNotComplete;
- }
+ if (NS_OK == rv) {
+ if ((nsnull != mLines) && (aState.mAvailSize.height <= 0)) {
+ // We are out of space
}
- }
-
- // Deal with last line - make sure it gets vertically and
- // horizontally aligned. This also updates the state's y coordinate
- // which is good because that's how we size ourselves.
- if (0 != aState.lineLength) {
- if (!AdvanceToNextLine(aCX, aState)) {
- // The last line of output doesn't fit. Push all of the kids to
- // the next in flow and change our reflow status to not complete
- // so that we are continued.
-#ifdef NOISY
- ListTag(stdout);
- printf(": pushing kids since last line doesn't fit\n");
-#endif
-
- PushKids(aState);
- aStatus = frNotComplete;
+ if (MoreToReflow(aState)) {
+ rv = ReflowUnmapped(aState);
}
}
- if (frComplete == aStatus) {
- // Don't forget to add in the bottom margin from our last child.
- // Only add it in if there is room for it.
- nscoord margin = aState.prevMaxPosBottomMargin -
- aState.prevMaxNegBottomMargin;
- nscoord y = aState.y + margin;
- if (y <= aState.borderPadding.top + aState.availSize.height) {
- aState.y = y;
- }
- }
-
- // Now that reflow has finished, remove the cached pointer
- shell->RemoveCachedData(this);
- NS_RELEASE(shell);
-
- // Translate state.lineLengths into an integer array
- mNumLines = aState.lineLengths.Count();
- if (mNumLines > 0) {
- mLines = new PRInt32[mNumLines];
- for (PRInt32 i = 0; i < mNumLines; i++) {
- PRInt32 ll = (PRInt32) aState.lineLengths.ElementAt(i);
- mLines[i] = ll;
- }
- }
-
- if (!aState.unconstrainedWidth && aState.justifying) {
- // Perform justification now that we know how many lines we have.
- JustifyLines(aCX, aState);
- }
-
// Return our desired rect and our status
aDesiredRect.x = 0;
aDesiredRect.y = 0;
- aDesiredRect.width = aState.kidXMost + aState.borderPadding.right;
- if (!aState.unconstrainedWidth) {
+ aDesiredRect.width = aState.mKidXMost + aState.mBorderPadding.right;
+ if (!aState.mUnconstrainedWidth) {
// Make sure we're at least as wide as the max size we were given
- nscoord maxWidth = aState.availSize.width + aState.borderPadding.left +
- aState.borderPadding.right;
-
+ nscoord maxWidth = aState.mAvailSize.width + aState.mBorderPadding.left +
+ aState.mBorderPadding.right;
if (aDesiredRect.width < maxWidth) {
aDesiredRect.width = maxWidth;
}
}
- aState.y += aState.borderPadding.bottom;
- aDesiredRect.height = aState.y;
-
-#ifdef NS_DEBUG
- PostReflowCheck(aStatus);
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": resize reflow %g,%g %scomplete [%d,%d,%c]\n",
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width),
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height),
- ((status == frNotComplete) ? "NOT " : ""),
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F')
- );
- DumpFlow();
-#endif
- mCurrentState = nsnull;
- return NS_OK;
-}
-
-void nsBlockFrame::JustifyLines(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
- // XXX we don't justify the last line; what if we are continued,
- // should we do it then?
- nsIFrame* kid = mFirstChild;
- for (PRInt32 i = 0; i < mNumLines; i++) {
- nsIFrame* lineStart = kid;
- PRInt32 lineLength = mLines[i];
- if (i < mNumLines - 1) {
- if (1 == lineLength) {
- // For lines with one element on them we can take a shortcut and
- // let them do the justification. Note that we still call
- // JustifyReflow even if the available space is zero in case the
- // child is a hunk of text that ends in whitespace. The whitespace
- // will be distributed elsewhere causing a proper flush right look
- // for the last word.
- nsRect r;
- kid->GetRect(r);
- nscoord maxWidth = aState.availSize.width;
- nscoord availableSpace = maxWidth - r.width;
- nscoord maxAvailSpace = nscoord(maxWidth * 0.1f);
- if ((availableSpace >= 0) && (availableSpace < maxAvailSpace)) {
- kid->JustifyReflow(aCX, availableSpace);
- kid->SizeTo(r.width + availableSpace, r.height);
- }
- kid->GetNextSibling(kid);
- } else {
- // XXX Get justification of multiple elements working
- while (--lineLength >= 0) {
- kid->GetNextSibling(kid);
- }
+ aState.mY += aState.mBorderPadding.bottom;
+ nscoord lastBottomMargin = aState.mPrevMaxPosBottomMargin -
+ aState.mPrevMaxNegBottomMargin;
+ if (!aState.mUnconstrainedHeight && (lastBottomMargin > 0)) {
+ // It's possible that we don't have room for the last bottom
+ // margin (the last bottom margin is the margin following a block
+ // element that we contain; it isn't applied immediately because
+ // of the margin collapsing logic). This can happen when we are
+ // reflowed in a limited amount of space because we don't know in
+ // advance what the last bottom margin will be.
+ nscoord maxY = aMaxSize.height;
+ if (aState.mY + lastBottomMargin > maxY) {
+ lastBottomMargin = maxY - aState.mY;
+ if (lastBottomMargin < 0) {
+ lastBottomMargin = 0;
}
}
-
- // Finally, now that the in-flow positions of the line's frames are
- // known we can apply relative positioning if any of them need it.
- nsCSSLayout::RelativePositionChildren(aCX, this, lineStart, mLines[i]);
+ aState.mY += lastBottomMargin;
}
-}
+ aDesiredRect.height = aState.mY;
-NS_METHOD nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const
-{
- aIsSplittable = frSplittableNonRectangular;
- return NS_OK;
-}
-
-NS_METHOD nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX,
- nsIFrame* aParent,
- nsIFrame*& aContinuingFrame)
-{
- nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent);
- PrepareContinuingFrame(aCX, aParent, cf);
- aContinuingFrame = cf;
- return NS_OK;
-}
-
-NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aCX,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsReflowCommand& aReflowCommand,
- ReflowStatus& aStatus)
-{
+ // Set return status
aStatus = frComplete;
-
- if (aReflowCommand.GetTarget() == this) {
- // XXX for now, just do a complete reflow mapped (it'll kinda
- // work, but it's slow)
-
- nsBlockReflowState state;
- SetupState(aCX, state, aMaxSize, nsnull, aSpaceManager);
- PRBool reflowMappedOK = ReflowMappedChildren(aCX, state);
- if (!reflowMappedOK) {
- aStatus = frNotComplete;
- }
- } else {
- // XXX not yet implemented
- NS_ABORT();
- // XXX work to compute initial state goes *HERE*
- aDesiredRect.x = 0;
- aDesiredRect.y = 0;
- aDesiredRect.width = 0;
- aDesiredRect.height = 0;
+ if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) {
+ rv = NS_OK;
+ aStatus = frNotComplete;
}
+#ifdef NS_DEBUG
+ // Verify that the line layout code pulled everything up when it
+ // indicates a complete reflow.
+ if (frComplete == aStatus) {
+ nsBlockFrame* nextBlock = (nsBlockFrame*) mNextInFlow;
+ while (nsnull != nextBlock) {
+ NS_ASSERTION((nsnull == nextBlock->mLines) &&
+ (nextBlock->mChildCount == 0) &&
+ (nsnull == nextBlock->mFirstChild),
+ "bad completion status");
+ nextBlock = (nsBlockFrame*) nextBlock->mNextInFlow;
+ }
- mCurrentState = nsnull;
- return NS_OK;
+#if XXX
+ // We better not be in the same parent frame as our prev-in-flow.
+ // If we are it means that we were continued instead of pulling up
+ // children.
+ if (nsnull != mPrevInFlow) {
+ nsIFrame* ourParent = mGeometricParent;
+ nsIFrame* prevParent = ((nsBlockFrame*)mPrevInFlow)->mGeometricParent;
+ NS_ASSERTION(ourParent != prevParent, "bad continuation");
+ }
+#endif
+ }
+#endif
+
+ // Now that reflow has finished, remove the cached pointer
+ shell->RemoveCachedData(this);
+ NS_RELEASE(shell);
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+ PostReflowCheck(aStatus);
+#endif
+#ifdef NOISY_REFLOW
+ ListTag(stdout);
+ printf(": After:\n");
+ List(stdout, 1);
+#endif
+ return rv;
}
-PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
+//----------------------------------------------------------------------
+
+NS_METHOD
+nsBlockFrame::ContentAppended(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentInserted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentReplaced(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aOldChild,
+ nsIContent* aNewChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentDeleted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::GetReflowMetrics(nsIPresContext* aPresContext,
+ nsReflowMetrics& aMetrics)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+NS_METHOD
+nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ nsIFrame::ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ aStatus = frComplete;
+ nsBlockReflowState state;
+ rv = InitializeState(aPresContext, aSpaceManager, aMaxSize,
+ aMaxElementSize, state);
+ if (NS_OK == rv) {
+ nsRect desiredRect;
+ rv = DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus);
+ }
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsReflowCommand& aReflowCommand,
+ nsIFrame::ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+PRBool
+nsBlockFrame::AddFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder)
+{
+ nsIPresShell* shell = aPresContext->GetShell();
+ nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this);
+ NS_RELEASE(shell);
+
+ if (nsnull != state) {
+ // Get the frame associated with the space manager, and get its
+ // nsIAnchoredItems interface
+ nsIFrame* frame = state->mSpaceManager->GetFrame();
+ nsIAnchoredItems* anchoredItems = nsnull;
+
+ frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems);
+ NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface");
+ if (nsnull != anchoredItems) {
+ anchoredItems->AddAnchoredItem(aFloater,
+ nsIAnchoredItems::anHTMLFloater,
+ this);
+ PlaceFloater(aPresContext, aFloater, aPlaceholder, *state);
+ return PR_TRUE;
+ }
+ }
+
+ return PR_FALSE;
+}
+
+void
+nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder)
+{
+ nsIPresShell* shell = aPresContext->GetShell();
+ nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this);
+ NS_RELEASE(shell);
+ if (nsnull != state) {
+ PlaceFloater(aPresContext, aFloater, aPlaceholder, *state);
+ }
+}
+
+PRBool
+nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
{
do {
nsIFrame* parent;
-
aFrame->GetGeometricParent(parent);
- // See if there are any non-zero sized child frames that precede aFrame
- // in the child list
+ // See if there are any non-zero sized child frames that precede
+ // aFrame in the child list
nsIFrame* child;
-
parent->FirstChild(child);
while ((nsnull != child) && (aFrame != child)) {
nsSize size;
@@ -1949,7 +1285,6 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
// We found a non-zero sized child frame that precedes aFrame
return PR_FALSE;
}
-
child->GetNextSibling(child);
}
@@ -1957,184 +1292,127 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
// Walk up one level and check that its parent is left-most as well
aFrame = parent;
} while (aFrame != this);
-
return PR_TRUE;
}
-PRBool nsBlockFrame::AddFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
- PlaceholderFrame* aPlaceholder)
-{
- // Get the frame associated with the space manager, and get its nsIAnchoredItems
- // interface
- nsIFrame* frame = mCurrentState->spaceManager->GetFrame();
- nsIAnchoredItems* anchoredItems = nsnull;
-
- frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems);
- NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface");
-
- if (nsnull != anchoredItems) {
- anchoredItems->AddAnchoredItem(aFloater, nsIAnchoredItems::anHTMLFloater, this);
- PlaceFloater(aCX, aFloater, aPlaceholder);
- return PR_TRUE;
- }
-
- return PR_FALSE;
-}
-
-// XXX The size of the floater needs to be taken into consideration if we're
-// computing a maximum element size
-void nsBlockFrame::PlaceFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
- PlaceholderFrame* aPlaceholder)
+void
+nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder,
+ nsBlockReflowState& aState)
{
// If the floater is the left-most non-zero size child frame then insert
// it before the current line; otherwise add it to the below-current-line
// todo list and we'll handle it when we flush out the line
if (IsLeftMostChild(aPlaceholder)) {
- // Get the type of floater
- nsIStyleContextPtr styleContext;
+ nsISpaceManager* sm = aState.mSpaceManager;
- aFloater->GetStyleContext(aCX, styleContext.AssignRef());
+ // Get the type of floater
+ nsIStyleContextPtr styleContext;
+ aFloater->GetStyleContext(aPresContext, styleContext.AssignRef());
nsStyleDisplay* floaterDisplay = (nsStyleDisplay*)
styleContext->GetData(kStyleDisplaySID);
- if (!mCurrentState->isInline) {
- // Get the current band for this line
- GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin);
- }
-
- // Commit some space in the space manager
- nsRect region;
-
+ // Commit some space in the space manager and adjust our current
+ // band of available space.
+ nsRect region;
aFloater->GetRect(region);
- region.y = mCurrentState->currentBand->availSpace.y;
-
+ region.y = aState.mY;
if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) {
- region.x = mCurrentState->currentBand->availSpace.x;
+ region.x = aState.mX;
} else {
- NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, "bad float type");
- region.x = mCurrentState->currentBand->availSpace.XMost() - region.width;
+ NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats,
+ "bad float type");
+ region.x = aState.mCurrentBand.availSpace.XMost() - region.width;
}
// XXX Don't forget the floater's margins...
- mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, 0);
- mCurrentState->spaceManager->AddRectRegion(region, aFloater);
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->AddRectRegion(region, aFloater);
// Set the origin of the floater in world coordinates
nscoord worldX, worldY;
-
- mCurrentState->spaceManager->GetTranslation(worldX, worldY);
+ sm->GetTranslation(worldX, worldY);
aFloater->MoveTo(region.x + worldX, region.y + worldY);
- mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0);
+ sm->Translate(-aState.mBorderPadding.left, 0);
- // Update the band of available space to reflect space taken up by the floater
- GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin);
+ // Update the band of available space to reflect space taken up by
+ // the floater
+ GetAvailableSpace(aState, aState.mY);
+ if (nsnull != aState.mCurrentLine) {
+ nsLineLayout& lineLayout = *aState.mCurrentLine;
+ lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace);
+ }
} else {
// Add the floater to our to-do list
- mCurrentState->floaterToDo.AppendElement(aFloater);
+ aState.mPendingFloaters.AppendElement(aFloater);
}
}
-NS_METHOD nsBlockFrame::ContentAppended(nsIPresShell* aShell,
- nsIPresContext* aPresContext,
- nsIContent* aContainer)
+void
+nsBlockFrame::PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState,
+ nscoord aY)
{
- // Get the last-in-flow
- nsBlockFrame* flow = (nsBlockFrame*)GetLastInFlow();
- PRInt32 kidIndex = flow->NextChildOffset();
- PRInt32 startIndex = kidIndex;
+ NS_PRECONDITION(aState.mPendingFloaters.Count() > 0, "no floaters");
-#if 0
- nsIFrame* kidFrame = nsnull;
- nsIFrame* prevKidFrame;
-
- flow->LastChild(prevKidFrame);
- for (;;) {
- // Get the next content object
- nsIContentPtr kid = mContent->ChildAt(kidIndex);
- if (nsnull == kid) {
- break;
- }
+ nsISpaceManager* sm = aState.mSpaceManager;
+ nsBlockBandData* bd = &aState.mCurrentBand;
- // Resolve style for the kid
- nsIStyleContextPtr kidSC = aPresContext->ResolveStyleContextFor(kid, this);
- nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
- kidSC->GetData(kStyleDisplaySID);
+ // XXX Factor this code with PlaceFloater()...
+ PRInt32 numFloaters = aState.mPendingFloaters.Count();
+ for (PRInt32 i = 0; i < numFloaters; i++) {
+ nsIFrame* floater = (nsIFrame*) aState.mPendingFloaters[i];
+ nsRect region;
- // Is it a floater?
- if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
- PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this);
+ // Get the band of available space
+ // XXX This is inefficient to do this inside the loop...
+ GetAvailableSpace(aState, aY);
+
+ // Get the type of floater
+ nsIStyleContextPtr styleContext;
+ floater->GetStyleContext(aState.mPresContext, styleContext.AssignRef());
+ nsStyleDisplay* sd = (nsStyleDisplay*)
+ styleContext->GetData(kStyleDisplaySID);
+
+ floater->GetRect(region);
+ region.y = bd->availSpace.y;
+ if (NS_STYLE_FLOAT_LEFT == sd->mFloats) {
+ region.x = bd->availSpace.x;
} else {
- // Create initial frame for the child
- nsIContentDelegate* kidDel;
- nsresult fr;
- switch (kidDisplay->mDisplay) {
- case NS_STYLE_DISPLAY_BLOCK:
- case NS_STYLE_DISPLAY_LIST_ITEM:
- // Pseudo block frames do not contain other block elements
- // unless the block element would be the first child.
- if (IsPseudoFrame()) {
- // Update last content offset now that we are done drawing
- // children from our parent.
- SetLastContentOffset(prevKidFrame);
-
- // It better be true that we are not being asked to flow a
- // block element as our first child. That means the body
- // decided it needed a pseudo-frame when it shouldn't have.
- NS_ASSERTION(nsnull != mFirstChild, "bad body");
-
- return NS_OK;
- }
- // FALL THROUGH (and create frame)
-
- case NS_STYLE_DISPLAY_INLINE:
- kidDel = kid->GetDelegate(aPresContext);
- kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this);
- NS_RELEASE(kidDel);
- break;
-
- default:
- fr = nsFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- break;
- }
+ NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type");
+ region.x = bd->availSpace.XMost() - region.width;
}
- kidFrame->SetStyleContext(aCX,kidSC);
- // Link child frame into the list of children
- if (nsnull != prevKidFrame) {
-#ifdef NS_DEBUG
- nsIFrame* nextSibling;
+ // XXX Don't forget the floater's margins...
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->AddRectRegion(region, floater);
- prevKidFrame->GetNextSibling(nextSibling);
- NS_ASSERTION(nsnull == nextSibling, "bad append");
-#endif
- prevKidFrame->SetNextSibling(kidFrame);
- } else {
- NS_ASSERTION(nsnull == mFirstChild, "bad create");
- mFirstChild = kidFrame;
- SetFirstContentOffset(kidFrame);
- }
- prevKidFrame = kidFrame;
- kidIndex++;
- mChildCount++;
+ // Set the origin of the floater in world coordinates
+ nscoord worldX, worldY;
+ sm->GetTranslation(worldX, worldY);
+ floater->MoveTo(region.x + worldX, region.y + worldY);
+ sm->Translate(-aState.mBorderPadding.left, 0);
}
- SetLastContentOffset(prevKidFrame);
-#endif
- // If this is a pseudo-frame then our parent will generate the
- // reflow command. Otherwise, if the container is us then we should
- // generate the reflow command because we were directly called.
- if (!IsPseudoFrame() && (aContainer == mContent)) {
- nsReflowCommand* rc =
- new nsReflowCommand(aPresContext, flow, nsReflowCommand::FrameAppended,
- startIndex);
- aShell->AppendReflowCommand(rc);
+ aState.mPendingFloaters.Clear();
+
+ // Pass on updated available space to the current line
+ if (nsnull != aState.mCurrentLine) {
+ nsLineLayout& lineLayout = *aState.mCurrentLine;
+ lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace);
}
- return NS_OK;
}
-PRIntn nsBlockFrame::GetSkipSides() const
+//----------------------------------------------------------------------
+
+nsLineData*
+nsBlockFrame::GetFirstLine()
+{
+ return mLines;
+}
+
+PRIntn
+nsBlockFrame::GetSkipSides() const
{
PRIntn skip = 0;
if (nsnull != mPrevInFlow) {
@@ -2145,40 +1423,3 @@ PRIntn nsBlockFrame::GetSkipSides() const
}
return skip;
}
-
-nsHTMLFrameType nsBlockFrame::GetFrameType() const
-{
- return eHTMLFrame_Block;
-}
-
-NS_METHOD nsBlockFrame::ListTag(FILE* out) const
-{
- if ((nsnull != mGeometricParent) && IsPseudoFrame()) {
- fprintf(out, "*block<");
- nsIAtom* atom = mContent->GetTag();
- if (nsnull != atom) {
- nsAutoString tmp;
- atom->ToString(tmp);
- fputs(tmp, out);
- }
- fprintf(out, ">(%d)@%p", mIndexInParent, this);
- } else {
- nsHTMLContainerFrame::ListTag(out);
- }
- return NS_OK;
-}
-
-#ifdef NS_DEBUG
-void nsBlockFrame::DumpFlow() const
-{
-#ifdef NOISY_FLOW
- nsBlockFrame* flow = (nsBlockFrame*) mNextInFlow;
- while (flow != 0) {
- printf(" %p: [%d,%d,%c]\n",
- flow, flow->mFirstContentOffset, flow->mLastContentOffset,
- (flow->mLastContentIsComplete ? 'T' : 'F'));
- flow = (nsBlockFrame*) flow->mNextInFlow;
- }
-#endif
-}
-#endif
diff --git a/layout/html/base/src/nsBlockReflowState.h b/layout/html/base/src/nsBlockReflowState.h
index 59d7407ec5b1..0228ca80d413 100644
--- a/layout/html/base/src/nsBlockReflowState.h
+++ b/layout/html/base/src/nsBlockReflowState.h
@@ -16,66 +16,37 @@
* Reserved.
*/
#include "nsBlockFrame.h"
-#include "nsSize.h"
-#include "nsIAnchoredItems.h"
-#include "nsIContent.h"
-#include "nsIContentDelegate.h"
-#include "nsISpaceManager.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
-#include "nsIPresContext.h"
-#include "nsMargin.h"
-#include "nsHTMLIIDs.h"
-#include "nsCSSLayout.h"
-#include "nsCRT.h"
-#include "nsIPresShell.h"
-#include "nsReflowCommand.h"
-#include "nsPlaceholderFrame.h"
-#include "nsHTMLAtoms.h"
-#include "nsHTMLValue.h"
#include "nsIHTMLContent.h"
-#include "nsAbsoluteFrame.h"
+#include "nsIPresContext.h"
+#include "nsIPresShell.h"
+#include "nsIAnchoredItems.h"
+#include "nsPlaceholderFrame.h"
#include "nsIPtr.h"
+#include "nsHTMLAtoms.h"
+#include "nsHTMLIIDs.h"
+#include "nsHTMLValue.h"
-#ifdef NS_DEBUG
-#undef NOISY
-#undef NOISY_FLOW
-#else
-#undef NOISY
-#undef NOISY_FLOW
-#endif
+// XXX what do we do with catastrophic errors (rv < 0)? What is the
+// state of the reflow world after such an error?
-static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID);
-static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID);
-static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID);
+#undef NOISY_REFLOW
static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID);
-static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID);
-static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID);
-static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID);
static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID);
-NS_DEF_PTR(nsIStyleContext);
+static NS_DEFINE_IID(kIAnchoredItemsIID, NS_IANCHOREDITEMS_IID);
+static NS_DEFINE_IID(kIRunaroundIID, NS_IRUNAROUND_IID);
+static NS_DEFINE_IID(kIFloaterContainerIID, NS_IFLOATERCONTAINER_IID);
+
NS_DEF_PTR(nsIContent);
+NS_DEF_PTR(nsIStyleContext);
-struct BlockBandData : public nsBandData {
- nsBandTrapezoid data[12];
+//----------------------------------------------------------------------
- // Bounding rect of available space between any left and right floaters
- nsRect availSpace;
- // Constructor
- BlockBandData() {size = 12; trapezoids = data;}
-
- // Computes the bounding rect of the available space, i.e. space between
- // any left and right floaters
- //
- // Uses the current trapezoid data, see nsISpaceManager::GetBandData().
- // Updates member data "availSpace"
- void ComputeAvailSpaceRect();
-};
-
-void BlockBandData::ComputeAvailSpaceRect()
+void nsBlockBandData::ComputeAvailSpaceRect()
{
nsBandTrapezoid* trapezoid = data;
@@ -121,198 +92,60 @@ void BlockBandData::ComputeAvailSpaceRect()
}
}
-// XXX Bugs
-// 1. right to left reflow can generate negative x coordinates.
-
-// XXX Speedup idea (all containers)
-
-// If I reflow a child and it gives back not-complete status then
-// there is no sense in trying to pullup children. For blocks, it's a
-// little more complicated unless the child is a block - if the child
-// is a block, then we must be out of room hence we should stop. If
-// the child is not a block then our line should be flushed (see #2
-// below) if our line is already empty then we must be out of room.
-
-// For inline frames and column frames, if we reflow a child and get
-// back not-complete status then we should bail immediately because we
-// are out of room.
-
-// XXX Speedup ideas:
-// 1. change pullup code to use line information from next in flow
-// 2. we can advance to next line immediately after reflowing something
-// and noticing that it's not complete.
-// 3. pass down last child information in aState so that pullup, etc.,
-// don't need to recompute it
-
-// XXX TODO:
-// 0. Move justification into line flushing code
-
-// 1. To get ebina margins I need "auto" information from the style
-// system margin's. A bottom/top margin of auto will then be computed like
-// ebina computes it [however the heck that is].
-
-// 2. kicking out floaters and talking with floater container to adjust
-// left and right margins
+//----------------------------------------------------------------------
nsBlockReflowState::nsBlockReflowState()
{
}
-void nsBlockReflowState::Init(const nsSize& aMaxSize,
- nsSize* aMaxElementSize,
- nsIStyleContext* aBlockSC,
- nsISpaceManager* aSpaceManager)
+nsBlockReflowState::~nsBlockReflowState()
{
- firstLine = PR_TRUE;
- allowLeadingWhitespace = PR_FALSE;
- breakAfterChild = PR_FALSE;
- breakBeforeChild = PR_FALSE;
- firstChildIsInsideBullet = PR_FALSE;
- nextListOrdinal = -1;
- column = 0;
+}
- spaceManager = aSpaceManager;
- currentBand = new BlockBandData;
+nsresult
+nsBlockReflowState::Initialize(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockFrame* aBlock)
+{
+ nsresult rv = NS_OK;
- styleContext = aBlockSC;
- styleText = (nsStyleText*) aBlockSC->GetData(kStyleTextSID);
- styleFont = (nsStyleFont*) aBlockSC->GetData(kStyleFontSID);
- styleDisplay = (nsStyleDisplay*) aBlockSC->GetData(kStyleDisplaySID);
+ mPresContext = aPresContext;
+ mBlock = aBlock;
+ mSpaceManager = aSpaceManager;
+ mBlockIsPseudo = aBlock->IsPseudoFrame();
+ mCurrentLine = nsnull;
+ mPrevKidFrame = nsnull;
- justifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
- (NS_STYLE_WHITESPACE_PRE != styleText->mWhiteSpace);
-
- availSize.width = aMaxSize.width;
- availSize.height = aMaxSize.height;
- maxElementSize = aMaxElementSize;
+ mX = 0;
+ mY = 0;
+ mAvailSize = aMaxSize;
+ mUnconstrainedWidth = PRBool(mAvailSize.width == NS_UNCONSTRAINEDSIZE);
+ mUnconstrainedHeight = PRBool(mAvailSize.height == NS_UNCONSTRAINEDSIZE);
+ mMaxElementSizePointer = aMaxElementSize;
if (nsnull != aMaxElementSize) {
aMaxElementSize->width = 0;
aMaxElementSize->height = 0;
}
+ mKidXMost = 0;
- kidXMost = 0;
- x = 0;
- y = 0;
+ mPrevMaxPosBottomMargin = 0;
+ mPrevMaxNegBottomMargin = 0;
- isInline = PR_FALSE;
- currentLineNumber = 0;
- lineStart = nsnull;
- lineLength = 0;
- ascents = ascentBuf;
- maxAscent = 0;
- maxDescent = 0;
- lineWidth = 0;
- maxPosBottomMargin = 0;
- maxNegBottomMargin = 0;
- lineMaxElementSize.width = 0;
- lineMaxElementSize.height = 0;
- lastContentIsComplete = PR_TRUE;
+ mNextListOrdinal = -1;
+ mFirstChildIsInsideBullet = PR_FALSE;
- maxAscents = sizeof(ascentBuf) / sizeof(ascentBuf[0]);
- needRelativePos = PR_FALSE;
-
- prevLineLastFrame = nsnull;
- prevLineHeight = 0;
- topMargin = 0;
- prevMaxPosBottomMargin = 0;
- prevMaxNegBottomMargin = 0;
- prevLineLastContentIsComplete = PR_TRUE;
-
- unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE);
- unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE);
-
- reflowStatus = nsIFrame::frNotComplete;
+ return rv;
}
-nsBlockReflowState::~nsBlockReflowState()
-{
- if (ascents != ascentBuf) {
- delete ascents;
- }
- delete currentBand;
-}
-
-void nsBlockReflowState::AddAscent(nscoord aAscent)
-{
- NS_PRECONDITION(lineLength <= maxAscents, "bad line length");
- if (lineLength == maxAscents) {
- maxAscents *= 2;
- nscoord* newAscents = new nscoord[maxAscents];
- if (nsnull != newAscents) {
- nsCRT::memcpy(newAscents, ascents, sizeof(nscoord) * lineLength);
- if (ascents != ascentBuf) {
- delete ascents;
- }
- ascents = newAscents;
- } else {
- // Yikes! Out of memory!
- return;
- }
- }
- ascents[lineLength] = aAscent;
-}
-
-void nsBlockReflowState::AdvanceToNextLine(nsIFrame* aPrevLineLastFrame,
- nscoord aPrevLineHeight)
-{
- firstLine = PR_FALSE;
- allowLeadingWhitespace = PR_FALSE;
- column = 0;
- breakAfterChild = PR_FALSE;
- breakBeforeChild = PR_FALSE;
- lineStart = nsnull;
- lineLength = 0;
- currentLineNumber++;
- maxAscent = 0;
- maxDescent = 0;
- lineWidth = 0;
- needRelativePos = PR_FALSE;
-
- prevLineLastFrame = aPrevLineLastFrame;
- prevLineHeight = aPrevLineHeight;
- prevMaxPosBottomMargin = maxPosBottomMargin;
- prevMaxNegBottomMargin = maxNegBottomMargin;
-
- // Remember previous line's lastContentIsComplete
- prevLineLastContentIsComplete = lastContentIsComplete;
- lastContentIsComplete = PR_TRUE;
-
- topMargin = 0;
- maxPosBottomMargin = 0;
- maxNegBottomMargin = 0;
-}
-
-#ifdef NS_DEBUG
-void nsBlockReflowState::DumpLine()
-{
- nsIFrame* f = lineStart;
- PRInt32 ll = lineLength;
- while (--ll >= 0) {
- printf(" ");
- ((nsFrame*)f)->ListTag(stdout);/* XXX */
- printf("\n");
- f->GetNextSibling(f);
- }
-}
-
-void nsBlockReflowState::DumpList()
-{
- nsIFrame* f = lineStart;
- while (nsnull != f) {
- printf(" ");
- ((nsFrame*)f)->ListTag(stdout);/* XXX */
- printf("\n");
- f->GetNextSibling(f);
- }
-}
-#endif
-
//----------------------------------------------------------------------
-nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
- nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent)
+nsresult
+nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
+ nsIContent* aContent,
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
if (nsnull == aInstancePtrResult) {
@@ -327,963 +160,818 @@ nsresult nsBlockFrame::NewFrame(nsIFrame** aInstancePtrResult,
}
nsBlockFrame::nsBlockFrame(nsIContent* aContent,
- PRInt32 aIndexInParent,
- nsIFrame* aParent)
+ PRInt32 aIndexInParent,
+ nsIFrame* aParent)
: nsHTMLContainerFrame(aContent, aIndexInParent, aParent)
{
}
nsBlockFrame::~nsBlockFrame()
{
- if (nsnull != mLines) {
- delete mLines;
- }
+ DestroyLines();
}
-nsresult
+void nsBlockFrame::DestroyLines()
+{
+}
+
+NS_METHOD
nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
NS_PRECONDITION(0 != aInstancePtr, "null ptr");
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
}
- if (aIID.Equals(kIHTMLFrameTypeIID)) {
- *aInstancePtr = (void*) ((nsIHTMLFrameType*) this);
+ if (aIID.Equals(kBlockFrameCID)) {
+ *aInstancePtr = (void*) (this);
return NS_OK;
- } else if (aIID.Equals(kIRunaroundIID)) {
+ }
+ else if (aIID.Equals(kIRunaroundIID)) {
*aInstancePtr = (void*) ((nsIRunaround*) this);
return NS_OK;
- } else if (aIID.Equals(kIFloaterContainerIID)) {
+ }
+ else if (aIID.Equals(kIFloaterContainerIID)) {
*aInstancePtr = (void*) ((nsIFloaterContainer*) this);
return NS_OK;
}
return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
}
-// Computes the top margin to use for this child frames based on its display
-// type and the display type of the previous child frame.
-//
-// Adjacent vertical margins between block-level elements are collapsed.
-nscoord nsBlockFrame::GetTopMarginFor(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC,
- PRBool aIsInline)
+NS_METHOD
+nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const
{
- if (aIsInline) {
- // Just use whatever the previous bottom margin was
- return aState.prevMaxPosBottomMargin - aState.prevMaxNegBottomMargin;
+ aIsSplittable = frSplittableNonRectangular;
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX,
+ nsIFrame* aParent,
+ nsIFrame*& aContinuingFrame)
+{
+ nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent);
+ PrepareContinuingFrame(aCX, aParent, cf);
+ aContinuingFrame = cf;
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::ListTag(FILE* out) const
+{
+ if ((nsnull != mGeometricParent) && IsPseudoFrame()) {
+ fprintf(out, "*block<");
+ nsIAtom* atom = mContent->GetTag();
+ if (nsnull != atom) {
+ nsAutoString tmp;
+ atom->ToString(tmp);
+ fputs(tmp, out);
+ }
+ fprintf(out, ">(%d)@%p", mIndexInParent, this);
} else {
- // Does the frame have a prev-in-flow?
- nsIFrame* kidPrevInFlow;
+ nsHTMLContainerFrame::ListTag(out);
+ }
+ return NS_OK;
+}
- aKidFrame->GetPrevInFlow(kidPrevInFlow);
+NS_METHOD
+nsBlockFrame::List(FILE* out, PRInt32 aIndent) const
+{
+ // Indent
+ for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
- if (nsnull == kidPrevInFlow) {
+ // Output the tag
+ ListTag(out);
+
+ // Output the first/last content offset
+ fprintf(out, "[%d,%d,%c] pif=%p nif=%p",
+ mFirstContentOffset, mLastContentOffset,
+ (mLastContentIsComplete ? 'T' : 'F'),
+ mPrevInFlow, mNextInFlow);
+
+ // Output the rect
+ out << mRect;
+
+ // Output the children, one line at a time
+ if (nsnull != mLines) {
+ fputs("<\n", out);
+ aIndent++;
+
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ line->List(out, aIndent);
+ line = line->mNextLine;
+ }
+
+ aIndent--;
+ for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
+ fputs(">\n", out);
+ } else {
+ fputs("<>\n", out);
+ }
+
+ return NS_OK;
+}
+
+NS_METHOD
+nsBlockFrame::VerifyTree() const
+{
+ nsresult rv = nsHTMLContainerFrame::VerifyTree();
+ if (NS_OK != rv) {
+ return rv;
+ }
+ rv = VerifyLines(PR_TRUE);
+ return rv;
+}
+
+nsresult
+nsBlockFrame::VerifyLines(PRBool aFinalCheck) const
+{
+ nsresult rv = NS_OK;
+
+ // Make sure that the list of children agrees with our child count.
+ // If this is not the case then the child list and the line list are
+ // not properly arranged.
+ PRInt32 len = LengthOf(mFirstChild);
+ NS_ASSERTION(mChildCount == len, "bad child list");
+
+ // Verify that our lines are correctly setup
+ PRInt32 offset = mFirstContentOffset;
+ PRInt32 lineChildCount = 0;
+ nsLineData* line = mLines;
+ nsLineData* prevLine = nsnull;
+ while (nsnull != line) {
+ if (aFinalCheck) {
+ NS_ASSERTION(((offset == line->mFirstContentOffset) &&
+ (line->mFirstContentOffset <= line->mLastContentOffset)),
+ "bad line mFirstContentOffset");
+ NS_ASSERTION(line->mLastContentOffset <= mLastContentOffset,
+ "bad line mLastContentOffset");
+ offset = line->mLastContentOffset;
+ if (line->mLastContentIsComplete) {
+ offset++;
+ }
+ }
+ lineChildCount += line->mChildCount;
+ rv = line->Verify(aFinalCheck);
+ if (NS_OK != rv) {
+ return rv;
+ }
+ prevLine = line;
+ line = line->mNextLine;
+ }
+ if (aFinalCheck && (nsnull != prevLine)) {
+ NS_ASSERTION(prevLine->mLastContentOffset == mLastContentOffset,
+ "bad mLastContentOffset");
+ NS_ASSERTION(prevLine->mLastContentIsComplete == mLastContentIsComplete,
+ "bad mLastContentIsComplete");
+ }
+ NS_ASSERTION(lineChildCount == mChildCount, "bad line counts");
+
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+// Remove a next-in-flow from from this block's list of lines
+
+// XXX problems here:
+// 1. we always have to start from the first line: slow!
+// 2. we can avoid this when the child is not the last child in a line
+
+void
+nsBlockFrame::WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow)
+{
+ // When a reflow indicates completion it's possible that
+ // next-in-flows were just removed. We have to remove them from any
+ // nsLineData's that follow the current line.
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ if (line->mFirstChild == aNextInFlow) {
+ // Remove child from line.
+ if (0 == --line->mChildCount) {
+ line->mFirstChild = nsnull;
+ }
+ else {
+ // Fixup the line
+ nsIFrame* nextKid;
+ aNextInFlow->GetNextSibling(nextKid);
+ line->mFirstChild = nextKid;
+ nextKid->GetIndexInParent(line->mFirstContentOffset);
+ }
+ break;
+ }
+ line = line->mNextLine;
+ }
+}
+
+nsresult
+nsBlockFrame::ReflowInlineChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ aStatus = ReflowChild(aKidFrame, aPresContext, aDesiredSize, aMaxSize,
+ aMaxElementSize);
+ return NS_OK;
+}
+
+nsresult
+nsBlockFrame::ReflowBlockChild(nsIFrame* aKidFrame,
+ nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ aStatus = ReflowChild(aKidFrame, aPresContext, aSpaceManager,
+ aMaxSize, aDesiredRect, aMaxElementSize);
+ return NS_OK;
+}
+
+nsLineData*
+nsBlockFrame::CreateLineForOverflowList(nsIFrame* aOverflowList)
+{
+ nsLineData* newLine = new nsLineData();
+ if (nsnull != newLine) {
+ nsIFrame* kid = aOverflowList;
+ newLine->mFirstChild = kid;
+ kid->GetIndexInParent(newLine->mFirstContentOffset);
+ newLine->mLastContentOffset = -1;
+ newLine->mLastContentIsComplete = PRPackedBool(0x255);
+ PRInt32 kids = 0;
+ while (nsnull != kid) {
+ kids++;
+ kid->GetNextSibling(kid);
+ }
+ newLine->mChildCount = kids;
+ }
+ return newLine;
+}
+
+void
+nsBlockFrame::DrainOverflowList()
+{
+ nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow;
+ if (nsnull != prevBlock) {
+ nsIFrame* overflowList = prevBlock->mOverflowList;
+ if (nsnull != overflowList) {
+ NS_ASSERTION(nsnull == mFirstChild, "bad overflow list");
+ NS_ASSERTION(nsnull == mLines, "bad overflow list");
+
+ // Create a line to hold the entire overflow list
+ nsLineData* newLine = CreateLineForOverflowList(overflowList);
+
+ // Place the children on our child list; this also reassigns
+ // their geometric parent and updates our mChildCount.
+ AppendChildren(overflowList);
+ prevBlock->mOverflowList = nsnull;
+
+ // The new line is the first line
+ mLines = newLine;
+ }
+ }
+
+ if (nsnull != mOverflowList) {
+ NS_ASSERTION(nsnull != mFirstChild,
+ "overflow list but no mapped children");
+
+ // Create a line to hold the overflow list
+ nsLineData* newLine = CreateLineForOverflowList(mOverflowList);
+
+ // Place the children on our child list; this also reassigns
+ // their geometric parent and updates our mChildCount.
+ AppendChildren(mOverflowList, PR_FALSE);
+ mOverflowList = nsnull;
+
+ // The new line is appended after our other lines
+ nsLineData* prevLine = nsnull;
+ nsLineData* line = mLines;
+ while (nsnull != line) {
+ prevLine = line;
+ line = line->mNextLine;
+ }
+ if (nsnull == prevLine) {
+ mLines = newLine;
+ }
+ else {
+ prevLine->mNextLine = newLine;
+ newLine->mPrevLine = prevLine;
+ }
+ }
+#ifdef NS_DEBUG
+ VerifyLines(PR_FALSE);
+#endif
+}
+
+// XXX add in code here that notices if margin's were not provided by
+// the style system and when that is the case to apply the old layout
+// engines margin calculations.
+
+nsresult
+nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
+ nsLineLayout& aLineLayout,
+ nsLineData* aLine)
+{
+ nsresult rv = NS_LINE_LAYOUT_COMPLETE;
+
+ nscoord topMargin = 0;
+ nscoord bottomMargin = 0;
+ nscoord maxNegBottomMargin = 0;
+ nscoord maxPosBottomMargin = 0;
+
+ // See if block margins apply to this line or not
+ PRBool isBlockLine = PR_FALSE;
+ if (1 == aLine->mChildCount) {
+ nsIStyleContextPtr kidSC;
+ nsIFrame* kid = aLine->mFirstChild;
+ rv = kid->GetStyleContext(aState.mPresContext, kidSC.AssignRef());
+ if (NS_OK != rv) return rv;
+
+ nsStyleDisplay* display = (nsStyleDisplay*)
+ kidSC->GetData(kStyleDisplaySID);
+ switch (display->mDisplay) {
+ case NS_STYLE_DISPLAY_BLOCK:
+ case NS_STYLE_DISPLAY_LIST_ITEM:
+ isBlockLine = PR_TRUE;
+ nsStyleSpacing* spacing = (nsStyleSpacing*)
+ kidSC->GetData(kStyleSpacingSID);
+
+ // Calculate top margin by collapsing with previous bottom margin
+ // if any.
nscoord maxNegTopMargin = 0;
nscoord maxPosTopMargin = 0;
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- if (ss->mMargin.top < 0) {
- maxNegTopMargin = -ss->mMargin.top;
+ if (spacing->mMargin.top < 0) {
+ maxNegTopMargin = -spacing->mMargin.top;
} else {
- maxPosTopMargin = ss->mMargin.top;
+ maxPosTopMargin = spacing->mMargin.top;
}
-
- nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin);
- nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin);
- return maxPos - maxNeg;
- } else {
- return 0;
+ nscoord maxPos = PR_MAX(aState.mPrevMaxPosBottomMargin, maxPosTopMargin);
+ nscoord maxNeg = PR_MAX(aState.mPrevMaxNegBottomMargin, maxNegTopMargin);
+ topMargin = maxPos - maxNeg;
+
+ // Save away bottom information for later promotion into aState
+ if (spacing->mMargin.bottom < 0) {
+ maxNegBottomMargin = -spacing->mMargin.bottom;
+ } else {
+ maxPosBottomMargin = spacing->mMargin.bottom;
+ }
+ break;
}
}
+
+ // Before we move the line, make sure that it will fit in it's new
+ // location. It always fits if the height isn't constrained or it's
+ // the first line.
+ nscoord totalHeight = topMargin + aLine->mBounds.height;
+ if (!aState.mUnconstrainedHeight && (aLine != mLines)) {
+ if (aState.mY + totalHeight > aState.mAvailSize.height) {
+ // The line will not fit
+ rv = PushLines(aState, aLine);
+ goto done;
+ }
+ }
+ if (isBlockLine) {
+ if (0 != topMargin) {
+ // We have to move the line now that we know the top margin
+ // for it.
+ aLine->MoveLineBy(0, topMargin);
+ aState.mY += topMargin;
+ }
+ }
+ else {
+ // Apply previous line's bottom margin before the inline-line.
+ nscoord bottomMargin = aState.mPrevMaxPosBottomMargin -
+ aState.mPrevMaxNegBottomMargin;
+ if (0 != bottomMargin) {
+ aLine->MoveLineBy(0, bottomMargin);
+ aState.mY += topMargin;
+ }
+ }
+
+ // Consume space and advance running values
+ aState.mY += aLine->mBounds.height;
+ aState.mPrevMaxNegBottomMargin = maxNegBottomMargin;
+ aState.mPrevMaxPosBottomMargin = maxPosBottomMargin;
+ if (nsnull != aState.mMaxElementSizePointer) {
+ nsSize* maxSize = aState.mMaxElementSizePointer;
+ if (aLineLayout.mReflowData.mMaxElementSize.width > maxSize->width) {
+ maxSize->width = aLineLayout.mReflowData.mMaxElementSize.width;
+ }
+ if (aLineLayout.mReflowData.mMaxElementSize.height > maxSize->height) {
+ maxSize->height = aLineLayout.mReflowData.mMaxElementSize.height;
+ }
+ }
+ {
+ nscoord xmost = aLine->mBounds.XMost();
+ if (xmost > aState.mKidXMost) {
+ aState.mKidXMost = xmost;
+ }
+ }
+
+ // Any below current line floaters to place?
+ if (aState.mPendingFloaters.Count() > 0) {
+ PlaceBelowCurrentLineFloaters(aState, aState.mY);
+ // XXX Factor in the height of the floaters as well when considering
+ // whether the line fits.
+ // The default policy is that if there isn't room for the floaters then
+ // both the line and the floaters are pushed to the next-in-flow...
+ }
+
+ if (aState.mY >= aState.mCurrentBand.availSpace.YMost()) {
+ // The current y coordinate is now past our available space
+ // rectangle. Get a new band of space.
+ GetAvailableSpace(aState, aState.mY);
+ }
+
+done:
+ return rv;
}
-void nsBlockFrame::PlaceBelowCurrentLineFloaters(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nscoord aY)
+// aY has borderpadding.top already factored in
+// aResult is relative to left,aY
+nsresult
+nsBlockFrame::GetAvailableSpace(nsBlockReflowState& aState, nscoord aY)
{
- NS_PRECONDITION(aState.floaterToDo.Count() > 0, "no floaters");
+ nsresult rv = NS_OK;
- // XXX Factor this code with PlaceFloater()...
- PRInt32 numFloaters = aState.floaterToDo.Count();
-
- for (PRInt32 i = 0; i < numFloaters; i++) {
- nsIFrame* floater = (nsIFrame*)aState.floaterToDo[i];
- nsRect region;
+ nsISpaceManager* sm = aState.mSpaceManager;
- // Get the band of available space
- // XXX This is inefficient to do this inside the loop...
- GetAvailableSpaceBand(aState, aY);
+ // Fill in band data for the specific Y coordinate
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->GetBandData(aY, aState.mAvailSize, aState.mCurrentBand);
+ sm->Translate(-aState.mBorderPadding.left, 0);
- // Get the type of floater
- nsIStyleContextPtr styleContext;
-
- floater->GetStyleContext(aCX, styleContext.AssignRef());
- nsStyleDisplay* sd = (nsStyleDisplay*)
- styleContext->GetData(kStyleDisplaySID);
-
- floater->GetRect(region);
- region.y = mCurrentState->currentBand->availSpace.y;
+ // Compute the bounding rect of the available space, i.e. space
+ // between any left and right floaters
+ aState.mCurrentBand.ComputeAvailSpaceRect();
- if (NS_STYLE_FLOAT_LEFT == sd->mFloats) {
- region.x = mCurrentState->currentBand->availSpace.x;
- } else {
- NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type");
- region.x = mCurrentState->currentBand->availSpace.XMost() - region.width;
- }
-
- // XXX Don't forget the floater's margins...
- mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left,
- 0);
- mCurrentState->spaceManager->AddRectRegion(region, floater);
-
- // Set the origin of the floater in world coordinates
- nscoord worldX, worldY;
-
- mCurrentState->spaceManager->GetTranslation(worldX, worldY);
- floater->MoveTo(region.x + worldX, region.y + worldY);
- mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0);
- }
- aState.floaterToDo.Clear();
-}
-
-/**
- * Flush a line out. Return true if the line fits in our available
- * height. If the line does not fit then return false. When the line
- * fits we advance the y coordinate, reset the x coordinate and
- * prepare the nsBlockReflowState for the next line.
- */
-PRBool nsBlockFrame::AdvanceToNextLine(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
- NS_PRECONDITION(aState.lineLength > 0, "bad line");
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
-
- nscoord y = aState.y + aState.topMargin;
- nscoord lineHeight;
-
- if (aState.isInline) {
- // Vertically align the children on this line, returning the height of
- // the line upon completion.
- lineHeight = nsCSSLayout::VerticallyAlignChildren(aCX, this,
- aState.styleFont,
- y,
- aState.lineStart,
- aState.lineLength,
- aState.ascents,
- aState.maxAscent);
-
- // Any below current line floaters to place?
- if (aState.floaterToDo.Count() > 0) {
- PlaceBelowCurrentLineFloaters(aCX, aState, y + lineHeight);
- // XXX Factor in the height of the floaters as well when considering
- // whether the line fits.
- // The default policy is that if there isn't room for the floaters then
- // both the line and the floaters are pushed to the next-in-flow...
- }
- } else {
- nsSize size;
-
- aState.lineStart->GetSize(size);
- lineHeight = size.height;
- }
-
- // The first line always fits
- if (aState.currentLineNumber > 0) {
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- if (y + lineHeight > yb) {
- // After vertical alignment of the children and factoring in the
- // proper margin, the line doesn't fit.
- return PR_FALSE;
- }
- }
-
- if (aState.isInline) {
- // Check if the right-edge of the line exceeds our running x-most
- nscoord xMost = aState.borderPadding.left + aState.lineWidth;
- if (xMost > aState.kidXMost) {
- aState.kidXMost = xMost;
- }
- }
-
- // Advance the y coordinate to the new position where the next
- // line or block element will go.
- aState.y = y + lineHeight;
- aState.x = 0;
-
- // Now that the vertical alignment is done we can perform horizontal
- // alignment and relative positioning. Skip all of these if we are
- // doing an unconstrained (in x) reflow. There's no point in doing
- // the work if we *know* we are going to reflowed again.
- if (!aState.unconstrainedWidth) {
- nsCSSLayout::HorizontallyPlaceChildren(aCX, this, aState.styleText,
- aState.lineStart, aState.lineLength,
- aState.lineWidth,
- aState.availSize.width);
-
- // Finally, now that the in-flow positions of the line's frames are
- // known we can apply relative positioning if any of them need it.
- if (!aState.justifying) {
- nsCSSLayout::RelativePositionChildren(aCX, this,
- aState.lineStart,
- aState.lineLength);
- }
- }
-
- // Record line length
- aState.lineLengths.AppendElement((void*)aState.lineLength);
-
- // Find the last frame in the line
- // XXX keep this as running state in the nsBlockReflowState
- nsIFrame* lastFrame = aState.lineStart;
- PRInt32 lineLen = aState.lineLength - 1;
- while (--lineLen >= 0) {
- lastFrame->GetNextSibling(lastFrame);
- }
-
- // Update maxElementSize
- if (nsnull != aState.maxElementSize) {
- nsSize& lineMax = aState.lineMaxElementSize;
- nsSize* maxMax = aState.maxElementSize;
- if (lineMax.width > maxMax->width) {
- maxMax->width = lineMax.width;
- }
- if (lineMax.height > maxMax->height) {
- maxMax->height = lineMax.height;
- }
- aState.lineMaxElementSize.width = 0;
- aState.lineMaxElementSize.height = 0;
- }
-
- // Advance to the next line
- aState.AdvanceToNextLine(lastFrame, lineHeight);
-
- return PR_TRUE;
-}
-
-/**
- * Add an inline child to the current line. Advance various running
- * values after placement.
- */
-void nsBlockFrame::AddInlineChildToLine(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsReflowMetrics& aKidSize,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC)
-{
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
-
- nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID);
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID);
-
- if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) {
- aState.needRelativePos = PR_TRUE;
- }
-
- // Place and size the child
- // XXX add in left margin from kid
- nsRect r;
- r.y = aState.y;
- r.width = aKidSize.width;
- r.height = aKidSize.height;
- if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) {
- // Left to right positioning.
- r.x = aState.borderPadding.left + aState.x + ss->mMargin.left;
- aState.x += aKidSize.width + ss->mMargin.left + ss->mMargin.right;
- } else {
- // Right to left positioning
- // XXX what should we do when aState.x goes negative???
- r.x = aState.x - aState.borderPadding.right - ss->mMargin.right -
- aKidSize.width;
- aState.x -= aKidSize.width + ss->mMargin.right + ss->mMargin.left;
- }
- aKidFrame->SetRect(r);
- aState.AddAscent(aKidSize.ascent);
- aState.lineWidth += aKidSize.width;
- aState.lineLength++;
-
- // Update maximums for the line
- if (aKidSize.ascent > aState.maxAscent) {
- aState.maxAscent = aKidSize.ascent;
- }
- if (aKidSize.descent > aState.maxDescent) {
- aState.maxDescent = aKidSize.descent;
- }
- // Update running margin maximums
- if (aState.firstChildIsInsideBullet && (aKidFrame == mFirstChild)) {
- // XXX temporary code. Since the molecule for the bullet frame
- // is the same as the LI frame, we get bad style information.
- // ignore it.
- } else {
- nscoord margin;
#if 0
- // XXX CSS2 spec says that top/bottom margin don't affect line height
- // calculation. We're waiting for clarification on this issue...
- if ((margin = ss->mMargin.top) < 0) {
- margin = -margin;
- if (margin > aState.maxNegTopMargin) {
- aState.maxNegTopMargin = margin;
- }
- } else {
- if (margin > aState.maxPosTopMargin) {
- aState.maxPosTopMargin = margin;
- }
- }
+ // XXX For now we assume that there are no height restrictions
+ // (e.g. no "float to bottom of column/page")
+ nsRect& availSpace = aState.mCurrentBand.availSpace;
+ aResult.x = availSpace.x;
+ aResult.y = availSpace.y;
+ aResult.width = availSpace.width;
+ aResult.height = aState.mAvailSize.height;
+#else
+ aState.mCurrentBand.availSpace.MoveBy(aState.mBorderPadding.left,
+ aState.mY);
#endif
- if ((margin = ss->mMargin.bottom) < 0) {
- margin = -margin;
- if (margin > aState.maxNegBottomMargin) {
- aState.maxNegBottomMargin = margin;
- }
- } else {
- if (margin > aState.maxPosBottomMargin) {
- aState.maxPosBottomMargin = margin;
- }
- }
- }
- // Update line max element size
- nsSize& mes = aState.lineMaxElementSize;
- if (nsnull != aKidMaxElementSize) {
- if (aKidMaxElementSize->width > mes.width) {
- mes.width = aKidMaxElementSize->width;
- }
- if (aKidMaxElementSize->height > mes.height) {
- mes.height = aKidMaxElementSize->height;
- }
- }
+ return rv;
}
-// Places and sizes the block-level element, and advances the line.
-// The rect is in the local coordinate space of the kid frame.
-void nsBlockFrame::AddBlockChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsRect& aKidRect,
- nsSize* aKidMaxElementSize,
- nsIStyleContext* aKidSC)
+// Give aLine and any successive lines to the block's next-in-flow; if
+// we don't have a next-in-flow then push all the children onto our
+// overflow list.
+nsresult
+nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineData* aLine)
{
- NS_PRECONDITION(nsnull != aState.lineStart, "bad line");
+ PRInt32 i;
- nsStyleDisplay* ds = (nsStyleDisplay*) aKidSC->GetData(kStyleDisplaySID);
- nsStyleSpacing* ss = (nsStyleSpacing*) aKidSC->GetData(kStyleSpacingSID);
- nsStylePosition* sp = (nsStylePosition*) aKidSC->GetData(kStylePositionSID);
-
- if (NS_STYLE_POSITION_RELATIVE == sp->mPosition) {
- aState.needRelativePos = PR_TRUE;
+ // Split our child-list in two; revert our last content offset and
+ // completion status to the previous line.
+ nsLineData* prevLine = aLine->mPrevLine;
+ NS_PRECONDITION(nsnull != prevLine, "pushing first line");
+ nsIFrame* prevKidFrame = prevLine->mFirstChild;
+ for (i = prevLine->mChildCount - 1; --i >= 0; ) {
+ prevKidFrame->GetNextSibling(prevKidFrame);
}
-
- // Translate from the kid's coordinate space to our coordinate space
- aKidRect.x += aState.borderPadding.left + ss->mMargin.left;
- aKidRect.y += aState.y + aState.topMargin;
-
- // Place and size the child
- aKidFrame->SetRect(aKidRect);
-
- aState.AddAscent(aKidRect.height);
- aState.lineLength++;
-
- // Is this the widest child frame?
- nscoord xMost = aKidRect.XMost() + ss->mMargin.right;
- if (xMost > aState.kidXMost) {
- aState.kidXMost = xMost;
- }
-
- // Update the max element size
- if (nsnull != aKidMaxElementSize) {
- if (aKidMaxElementSize->width > aState.maxElementSize->width) {
- aState.maxElementSize->width = aKidMaxElementSize->width;
- }
- if (aKidMaxElementSize->height > aState.maxElementSize->height) {
- aState.maxElementSize->height = aKidMaxElementSize->height;
- }
- }
-
- // and the bottom line margin information which we'll use when placing
- // the next child
- if (ss->mMargin.bottom < 0) {
- aState.maxNegBottomMargin = -ss->mMargin.bottom;
- } else {
- aState.maxPosBottomMargin = ss->mMargin.bottom;
- }
-
- // Update the running y-offset
- aState.y += aKidRect.height + aState.topMargin;
-
- // Apply relative positioning if necessary
- nsCSSLayout::RelativePositionChildren(aCX, this, aKidFrame, 1);
-
- // Advance to the next line
- aState.AdvanceToNextLine(aKidFrame, aKidRect.height);
-}
-
-/**
- * Compute the available size for reflowing the given child at the
- * current x,y position in the state. Note that this may return
- * negative or zero width/height's if we are out of room.
- */
-void nsBlockFrame::GetAvailSize(nsSize& aResult,
- nsBlockReflowState& aState,
- nsIStyleContext* aKidSC,
- PRBool aIsInline)
-{
- // Determine the maximum available reflow height for the child
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- aResult.height = aState.unconstrainedHeight ? NS_UNCONSTRAINEDSIZE :
- yb - aState.y - aState.topMargin;
-
- // Determine the maximum available reflow width for the child
- if (aState.unconstrainedWidth) {
- aResult.width = NS_UNCONSTRAINEDSIZE;
- } else if (aIsInline) {
- if (NS_STYLE_DIRECTION_LTR == aState.styleDisplay->mDirection) {
- aResult.width = aState.currentBand->availSpace.XMost() - aState.x;
- } else {
- aResult.width = aState.x - aState.currentBand->availSpace.x;
- }
- } else {
- // It's a block. Don't adjust for the left/right margin here. That happens
- // later on once we know the current left/right edge
- aResult.width = aState.availSize.width;
- }
-}
-
-/**
- * Push all of the kids that we have not reflowed, starting at
- * aState.lineStart. aPrevKid is the kid previous to aState.lineStart
- * and is also our last child. Note that line length is NOT a
- * reflection of the number of children we are actually pushing
- * (because we don't break the sibling list as we add children to the
- * line).
- */
-void nsBlockFrame::PushKids(nsBlockReflowState& aState)
-{
- nsIFrame* prevFrame = aState.prevLineLastFrame;
- NS_PRECONDITION(nsnull != prevFrame, "pushing all kids");
#ifdef NS_DEBUG
- nsIFrame* nextSibling;
-
- prevFrame->GetNextSibling(nextSibling);
- NS_PRECONDITION(nextSibling == aState.lineStart, "bad prev line");
+ nsIFrame* nextFrame;
+ prevKidFrame->GetNextSibling(nextFrame);
+ NS_ASSERTION(nextFrame == aLine->mFirstChild, "bad line list");
#endif
+ prevKidFrame->SetNextSibling(nsnull);
+ prevLine->mNextLine = nsnull;
+ mLastContentOffset = prevLine->mLastContentOffset;
+ mLastContentIsComplete = prevLine->mLastContentIsComplete;
+
+ // Push children to our next-in-flow if we have, or to our overflow list
+ nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
+ PRInt32 pushCount = 0;
+ if (nsnull == nextInFlow) {
+ // Place children on the overflow list
+ mOverflowList = aLine->mFirstChild;
+
+ // Destroy the lines
+ nsLineData* line = aLine;
+ while (nsnull != line) {
+ pushCount += line->mChildCount;
+ nsLineData* next = line->mNextLine;
+ delete line;
+ line = next;
+ }
+ }
+ else {
+ aLine->mPrevLine = nsnull;
+
+ // Pass on the children to our next-in-flow
+ nsLineData* line = aLine;
+ prevLine = line;
+ nsIFrame* lastKid = aLine->mFirstChild;
+ nsIFrame* kid = lastKid;
+ while (nsnull != line) {
+ i = line->mChildCount;
+ pushCount += i;
+ NS_ASSERTION(kid == line->mFirstChild, "bad line list");
+ while (--i >= 0) {
+ kid->SetGeometricParent(nextInFlow);
+ nsIFrame* contentParent;
+ kid->GetContentParent(contentParent);
+ if (this == contentParent) {
+ kid->SetContentParent(nextInFlow);
+ }
+ lastKid = kid;
+ kid->GetNextSibling(kid);
+ }
+ prevLine = line;
+ line = line->mNextLine;
+ }
+
+ // Join the two line lists
+ nsLineData* nextInFlowLine = nextInFlow->mLines;
+ if (nsnull != nextInFlowLine) {
+ lastKid->SetNextSibling(nextInFlowLine->mFirstChild);
+ nextInFlowLine->mPrevLine = prevLine;
+ }
+ nsIFrame* firstKid = aLine->mFirstChild;
+ prevLine->mNextLine = nextInFlowLine;
+ nextInFlow->mLines = aLine;
+ nextInFlow->mFirstChild = firstKid;
+ nextInFlow->mChildCount += pushCount;
+ firstKid->GetIndexInParent(nextInFlow->mFirstContentOffset);
#ifdef NS_DEBUG
- PRInt32 numKids = LengthOf(mFirstChild);
- NS_ASSERTION(numKids == mChildCount, "bad child count");
+ nextInFlow->VerifyLines(PR_FALSE);
+#endif
+ }
+ mChildCount -= pushCount;
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return NS_LINE_LAYOUT_NOT_COMPLETE;
+}
+
+nsresult
+nsBlockFrame::ReflowMapped(nsBlockReflowState& aState)
+{
+ nsresult rv = NS_OK;
+
+ // Get some space to start reflowing with
+ GetAvailableSpace(aState, aState.mY);
+
+ nsLineData* prevLine = nsnull;
+ nsLineData* line = mLines;
+ nsLineLayout lineLayout(aState);
+ aState.mCurrentLine = &lineLayout;
+ while (nsnull != line) {
+ // Initialize the line layout for this line
+ rv = lineLayout.Initialize(aState, line);
+ if (NS_OK != rv) {
+ goto done;
+ }
+ lineLayout.mPrevKidFrame = aState.mPrevKidFrame;
+
+ // Reflow the line
+ nsresult lineReflowStatus = lineLayout.ReflowLine();
+ if (lineReflowStatus < 0) {
+ // Some kind of hard error
+ rv = lineReflowStatus;
+ goto done;
+ }
+ mChildCount += lineLayout.mNewFrames;
+
+ // Now place it. It's possible it won't fit.
+ rv = PlaceLine(aState, lineLayout, line);
+ if (NS_LINE_LAYOUT_COMPLETE != rv) {
+ goto done;
+ }
+
+ mLastContentOffset = line->mLastContentOffset;
+ mLastContentIsComplete = PRBool(line->mLastContentIsComplete);
+ prevLine = line;
+ line = line->mNextLine;
+ aState.mPrevKidFrame = lineLayout.mPrevKidFrame;
+ }
+
+done:
+ aState.mCurrentLine = nsnull;
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return rv;
+}
+
+nsresult
+nsBlockFrame::ReflowUnmapped(nsBlockReflowState& aState)
+{
+ nsresult rv = NS_OK;
+
+ // If we have no children and we have a prev-in-flow then we need to
+ // pick up where it left off. If we have children, e.g. we're being
+ // resized, then our content offset will have already been set
+ // correctly.
+ nsIFrame* kidPrevInFlow = nsnull;
+ if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
+ nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow;
+ mFirstContentOffset = prev->NextChildOffset();// XXX Is this necessary?
+ if (PR_FALSE == prev->mLastContentIsComplete) {
+ // Our prev-in-flow's last child is not complete
+ prev->LastChild(kidPrevInFlow);
+ }
+ }
+
+ // Get to the last line where the new content may be added
+ nsLineData* line = nsnull;
+ nsLineData* prevLine = nsnull;
+ if (nsnull != mLines) {
+ line = mLines;
+ while (nsnull != line->mNextLine) {
+ line = line->mNextLine;
+ }
+ prevLine = line;
+
+ // If the last line is not complete then kidPrevInFlow should be
+ // set to the last-line's last child.
+ if (!prevLine->mLastContentIsComplete) {
+ kidPrevInFlow = prevLine->GetLastChild();
+ }
+ }
+
+ // Get some space to start reflowing with
+ GetAvailableSpace(aState, aState.mY);
+
+ // Now reflow the new content until we are out of new content or out
+ // of vertical space.
+ PRInt32 kidIndex = NextChildOffset();
+ nsLineLayout lineLayout(aState);
+ aState.mCurrentLine = &lineLayout;
+ lineLayout.mKidPrevInFlow = kidPrevInFlow;
+ PRInt32 contentChildCount = mContent->ChildCount();
+ while (kidIndex < contentChildCount) {
+ if (nsnull == line) {
+ if (!MoreToReflow(aState)) {
+ break;
+ }
+ line = new nsLineData();
+ if (nsnull == line) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto done;
+ }
+ line->mFirstContentOffset = kidIndex;
+ }
+
+ // Initialize the line layout for this line
+ rv = lineLayout.Initialize(aState, line);
+ if (NS_OK != rv) {
+ goto done;
+ }
+ lineLayout.mKidPrevInFlow = kidPrevInFlow;
+ lineLayout.mPrevKidFrame = aState.mPrevKidFrame;
+
+ // Reflow the line
+ nsresult lineReflowStatus = lineLayout.ReflowLine();
+ if (lineReflowStatus < 0) {
+ // Some kind of hard error
+ rv = lineReflowStatus;
+ goto done;
+ }
+ mChildCount += lineLayout.mNewFrames;
+
+ // Add line to the block; do this before placing the line in case
+ // PushLines is needed.
+ if (nsnull == prevLine) {
+ // For the first line, initialize mFirstContentOffset
+ mFirstContentOffset = line->mFirstContentOffset;
+ mFirstChild = line->mFirstChild;
+ mLines = line;
+ }
+ else {
+ prevLine->mNextLine = line;
+ line->mPrevLine = prevLine;
+ }
+
+ // Now place it. It's possible it won't fit.
+ rv = PlaceLine(aState, lineLayout, line);
+ if (NS_LINE_LAYOUT_COMPLETE != rv) {
+ goto done;
+ }
+ kidIndex = lineLayout.mKidIndex;
+ kidPrevInFlow = lineLayout.mKidPrevInFlow;
+
+ mLastContentOffset = line->mLastContentOffset;
+ mLastContentIsComplete = PRBool(line->mLastContentIsComplete);
+ prevLine = line;
+ line = line->mNextLine;
+ aState.mPrevKidFrame = lineLayout.mPrevKidFrame;
+ }
+
+done:
+ aState.mCurrentLine = nsnull;
+ if (aState.mBlockIsPseudo) {
+ PropagateContentOffsets();
+ }
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+#endif
+ return rv;
+}
+
+nsresult
+nsBlockFrame::InitializeState(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ nsBlockReflowState& aState)
+{
+ nsresult rv;
+ rv = aState.Initialize(aPresContext, aSpaceManager,
+ aMaxSize, aMaxElementSize, this);
+
+ nsStyleSpacing* mySpacing = (nsStyleSpacing*)
+ mStyleContext->GetData(kStyleSpacingSID);
+
+ // Apply border and padding adjustments for regular frames only
+ if (!aState.mBlockIsPseudo) {
+ aState.mY = mySpacing->mBorderPadding.top;
+ aState.mX = mySpacing->mBorderPadding.left;
+ aState.mAvailSize.width -=
+ (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right);
+ aState.mAvailSize.height -=
+ (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom);
+ aState.mBorderPadding = mySpacing->mBorderPadding;
+ }
+ else {
+ aState.mBorderPadding.SizeTo(0, 0, 0, 0);
+ }
+
+ // Setup initial list ordinal value
+ nsIAtom* tag = mContent->GetTag();
+ if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
+ (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
+ nsHTMLValue value;
+ if (eContentAttr_HasValue ==
+ ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) {
+ if (eHTMLUnit_Integer == value.GetUnit()) {
+ aState.mNextListOrdinal = value.GetIntValue();
+ }
+ }
+ }
+ NS_RELEASE(tag);
+
+ return rv;
+}
+
+#if XXX
+NS_METHOD
+nsBlockFrame::SizeTo(nscoord aWidth, nscoord aHeight)
+{
+ printf("size to %g,%g\n", NS_TWIPS_TO_POINTS_FLOAT(aWidth),
+ NS_TWIPS_TO_POINTS_FLOAT(aHeight));
+ return nsHTMLContainerFrame::SizeTo(aWidth, aHeight);
+}
+
+NS_METHOD
+nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsSize& aMaxSize,
+ nsSize* aMaxElementSize,
+ ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ aStatus = frComplete;
+ nsBlockReflowState state;
+ rv = InitializeState(aPresContext, aMaxSize, aMaxElementSize, state);
+ if (NS_OK == rv) {
+ nsRect desiredRect;
+ rv = DoResizeReflow(state, desiredRect, aStatus);
+ aDesiredSize.width = desiredRect.width;
+ aDesiredSize.height = desiredRect.height;
+ aDesiredSize.ascent = aDesiredSize.height;
+ aDesiredSize.descent = 0;
+ }
+ return rv;
+}
#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": push kids (childCount=%d)\n", mChildCount);
- DumpFlow();
-#endif
-
- PushChildren(aState.lineStart, prevFrame, mLastContentIsComplete);
- SetLastContentOffset(prevFrame);
-
- // Set mLastContentIsComplete to the previous lines last content is
- // complete now that the previous line's last child is our last
- // child.
- mLastContentIsComplete = aState.prevLineLastContentIsComplete;
-
- // Fix up child count
- // XXX is there a better way? aState.lineLength doesn't work because
- // we might be pushing more than just the pending line.
- nsIFrame* kid = mFirstChild;
- PRInt32 kids = 0;
- while (nsnull != kid) {
- kids++;
- kid->GetNextSibling(kid);
- }
- mChildCount = kids;
-
- // Make sure we have no lingering line data
- aState.lineLength = 0;
- aState.lineStart = nsnull;
-
-#ifdef NOISY
- ListTag(stdout);
- printf(": push kids done (childCount=%d) [%c]\n", mChildCount,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-}
-
-/**
- * Gets a band of available space starting at the specified y-offset. Assumes
- * the local coordinate space is currently set to the upper-left origin of the
- * bounding rect
- *
- * Updates "currentBand" and "x" member data of the block reflow state
- */
-void nsBlockFrame::GetAvailableSpaceBand(nsBlockReflowState& aState, nscoord aY)
-{
- // Gets a band of available space.
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
- aState.spaceManager->GetBandData(aY, aState.availSize, *aState.currentBand);
-
- // Compute the bounding rect of the available space, i.e. space between any
- // left and right floaters
- aState.currentBand->ComputeAvailSpaceRect();
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
- aState.x = aState.currentBand->availSpace.x;
-}
-
-void nsBlockFrame::ClearFloaters(nsBlockReflowState& aState, PRUint32 aClear)
-{
- // Translate the coordinate space
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
-
-getBand:
- nscoord y = aState.y + aState.topMargin;
- PRBool isLeftFloater = PR_FALSE;
- PRBool isRightFloater = PR_FALSE;
-
- // Get a band of available space
- aState.spaceManager->GetBandData(y, aState.availSize, *aState.currentBand);
-
- // Scan the trapezoids looking for left and right floaters
- nsBandTrapezoid* trapezoid = aState.currentBand->trapezoids;
- for (PRInt32 i = 0; i < aState.currentBand->count; i++) {
- // XXX Handle multiply occupied
- if (nsBandTrapezoid::smOccupied == trapezoid->state) {
- nsStyleDisplay* display;
-
- trapezoid->frame->GetStyleData(kStylePositionSID, (nsStyleStruct*&)display);
- if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
- isLeftFloater = PR_TRUE;
- } else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) {
- isRightFloater = PR_TRUE;
- }
- }
-
- trapezoid++;
- }
-
- if (isLeftFloater) {
- if ((aClear == NS_STYLE_CLEAR_LEFT) ||
- (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
- aState.y += aState.currentBand->trapezoids[0].GetHeight();
- goto getBand;
- }
- }
- if (isRightFloater) {
- if ((aClear == NS_STYLE_CLEAR_RIGHT) ||
- (aClear == NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
- aState.y += aState.currentBand->trapezoids[0].GetHeight();
- goto getBand;
- }
- }
-
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
-}
-
-// Bit's for PlaceAndReflowChild return value
-#define PLACE_FIT 0x1
-#define PLACE_FLOWED 0x2
-
-PRIntn
-nsBlockFrame::PlaceAndReflowChild(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsIFrame* aKidFrame,
- nsIStyleContext* aKidSC)
-{
- nsSize kidMaxElementSize;
- nsSize* pKidMaxElementSize =
- (nsnull != aState.maxElementSize) ? &kidMaxElementSize : nsnull;
-
- // Get line start setup if we are at the start of a new line
- if (nsnull == aState.lineStart) {
- NS_ASSERTION(0 == aState.lineLength, "bad line length");
- aState.lineStart = aKidFrame;
- }
-
- // Get kid and its style
- nsStyleDisplay* styleDisplay = (nsStyleDisplay*)
- aKidSC->GetData(kStyleDisplaySID);
-
- // Figure out if kid is a block element or not
- PRBool isInline = PR_TRUE;
- PRIntn display = styleDisplay->mDisplay;
- if (aState.firstChildIsInsideBullet && (mFirstChild == aKidFrame)) {
- // XXX Special hack for properly reflowing bullets that have the
- // inside value for list-style-position.
- display = NS_STYLE_DISPLAY_INLINE;
- }
- if ((NS_STYLE_DISPLAY_BLOCK == display) ||
- (NS_STYLE_DISPLAY_LIST_ITEM == display)) {
- // Block elements always end up on the next line (unless they are
- // already at the start of the line).
- isInline = PR_FALSE;
- if (aState.lineLength > 0) {
- aState.breakAfterChild = PR_TRUE;
- }
- }
-
- // Handle forced break first
- if (aState.breakAfterChild) {
- NS_ASSERTION(aState.lineStart != aKidFrame, "bad line");
-
- // Get the last child in the current line
- nsIFrame* lastFrame = aState.lineStart;
- PRInt32 lineLen = aState.lineLength - 1;
- while (--lineLen >= 0) {
- lastFrame->GetNextSibling(lastFrame);
- }
-
- if (!AdvanceToNextLine(aCX, aState)) {
- // The previous line didn't fit.
- return 0;
- }
- aState.lineStart = aKidFrame;
-
- // Get the style for the last child, and see if it wanted to clear
- // floaters. This handles the BR tag, which is the only inline
- // element for which clear applies
- nsIStyleContextPtr lastChildSC;
-
- lastFrame->GetStyleContext(aCX, lastChildSC.AssignRef());
- nsStyleDisplay* lastChildDisplay = (nsStyleDisplay*)
- lastChildSC->GetData(kStyleDisplaySID);
- switch (lastChildDisplay->mBreakType) {
- case NS_STYLE_CLEAR_LEFT:
- case NS_STYLE_CLEAR_RIGHT:
- case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
- ClearFloaters(aState, lastChildDisplay->mBreakType);
- break;
- }
- }
-
- // Now that we've handled force breaks (and maybe called AdvanceToNextLine()
- // which checks), remember whether it's an inline frame
- aState.isInline = isInline;
-
- // If we're at the beginning of a line then compute the top margin that we
- // should use
- if (aState.lineStart == aKidFrame) {
- // Compute the top margin to use for this line
- aState.topMargin = GetTopMarginFor(aCX, aState, aKidFrame, aKidSC,
- aState.isInline);
-
- // If it's an inline element then get a band of available space
- //
- // XXX If we have a current band and there's unused space in that band
- // then avoid this call to get a band...
- if (aState.isInline) {
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
- }
- }
-
- // Compute the available space to reflow the child into and then
- // reflow it into that space.
- nsSize kidAvailSize;
- GetAvailSize(kidAvailSize, aState, aKidSC, aState.isInline);
- if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) {
- // No more room
- return 0;
- }
-
- ReflowStatus status;
-
- if (aState.isInline) {
- nsReflowMetrics kidSize;
-
- // Inline elements are never passed the space manager
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
-
- // For first children, we skip all the fit checks because we must
- // fit at least one child for a parent to figure what to do with us.
- if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) {
- NS_ASSERTION(nsnull != aState.lineStart, "bad line start");
-
- if (aKidFrame == aState.lineStart) {
- // Width always fits when we are at the logical left margin.
- // Just check the height.
- //
- // XXX This height check isn't correct now that we have bands of
- // available space...
- if (kidSize.height > kidAvailSize.height) {
- // It's too tall
- return PLACE_FLOWED;
- }
- } else {
- // Examine state and if the breakBeforeChild is set and we
- // aren't already on the new line, do the forcing now.
- // XXX Why aren't we doing this check BEFORE we resize reflow the child?
- if (aState.breakBeforeChild) {
- aState.breakBeforeChild = PR_FALSE;
- if (aKidFrame != aState.lineStart) {
- if (!AdvanceToNextLine(aCX, aState)) {
- // Flushing out the line failed.
- return PLACE_FLOWED;
- }
- aState.lineStart = aKidFrame;
-
- // Get a band of available space
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
-
- // Reflow child now that it has the line to itself
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE);
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
- }
- }
-
- // When we are not at the logical left margin then we need
- // to check the width first. If we are too wide then advance
- // to the next line and try reflowing again.
- if (kidSize.width > kidAvailSize.width) {
- // Too wide. Try next line
- if (!AdvanceToNextLine(aCX, aState)) {
- // Flushing out the line failed.
- return PLACE_FLOWED;
- }
- aState.lineStart = aKidFrame;
-
- // Get a band of available space
- GetAvailableSpaceBand(aState, aState.y + aState.topMargin);
-
- // Reflow splittable children
- SplittableType isSplittable;
-
- aKidFrame->IsSplittable(isSplittable);
- if (isSplittable != frNotSplittable) {
- // Update size info now that we are on the next line. Then
- // reflow the child into the new available space.
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_TRUE);
- status = ReflowChild(aKidFrame, aCX, kidSize, kidAvailSize,
- pKidMaxElementSize);
-
- // If we just reflowed our last child then update the
- // mLastContentIsComplete state.
- nsIFrame* nextSibling;
-
- aKidFrame->GetNextSibling(nextSibling);
- if (nsnull == nextSibling) {
- // Use state from the reflow we just did
- mLastContentIsComplete = PRBool(status == frComplete);
- }
- }
-
- // XXX This height check isn't correct now that we have bands of
- // available space...
- if (kidSize.height > kidAvailSize.height) {
- // It's too tall on the next line
- return PLACE_FLOWED;
- }
- // It's ok if it's too wide on the next line.
- }
- }
- }
-
- // Add child to the line
- AddInlineChildToLine(aCX, aState, aKidFrame, kidSize,
- pKidMaxElementSize, aKidSC);
- } else {
- nsRect kidRect;
-
- // Does the block-level element want to clear any floaters that impact
- // it? Note that the clear property only applies to block-level elements
- // and the BR tag
- nsStyleDisplay* styleDisplay = (nsStyleDisplay*)
- aKidSC->GetData(kStyleDisplaySID);
- switch (styleDisplay->mBreakType) {
- case NS_STYLE_CLEAR_LEFT:
- case NS_STYLE_CLEAR_RIGHT:
- case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
- ClearFloaters(aState, styleDisplay->mBreakType);
- GetAvailSize(kidAvailSize, aState, aKidSC, PR_FALSE);
- if ((aState.currentLineNumber > 0) && (kidAvailSize.height <= 0)) {
- // No more room
- return 0;
- }
- break;
- }
-
- // Give the block its own local coordinate space.. Note: ReflowChild()
- // will adjust for the child's left/right margin after determining the
- // current left/right edge
- aState.spaceManager->Translate(aState.borderPadding.left, 0);
- // Give the block-level element the opportunity to use the space manager
- status = ReflowChild(aKidFrame, aCX, aState.spaceManager,
- kidAvailSize, kidRect, pKidMaxElementSize);
- aState.spaceManager->Translate(-aState.borderPadding.left, 0);
-
- // For first children, we skip all the fit checks because we must
- // fit at least one child for a parent to figure what to do with us.
- if ((aState.currentLineNumber > 0) || (aState.lineLength > 0)) {
- // Block elements always fit horizontally (because they are
- // always placed at the logical left margin). Check to see if
- // the block fits vertically
- if (kidRect.YMost() > kidAvailSize.height) {
- // Nope
- return PLACE_FLOWED;
- }
- }
-
- // Add block child
- // XXX We need to set lastContentIsComplete here, because AddBlockChild()
- // calls AdvaneceToNextLine(). We need to restructure the flow of control,
- // and use a state machine...
- aState.lastContentIsComplete = PRBool(status == frComplete);
- AddBlockChild(aCX, aState, aKidFrame, kidRect, pKidMaxElementSize, aKidSC);
- }
-
- // If we just reflowed our last child then update the
- // mLastContentIsComplete state.
- nsIFrame* nextSibling;
-
- aKidFrame->GetNextSibling(nextSibling);
- if (nsnull == nextSibling) {
- // Use state from the reflow we just did
- mLastContentIsComplete = PRBool(status == frComplete);
- }
-
- aState.lastContentIsComplete = PRBool(status == frComplete);
- if (aState.isInline && (frNotComplete == status)) {
- // Since the inline child didn't complete its reflow we *know*
- // that a continuation of it can't possibly fit on the current
- // line. Therefore, set a flag in the state that will cause the
- // a line break before the next frame is placed.
- aState.breakAfterChild = PR_TRUE;
- }
-
- aState.reflowStatus = status;
- return PLACE_FLOWED | PLACE_FIT;
-}
-
-/**
- * Reflow the existing frames.
- *
- * @param aCX presentation context to use
- * @param aState in out parameter which tracks the state of
- * reflow for the block frame.
- * @return true if we successfully reflowed all the mapped children and false
- * otherwise, e.g. we pushed children to the next in flow
- */
PRBool
-nsBlockFrame::ReflowMappedChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n",
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-
- PRBool result = PR_TRUE;
- nsIFrame* kidFrame;
-
- for (kidFrame = mFirstChild; nsnull != kidFrame; ) {
-
- /* we get the kid's style from kidFrame's cached style context */
- nsIStyleContextPtr kidSC;
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
-
- // Attempt to place and reflow the child
-
- // XXX if child is not splittable and it fits just place it where
- // it is, otherwise advance to the next line and place it there if
- // possible
-
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- ReflowStatus status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // The child doesn't fit. Push it and any remaining children.
- PushKids(aState);
- result = PR_FALSE;
- goto push_done;
- }
-
- // Is the child complete?
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- if (frComplete == status) {
- // Yes, the child is complete
- NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
- } else {
- // No the child isn't complete
- if (nsnull == kidNextInFlow) {
- // The child doesn't have a next-in-flow so create a continuing
- // frame. This hooks the child into the flow
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
- NS_ASSERTION(nsnull != continuingFrame, "frame creation failed");
-
- // Add the continuing frame to the sibling list
- nsIFrame* nextSib;
-
- kidFrame->GetNextSibling(nextSib);
- continuingFrame->SetNextSibling(nextSib);
- kidFrame->SetNextSibling(continuingFrame);
- mChildCount++;
- }
-
- // Unlike the inline frame code we can't assume that we used
- // up all of our space because the child's reflow status is
- // frNotComplete. Instead, the child is probably split and
- // we need to reflow the continuations as well.
- }
-
- // Get the next child frame
- kidFrame->GetNextSibling(kidFrame);
- }
-
- push_done:
-#ifdef NS_DEBUG
- nsIFrame* lastChild;
- PRInt32 lastIndexInParent;
-
- LastChild(lastChild);
- lastChild->GetIndexInParent(lastIndexInParent);
- NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset");
-
- PRInt32 len = LengthOf(mFirstChild);
- NS_POSTCONDITION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
-
-#ifdef NOISY
- ListTag(stdout);
- printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n",
- (result ? "" : "NOT "),
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
- return result;
-}
-
-/* XXX:
- * In this method, we seem to be getting the style for the next child,
- * and checking that child's style for the child's display type.
- * The problem is that it's very inefficient to ResolveStyleFor(kid) here
- * and then just throw it away, when we end up doing a ResolveStyleFor(kid)
- * again when we actually create a frame for the content.
- * It would be nice to cache the resolved style, maybe in the reflow state?
- */
-PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX)
+nsBlockFrame::MoreToReflow(nsBlockReflowState& aState)
{
PRBool rv = PR_FALSE;
- if (IsPseudoFrame()) {
+ if (aState.mBlockIsPseudo) {
// Get the next content object that we would like to reflow
PRInt32 kidIndex = NextChildOffset();
nsIContentPtr kid = mContent->ChildAt(kidIndex);
if (kid.IsNotNull()) {
// Resolve style for the kid
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
+ nsIStyleContextPtr kidSC =
+ aState.mPresContext->ResolveStyleContextFor(kid, this);
nsStyleDisplay* kidStyleDisplay = (nsStyleDisplay*)
kidSC->GetData(kStyleDisplaySID);
switch (kidStyleDisplay->mDisplay) {
@@ -1305,640 +993,288 @@ PRBool nsBlockFrame::MoreToReflow(nsIPresContext* aCX)
return rv;
}
-/**
- * Create new frames for content we haven't yet mapped
- *
- * @param aCX presentation context to use
- * @return frComplete if all content has been mapped and frNotComplete
- * if we should be continued
- */
-nsIFrame::ReflowStatus
-nsBlockFrame::ReflowAppendedChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
+nsBlockReflowState*
+nsBlockFrame::FindBlockReflowState(nsIPresContext* aPresContext,
+ nsIFrame* aFrame)
{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
- nsIFrame* kidPrevInFlow = nsnull;
- ReflowStatus result = frNotComplete;
-
- // If we have no children and we have a prev-in-flow then we need to pick
- // up where it left off. If we have children, e.g. we're being resized, then
- // our content offset should already be set correctly...
- if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
- nsBlockFrame* prev = (nsBlockFrame*) mPrevInFlow;
- NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow");
- mFirstContentOffset = prev->NextChildOffset();
- if (PR_FALSE == prev->mLastContentIsComplete) {
- // Our prev-in-flow's last child is not complete
- prev->LastChild(kidPrevInFlow);
- }
- }
-
- // Place our children, one at a time until we are out of children
- PRInt32 kidIndex = NextChildOffset();
- nsIFrame* kidFrame = nsnull;
- nsIFrame* prevKidFrame;
-
- LastChild(prevKidFrame);
- for (;;) {
- // Get the next content object
- nsIContentPtr kid = mContent->ChildAt(kidIndex);
- if (kid.IsNull()) {
- result = frComplete;
- break;
- }
-
- // Resolve style for the kid
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
- nsStylePosition* kidPosition = (nsStylePosition*)
- kidSC->GetData(kStylePositionSID);
- nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
- kidSC->GetData(kStyleDisplaySID);
-
- // Check whether it wants to floated or absolutely positioned
- if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) {
- AbsoluteFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- kidFrame->SetStyleContext(aCX,kidSC);
- } else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
- PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- kidFrame->SetStyleContext(aCX,kidSC);
- } else if (nsnull == kidPrevInFlow) {
- // Create initial frame for the child
- nsIContentDelegate* kidDel;
- switch (kidDisplay->mDisplay) {
- case NS_STYLE_DISPLAY_BLOCK:
- case NS_STYLE_DISPLAY_LIST_ITEM:
- // Pseudo block frames do not contain other block elements
- // unless the block element would be the first child.
- if (IsPseudoFrame()) {
- // If we're being used as a pseudo frame, i.e. we map the same
- // content as our parent then we want to indicate we're complete;
- // otherwise we'll be continued and go on mapping children...
-
- // It better be true that we are not being asked to flow a
- // block element as our first child. That means the body
- // decided it needed a pseudo-frame when it shouldn't have.
- NS_ASSERTION(nsnull != mFirstChild, "bad body");
-
- result = frComplete;
- goto done;
- }
- // FALL THROUGH (and create frame)
-
- case NS_STYLE_DISPLAY_INLINE:
- kidDel = kid->GetDelegate(aCX);
- kidFrame = kidDel->CreateFrame(aCX, kid, kidIndex, this);
- NS_RELEASE(kidDel);
- break;
-
- default:
- NS_ASSERTION(nsnull == kidPrevInFlow, "bad prev in flow");
- nsFrame::NewFrame(&kidFrame, kid, kidIndex, this);
+ nsBlockReflowState* state = nsnull;
+ if (nsnull != aFrame) {
+ nsIFrame* parent;
+ aFrame->GetGeometricParent(parent);
+ while (nsnull != parent) {
+ nsBlockFrame* block;
+ nsresult rv = parent->QueryInterface(kBlockFrameCID, (void**) &block);
+ if (NS_OK == rv) {
+ nsIPresShell* shell = aPresContext->GetShell();
+ state = (nsBlockReflowState*) shell->GetCachedData(block);
+ NS_RELEASE(shell);
break;
}
- kidFrame->SetStyleContext(aCX,kidSC);
- } else {
- // Since kid has a prev-in-flow, use that to create the next
- // frame.
- kidPrevInFlow->CreateContinuingFrame(aCX, this, kidFrame);
+ parent->GetGeometricParent(parent);
}
-
- // Link child frame into the list of children. If the frame ends
- // up not fitting and getting pushed, the PushKids code will fixup
- // the child count for us.
- if (nsnull != prevKidFrame) {
-#ifdef NS_DEBUG
- nsIFrame* nextSibling;
-
- prevKidFrame->GetNextSibling(nextSibling);
- NS_ASSERTION(nsnull == nextSibling, "bad append");
-#endif
- prevKidFrame->SetNextSibling(kidFrame);
- } else {
- NS_ASSERTION(nsnull == mFirstChild, "bad create");
- mFirstChild = kidFrame;
- SetFirstContentOffset(kidFrame);
- }
- prevKidFrame = kidFrame;
- mChildCount++;
-
- // Reflow child frame as many times as necessary until it is
- // complete.
- ReflowStatus status;
- do {
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // We ran out of room.
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- mLastContentIsComplete = PRBool(nsnull == kidNextInFlow);
- PushKids(aState);
-
- goto push_done;
- }
-
- // Did the child complete?
- prevKidFrame = kidFrame;
- if (frNotComplete == status) {
- // Child didn't complete so create a continuing frame
- kidPrevInFlow = kidFrame;
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
-
- // Add the continuing frame to the sibling list
- nsIFrame* kidNextSibling;
-
- kidFrame->GetNextSibling(kidNextSibling);
- continuingFrame->SetNextSibling(kidNextSibling);
- kidFrame->SetNextSibling(continuingFrame);
- kidFrame = continuingFrame;
- mChildCount++;
-
- // Switch to new kid style
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
- }
-#ifdef NS_DEBUG
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- NS_ASSERTION(nsnull == kidNextInFlow, "huh?");
-#endif
- } while (frNotComplete == status);
-
- // The child that we just reflowed is complete
-#ifdef NS_DEBUG
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
-#endif
- kidIndex++;
- kidPrevInFlow = nsnull;
}
-
- done:
- // To get here we either completely reflowed all our appended
- // children OR we are a pseudo-frame and we ran into a block
- // element. In either case our last content MUST be complete.
- NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state");
- NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
- SetLastContentOffset(prevKidFrame);
-
- push_done:
-#ifdef NS_DEBUG
- PRInt32 len = LengthOf(mFirstChild);
- NS_ASSERTION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
- return result;
+ return state;
}
-/**
- * Pullup frames from our next in flow and try to place them. Before
- * this is called our previously mapped children, if any have been
- * reflowed which means that the block reflow state's x and y
- * coordinates and other data are ready to go.
- *
- * Return true if we pulled everything up.
- */
-PRBool
-nsBlockFrame::PullUpChildren(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
-#ifdef NS_DEBUG
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": pullup (childCount=%d) [%d,%d,%c]\n",
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
-
- PRBool result = PR_TRUE;
- nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
- nsIFrame* prevKidFrame;
-
- LastChild(prevKidFrame);
- while (nsnull != nextInFlow) {
- // Get first available frame from the next-in-flow
- nsIFrame* kidFrame = PullUpOneChild(nextInFlow, prevKidFrame);
- if (nsnull == kidFrame) {
- // We've pulled up all the children from that next-in-flow, so
- // move to the next next-in-flow.
- nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
- continue;
- }
-
- // Get style information for the pulled up kid
- nsIContentPtr kid;
-
- kidFrame->GetContent(kid.AssignRef());
- nsIStyleContextPtr kidSC = aCX->ResolveStyleContextFor(kid, this);
-
- ReflowStatus status;
- do {
- PRIntn placeStatus = PlaceAndReflowChild(aCX, aState, kidFrame, kidSC);
- status = aState.reflowStatus;
- if (0 == (placeStatus & PLACE_FIT)) {
- // Push the kids that didn't fit back down to the next-in-flow
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- mLastContentIsComplete = PRBool(nsnull == kidNextInFlow);
- PushKids(aState);
-
- result = PR_FALSE;
- goto push_done;
- }
-
- if (frNotComplete == status) {
- // Child is not complete
- nsIFrame* kidNextInFlow;
-
- kidFrame->GetNextInFlow(kidNextInFlow);
- if (nsnull == kidNextInFlow) {
- // Create a continuing frame for the incomplete child
- nsIFrame* continuingFrame;
-
- kidFrame->CreateContinuingFrame(aCX, this, continuingFrame);
-
- // Add the continuing frame to our sibling list.
- nsIFrame* nextSibling;
-
- kidFrame->GetNextSibling(nextSibling);
- continuingFrame->SetNextSibling(nextSibling);
- kidFrame->SetNextSibling(continuingFrame);
- prevKidFrame = kidFrame;
- kidFrame = continuingFrame;
- mChildCount++;
-
- // Switch to new kid style
- kidFrame->GetStyleContext(aCX, kidSC.AssignRef());
- } else {
- // The child has a next-in-flow, but it's not one of ours.
- // It *must* be in one of our next-in-flows. Collect it
- // then.
- NS_ASSERTION(!IsChild(kidNextInFlow), "busted kid next-in-flow");
- break;
- }
- }
- } while (frNotComplete == status);
-
- prevKidFrame = kidFrame;
- }
-
- if (nsnull != prevKidFrame) {
- // The only way we can get here is by pulling up every last child
- // in our next-in-flows (and reflowing any continunations they
- // have). Therefore we KNOW that our last child is complete.
- NS_ASSERTION(PR_TRUE == aState.lastContentIsComplete, "bad state");
- NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
- SetLastContentOffset(prevKidFrame);
- }
-
- push_done:;
-
- if (result == PR_FALSE) {
- // If our next-in-flow is empty OR our next next-in-flow is empty
- // then adjust the offsets of all of the empty next-in-flows.
- nextInFlow = (nsBlockFrame*) mNextInFlow;
- if ((0 == nextInFlow->mChildCount) ||
- ((nsnull != nextInFlow->mNextInFlow) &&
- (0 == ((nsBlockFrame*)(nextInFlow->mNextInFlow))->mChildCount))) {
- // We didn't pullup everything and we need to fixup one of our
- // next-in-flows content offsets.
- AdjustOffsetOfEmptyNextInFlows();
- }
- }
-
-
-#ifdef NS_DEBUG
- PRInt32 len = LengthOf(mFirstChild);
- NS_ASSERTION(len == mChildCount, "bad child count");
- VerifyLastIsComplete();
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n",
- (result ? "" : "NOT "),
- mChildCount,
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F'));
- DumpFlow();
-#endif
- return result;
-}
-
-void nsBlockFrame::SetupState(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- const nsSize& aMaxSize,
- nsSize* aMaxElementSize,
- nsISpaceManager* aSpaceManager)
-{
- // Setup reflow state
- aState.Init(aMaxSize, aMaxElementSize, mStyleContext, aSpaceManager);
-
- nsStyleSpacing* mySpacing = (nsStyleSpacing*)
- mStyleContext->GetData(kStyleSpacingSID);
-
- // Apply border and padding adjustments for regular frames only
- if (PR_FALSE == IsPseudoFrame()) {
- aState.borderPadding = mySpacing->mBorderPadding;
- aState.y = mySpacing->mBorderPadding.top;
- aState.availSize.width -=
- (mySpacing->mBorderPadding.left + mySpacing->mBorderPadding.right);
- aState.availSize.height -=
- (mySpacing->mBorderPadding.top + mySpacing->mBorderPadding.bottom);
- } else {
- aState.borderPadding.SizeTo(0, 0, 0, 0);
- }
-
- // Setup initial list ordinal value
- nsIAtom* tag = mContent->GetTag();
- if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
- (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
- nsHTMLValue value;
- if (eContentAttr_HasValue ==
- ((nsIHTMLContent*)mContent)->GetAttribute(nsHTMLAtoms::start, value)) {
- if (eHTMLUnit_Integer == value.GetUnit()) {
- aState.nextListOrdinal = value.GetIntValue();
- }
- }
- }
- NS_RELEASE(tag);
-
- mCurrentState = &aState;
-}
-
-#include "nsUnitConversion.h"/* XXX */
-NS_METHOD nsBlockFrame::ResizeReflow(nsIPresContext* aCX,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsSize* aMaxElementSize,
- ReflowStatus& aStatus)
-{
- nsBlockReflowState state;
- SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager);
- return DoResizeReflow(aCX, state, aDesiredRect, aStatus);
-}
-
-nsresult nsBlockFrame::DoResizeReflow(nsIPresContext* aCX,
- nsBlockReflowState& aState,
- nsRect& aDesiredRect,
- ReflowStatus& aStatus)
+nsresult
+nsBlockFrame::DoResizeReflow(nsBlockReflowState& aState,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ ReflowStatus& aStatus)
{
#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
PreReflowCheck();
#endif
-#ifdef NOISY
+#ifdef NOISY_REFLOW
ListTag(stdout);
- printf(": resize reflow %g,%g\n",
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width),
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height));
- DumpFlow();
+ printf(": Before:\n");
+ List(stdout, 1);
#endif
- // Zap old line data
- if (nsnull != mLines) {
- delete mLines;
- mLines = nsnull;
- }
- mNumLines = 0;
+ nsresult rv = NS_OK;
- // Check for an overflow list
- MoveOverflowToChildList();
-
- // Before we start reflowing, cache a pointer to our state structure
- // so that inline frames can find it.
- nsIPresShell* shell = aCX->GetShell();
+ nsIPresShell* shell = aState.mPresContext->GetShell();
shell->PutCachedData(this, &aState);
- // First reflow any existing frames
- PRBool reflowMappedOK = PR_TRUE;
- aStatus = frComplete;
- if (nsnull != mFirstChild) {
- reflowMappedOK = ReflowMappedChildren(aCX, aState);
- if (!reflowMappedOK) {
- aStatus = frNotComplete;
- }
+ // Check for an overflow list
+ DrainOverflowList();
+
+ if (nsnull != mLines) {
+ rv = ReflowMapped(aState);
}
- if (reflowMappedOK) {
- // Any space left?
- nscoord yb = aState.borderPadding.top + aState.availSize.height;
- if ((nsnull != mFirstChild) && (aState.y >= yb)) {
- // No space left. Don't try to pull-up children or reflow
- // unmapped. We need to return the correct completion status,
- // so see if there is more to reflow.
- if (MoreToReflow(aCX)) {
- aStatus = frNotComplete;
- }
- } else if (MoreToReflow(aCX)) {
- // Try and pull-up some children from a next-in-flow
- if ((nsnull == mNextInFlow) || PullUpChildren(aCX, aState)) {
- // If we still have unmapped children then create some new frames
- if (MoreToReflow(aCX)) {
- aStatus = ReflowAppendedChildren(aCX, aState);
- }
- } else {
- // We were unable to pull-up all the existing frames from the
- // next in flow
- aStatus = frNotComplete;
- }
+ if (NS_OK == rv) {
+ if ((nsnull != mLines) && (aState.mAvailSize.height <= 0)) {
+ // We are out of space
}
- }
-
- // Deal with last line - make sure it gets vertically and
- // horizontally aligned. This also updates the state's y coordinate
- // which is good because that's how we size ourselves.
- if (0 != aState.lineLength) {
- if (!AdvanceToNextLine(aCX, aState)) {
- // The last line of output doesn't fit. Push all of the kids to
- // the next in flow and change our reflow status to not complete
- // so that we are continued.
-#ifdef NOISY
- ListTag(stdout);
- printf(": pushing kids since last line doesn't fit\n");
-#endif
-
- PushKids(aState);
- aStatus = frNotComplete;
+ if (MoreToReflow(aState)) {
+ rv = ReflowUnmapped(aState);
}
}
- if (frComplete == aStatus) {
- // Don't forget to add in the bottom margin from our last child.
- // Only add it in if there is room for it.
- nscoord margin = aState.prevMaxPosBottomMargin -
- aState.prevMaxNegBottomMargin;
- nscoord y = aState.y + margin;
- if (y <= aState.borderPadding.top + aState.availSize.height) {
- aState.y = y;
- }
- }
-
- // Now that reflow has finished, remove the cached pointer
- shell->RemoveCachedData(this);
- NS_RELEASE(shell);
-
- // Translate state.lineLengths into an integer array
- mNumLines = aState.lineLengths.Count();
- if (mNumLines > 0) {
- mLines = new PRInt32[mNumLines];
- for (PRInt32 i = 0; i < mNumLines; i++) {
- PRInt32 ll = (PRInt32) aState.lineLengths.ElementAt(i);
- mLines[i] = ll;
- }
- }
-
- if (!aState.unconstrainedWidth && aState.justifying) {
- // Perform justification now that we know how many lines we have.
- JustifyLines(aCX, aState);
- }
-
// Return our desired rect and our status
aDesiredRect.x = 0;
aDesiredRect.y = 0;
- aDesiredRect.width = aState.kidXMost + aState.borderPadding.right;
- if (!aState.unconstrainedWidth) {
+ aDesiredRect.width = aState.mKidXMost + aState.mBorderPadding.right;
+ if (!aState.mUnconstrainedWidth) {
// Make sure we're at least as wide as the max size we were given
- nscoord maxWidth = aState.availSize.width + aState.borderPadding.left +
- aState.borderPadding.right;
-
+ nscoord maxWidth = aState.mAvailSize.width + aState.mBorderPadding.left +
+ aState.mBorderPadding.right;
if (aDesiredRect.width < maxWidth) {
aDesiredRect.width = maxWidth;
}
}
- aState.y += aState.borderPadding.bottom;
- aDesiredRect.height = aState.y;
-
-#ifdef NS_DEBUG
- PostReflowCheck(aStatus);
-#endif
-#ifdef NOISY
- ListTag(stdout);
- printf(": resize reflow %g,%g %scomplete [%d,%d,%c]\n",
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.width),
- NS_TWIPS_TO_POINTS_FLOAT(aState.availSize.height),
- ((status == frNotComplete) ? "NOT " : ""),
- mFirstContentOffset, mLastContentOffset,
- (mLastContentIsComplete ? 'T' : 'F')
- );
- DumpFlow();
-#endif
- mCurrentState = nsnull;
- return NS_OK;
-}
-
-void nsBlockFrame::JustifyLines(nsIPresContext* aCX,
- nsBlockReflowState& aState)
-{
- // XXX we don't justify the last line; what if we are continued,
- // should we do it then?
- nsIFrame* kid = mFirstChild;
- for (PRInt32 i = 0; i < mNumLines; i++) {
- nsIFrame* lineStart = kid;
- PRInt32 lineLength = mLines[i];
- if (i < mNumLines - 1) {
- if (1 == lineLength) {
- // For lines with one element on them we can take a shortcut and
- // let them do the justification. Note that we still call
- // JustifyReflow even if the available space is zero in case the
- // child is a hunk of text that ends in whitespace. The whitespace
- // will be distributed elsewhere causing a proper flush right look
- // for the last word.
- nsRect r;
- kid->GetRect(r);
- nscoord maxWidth = aState.availSize.width;
- nscoord availableSpace = maxWidth - r.width;
- nscoord maxAvailSpace = nscoord(maxWidth * 0.1f);
- if ((availableSpace >= 0) && (availableSpace < maxAvailSpace)) {
- kid->JustifyReflow(aCX, availableSpace);
- kid->SizeTo(r.width + availableSpace, r.height);
- }
- kid->GetNextSibling(kid);
- } else {
- // XXX Get justification of multiple elements working
- while (--lineLength >= 0) {
- kid->GetNextSibling(kid);
- }
+ aState.mY += aState.mBorderPadding.bottom;
+ nscoord lastBottomMargin = aState.mPrevMaxPosBottomMargin -
+ aState.mPrevMaxNegBottomMargin;
+ if (!aState.mUnconstrainedHeight && (lastBottomMargin > 0)) {
+ // It's possible that we don't have room for the last bottom
+ // margin (the last bottom margin is the margin following a block
+ // element that we contain; it isn't applied immediately because
+ // of the margin collapsing logic). This can happen when we are
+ // reflowed in a limited amount of space because we don't know in
+ // advance what the last bottom margin will be.
+ nscoord maxY = aMaxSize.height;
+ if (aState.mY + lastBottomMargin > maxY) {
+ lastBottomMargin = maxY - aState.mY;
+ if (lastBottomMargin < 0) {
+ lastBottomMargin = 0;
}
}
-
- // Finally, now that the in-flow positions of the line's frames are
- // known we can apply relative positioning if any of them need it.
- nsCSSLayout::RelativePositionChildren(aCX, this, lineStart, mLines[i]);
+ aState.mY += lastBottomMargin;
}
-}
+ aDesiredRect.height = aState.mY;
-NS_METHOD nsBlockFrame::IsSplittable(SplittableType& aIsSplittable) const
-{
- aIsSplittable = frSplittableNonRectangular;
- return NS_OK;
-}
-
-NS_METHOD nsBlockFrame::CreateContinuingFrame(nsIPresContext* aCX,
- nsIFrame* aParent,
- nsIFrame*& aContinuingFrame)
-{
- nsBlockFrame* cf = new nsBlockFrame(mContent, mIndexInParent, aParent);
- PrepareContinuingFrame(aCX, aParent, cf);
- aContinuingFrame = cf;
- return NS_OK;
-}
-
-NS_METHOD nsBlockFrame::IncrementalReflow(nsIPresContext* aCX,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsReflowCommand& aReflowCommand,
- ReflowStatus& aStatus)
-{
+ // Set return status
aStatus = frComplete;
-
- if (aReflowCommand.GetTarget() == this) {
- // XXX for now, just do a complete reflow mapped (it'll kinda
- // work, but it's slow)
-
- nsBlockReflowState state;
- SetupState(aCX, state, aMaxSize, nsnull, aSpaceManager);
- PRBool reflowMappedOK = ReflowMappedChildren(aCX, state);
- if (!reflowMappedOK) {
- aStatus = frNotComplete;
- }
- } else {
- // XXX not yet implemented
- NS_ABORT();
- // XXX work to compute initial state goes *HERE*
- aDesiredRect.x = 0;
- aDesiredRect.y = 0;
- aDesiredRect.width = 0;
- aDesiredRect.height = 0;
+ if (NS_LINE_LAYOUT_NOT_COMPLETE == rv) {
+ rv = NS_OK;
+ aStatus = frNotComplete;
}
+#ifdef NS_DEBUG
+ // Verify that the line layout code pulled everything up when it
+ // indicates a complete reflow.
+ if (frComplete == aStatus) {
+ nsBlockFrame* nextBlock = (nsBlockFrame*) mNextInFlow;
+ while (nsnull != nextBlock) {
+ NS_ASSERTION((nsnull == nextBlock->mLines) &&
+ (nextBlock->mChildCount == 0) &&
+ (nsnull == nextBlock->mFirstChild),
+ "bad completion status");
+ nextBlock = (nsBlockFrame*) nextBlock->mNextInFlow;
+ }
- mCurrentState = nsnull;
- return NS_OK;
+#if XXX
+ // We better not be in the same parent frame as our prev-in-flow.
+ // If we are it means that we were continued instead of pulling up
+ // children.
+ if (nsnull != mPrevInFlow) {
+ nsIFrame* ourParent = mGeometricParent;
+ nsIFrame* prevParent = ((nsBlockFrame*)mPrevInFlow)->mGeometricParent;
+ NS_ASSERTION(ourParent != prevParent, "bad continuation");
+ }
+#endif
+ }
+#endif
+
+ // Now that reflow has finished, remove the cached pointer
+ shell->RemoveCachedData(this);
+ NS_RELEASE(shell);
+
+#ifdef NS_DEBUG
+ VerifyLines(PR_TRUE);
+ PostReflowCheck(aStatus);
+#endif
+#ifdef NOISY_REFLOW
+ ListTag(stdout);
+ printf(": After:\n");
+ List(stdout, 1);
+#endif
+ return rv;
}
-PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
+//----------------------------------------------------------------------
+
+NS_METHOD
+nsBlockFrame::ContentAppended(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentInserted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentReplaced(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aOldChild,
+ nsIContent* aNewChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::ContentDeleted(nsIPresShell* aShell,
+ nsIPresContext* aPresContext,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ PRInt32 aIndexInParent)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::GetReflowMetrics(nsIPresContext* aPresContext,
+ nsReflowMetrics& aMetrics)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+NS_METHOD
+nsBlockFrame::ResizeReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsSize* aMaxElementSize,
+ nsIFrame::ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ aStatus = frComplete;
+ nsBlockReflowState state;
+ rv = InitializeState(aPresContext, aSpaceManager, aMaxSize,
+ aMaxElementSize, state);
+ if (NS_OK == rv) {
+ nsRect desiredRect;
+ rv = DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus);
+ }
+ return rv;
+}
+
+NS_METHOD
+nsBlockFrame::IncrementalReflow(nsIPresContext* aPresContext,
+ nsISpaceManager* aSpaceManager,
+ const nsSize& aMaxSize,
+ nsRect& aDesiredRect,
+ nsReflowCommand& aReflowCommand,
+ nsIFrame::ReflowStatus& aStatus)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+//----------------------------------------------------------------------
+
+PRBool
+nsBlockFrame::AddFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder)
+{
+ nsIPresShell* shell = aPresContext->GetShell();
+ nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this);
+ NS_RELEASE(shell);
+
+ if (nsnull != state) {
+ // Get the frame associated with the space manager, and get its
+ // nsIAnchoredItems interface
+ nsIFrame* frame = state->mSpaceManager->GetFrame();
+ nsIAnchoredItems* anchoredItems = nsnull;
+
+ frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems);
+ NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface");
+ if (nsnull != anchoredItems) {
+ anchoredItems->AddAnchoredItem(aFloater,
+ nsIAnchoredItems::anHTMLFloater,
+ this);
+ PlaceFloater(aPresContext, aFloater, aPlaceholder, *state);
+ return PR_TRUE;
+ }
+ }
+
+ return PR_FALSE;
+}
+
+void
+nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder)
+{
+ nsIPresShell* shell = aPresContext->GetShell();
+ nsBlockReflowState* state = (nsBlockReflowState*) shell->GetCachedData(this);
+ NS_RELEASE(shell);
+ if (nsnull != state) {
+ PlaceFloater(aPresContext, aFloater, aPlaceholder, *state);
+ }
+}
+
+PRBool
+nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
{
do {
nsIFrame* parent;
-
aFrame->GetGeometricParent(parent);
- // See if there are any non-zero sized child frames that precede aFrame
- // in the child list
+ // See if there are any non-zero sized child frames that precede
+ // aFrame in the child list
nsIFrame* child;
-
parent->FirstChild(child);
while ((nsnull != child) && (aFrame != child)) {
nsSize size;
@@ -1949,7 +1285,6 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
// We found a non-zero sized child frame that precedes aFrame
return PR_FALSE;
}
-
child->GetNextSibling(child);
}
@@ -1957,184 +1292,127 @@ PRBool nsBlockFrame::IsLeftMostChild(nsIFrame* aFrame)
// Walk up one level and check that its parent is left-most as well
aFrame = parent;
} while (aFrame != this);
-
return PR_TRUE;
}
-PRBool nsBlockFrame::AddFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
- PlaceholderFrame* aPlaceholder)
-{
- // Get the frame associated with the space manager, and get its nsIAnchoredItems
- // interface
- nsIFrame* frame = mCurrentState->spaceManager->GetFrame();
- nsIAnchoredItems* anchoredItems = nsnull;
-
- frame->QueryInterface(kIAnchoredItemsIID, (void**)&anchoredItems);
- NS_ASSERTION(nsnull != anchoredItems, "no anchored items interface");
-
- if (nsnull != anchoredItems) {
- anchoredItems->AddAnchoredItem(aFloater, nsIAnchoredItems::anHTMLFloater, this);
- PlaceFloater(aCX, aFloater, aPlaceholder);
- return PR_TRUE;
- }
-
- return PR_FALSE;
-}
-
-// XXX The size of the floater needs to be taken into consideration if we're
-// computing a maximum element size
-void nsBlockFrame::PlaceFloater(nsIPresContext* aCX,
- nsIFrame* aFloater,
- PlaceholderFrame* aPlaceholder)
+void
+nsBlockFrame::PlaceFloater(nsIPresContext* aPresContext,
+ nsIFrame* aFloater,
+ PlaceholderFrame* aPlaceholder,
+ nsBlockReflowState& aState)
{
// If the floater is the left-most non-zero size child frame then insert
// it before the current line; otherwise add it to the below-current-line
// todo list and we'll handle it when we flush out the line
if (IsLeftMostChild(aPlaceholder)) {
- // Get the type of floater
- nsIStyleContextPtr styleContext;
+ nsISpaceManager* sm = aState.mSpaceManager;
- aFloater->GetStyleContext(aCX, styleContext.AssignRef());
+ // Get the type of floater
+ nsIStyleContextPtr styleContext;
+ aFloater->GetStyleContext(aPresContext, styleContext.AssignRef());
nsStyleDisplay* floaterDisplay = (nsStyleDisplay*)
styleContext->GetData(kStyleDisplaySID);
- if (!mCurrentState->isInline) {
- // Get the current band for this line
- GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin);
- }
-
- // Commit some space in the space manager
- nsRect region;
-
+ // Commit some space in the space manager and adjust our current
+ // band of available space.
+ nsRect region;
aFloater->GetRect(region);
- region.y = mCurrentState->currentBand->availSpace.y;
-
+ region.y = aState.mY;
if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) {
- region.x = mCurrentState->currentBand->availSpace.x;
+ region.x = aState.mX;
} else {
- NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats, "bad float type");
- region.x = mCurrentState->currentBand->availSpace.XMost() - region.width;
+ NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats,
+ "bad float type");
+ region.x = aState.mCurrentBand.availSpace.XMost() - region.width;
}
// XXX Don't forget the floater's margins...
- mCurrentState->spaceManager->Translate(mCurrentState->borderPadding.left, 0);
- mCurrentState->spaceManager->AddRectRegion(region, aFloater);
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->AddRectRegion(region, aFloater);
// Set the origin of the floater in world coordinates
nscoord worldX, worldY;
-
- mCurrentState->spaceManager->GetTranslation(worldX, worldY);
+ sm->GetTranslation(worldX, worldY);
aFloater->MoveTo(region.x + worldX, region.y + worldY);
- mCurrentState->spaceManager->Translate(-mCurrentState->borderPadding.left, 0);
+ sm->Translate(-aState.mBorderPadding.left, 0);
- // Update the band of available space to reflect space taken up by the floater
- GetAvailableSpaceBand(*mCurrentState, mCurrentState->y + mCurrentState->topMargin);
+ // Update the band of available space to reflect space taken up by
+ // the floater
+ GetAvailableSpace(aState, aState.mY);
+ if (nsnull != aState.mCurrentLine) {
+ nsLineLayout& lineLayout = *aState.mCurrentLine;
+ lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace);
+ }
} else {
// Add the floater to our to-do list
- mCurrentState->floaterToDo.AppendElement(aFloater);
+ aState.mPendingFloaters.AppendElement(aFloater);
}
}
-NS_METHOD nsBlockFrame::ContentAppended(nsIPresShell* aShell,
- nsIPresContext* aPresContext,
- nsIContent* aContainer)
+void
+nsBlockFrame::PlaceBelowCurrentLineFloaters(nsBlockReflowState& aState,
+ nscoord aY)
{
- // Get the last-in-flow
- nsBlockFrame* flow = (nsBlockFrame*)GetLastInFlow();
- PRInt32 kidIndex = flow->NextChildOffset();
- PRInt32 startIndex = kidIndex;
+ NS_PRECONDITION(aState.mPendingFloaters.Count() > 0, "no floaters");
-#if 0
- nsIFrame* kidFrame = nsnull;
- nsIFrame* prevKidFrame;
-
- flow->LastChild(prevKidFrame);
- for (;;) {
- // Get the next content object
- nsIContentPtr kid = mContent->ChildAt(kidIndex);
- if (nsnull == kid) {
- break;
- }
+ nsISpaceManager* sm = aState.mSpaceManager;
+ nsBlockBandData* bd = &aState.mCurrentBand;
- // Resolve style for the kid
- nsIStyleContextPtr kidSC = aPresContext->ResolveStyleContextFor(kid, this);
- nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
- kidSC->GetData(kStyleDisplaySID);
+ // XXX Factor this code with PlaceFloater()...
+ PRInt32 numFloaters = aState.mPendingFloaters.Count();
+ for (PRInt32 i = 0; i < numFloaters; i++) {
+ nsIFrame* floater = (nsIFrame*) aState.mPendingFloaters[i];
+ nsRect region;
- // Is it a floater?
- if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
- PlaceholderFrame::NewFrame(&kidFrame, kid, kidIndex, this);
+ // Get the band of available space
+ // XXX This is inefficient to do this inside the loop...
+ GetAvailableSpace(aState, aY);
+
+ // Get the type of floater
+ nsIStyleContextPtr styleContext;
+ floater->GetStyleContext(aState.mPresContext, styleContext.AssignRef());
+ nsStyleDisplay* sd = (nsStyleDisplay*)
+ styleContext->GetData(kStyleDisplaySID);
+
+ floater->GetRect(region);
+ region.y = bd->availSpace.y;
+ if (NS_STYLE_FLOAT_LEFT == sd->mFloats) {
+ region.x = bd->availSpace.x;
} else {
- // Create initial frame for the child
- nsIContentDelegate* kidDel;
- nsresult fr;
- switch (kidDisplay->mDisplay) {
- case NS_STYLE_DISPLAY_BLOCK:
- case NS_STYLE_DISPLAY_LIST_ITEM:
- // Pseudo block frames do not contain other block elements
- // unless the block element would be the first child.
- if (IsPseudoFrame()) {
- // Update last content offset now that we are done drawing
- // children from our parent.
- SetLastContentOffset(prevKidFrame);
-
- // It better be true that we are not being asked to flow a
- // block element as our first child. That means the body
- // decided it needed a pseudo-frame when it shouldn't have.
- NS_ASSERTION(nsnull != mFirstChild, "bad body");
-
- return NS_OK;
- }
- // FALL THROUGH (and create frame)
-
- case NS_STYLE_DISPLAY_INLINE:
- kidDel = kid->GetDelegate(aPresContext);
- kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this);
- NS_RELEASE(kidDel);
- break;
-
- default:
- fr = nsFrame::NewFrame(&kidFrame, kid, kidIndex, this);
- break;
- }
+ NS_ASSERTION(NS_STYLE_FLOAT_RIGHT == sd->mFloats, "bad float type");
+ region.x = bd->availSpace.XMost() - region.width;
}
- kidFrame->SetStyleContext(aCX,kidSC);
- // Link child frame into the list of children
- if (nsnull != prevKidFrame) {
-#ifdef NS_DEBUG
- nsIFrame* nextSibling;
+ // XXX Don't forget the floater's margins...
+ sm->Translate(aState.mBorderPadding.left, 0);
+ sm->AddRectRegion(region, floater);
- prevKidFrame->GetNextSibling(nextSibling);
- NS_ASSERTION(nsnull == nextSibling, "bad append");
-#endif
- prevKidFrame->SetNextSibling(kidFrame);
- } else {
- NS_ASSERTION(nsnull == mFirstChild, "bad create");
- mFirstChild = kidFrame;
- SetFirstContentOffset(kidFrame);
- }
- prevKidFrame = kidFrame;
- kidIndex++;
- mChildCount++;
+ // Set the origin of the floater in world coordinates
+ nscoord worldX, worldY;
+ sm->GetTranslation(worldX, worldY);
+ floater->MoveTo(region.x + worldX, region.y + worldY);
+ sm->Translate(-aState.mBorderPadding.left, 0);
}
- SetLastContentOffset(prevKidFrame);
-#endif
- // If this is a pseudo-frame then our parent will generate the
- // reflow command. Otherwise, if the container is us then we should
- // generate the reflow command because we were directly called.
- if (!IsPseudoFrame() && (aContainer == mContent)) {
- nsReflowCommand* rc =
- new nsReflowCommand(aPresContext, flow, nsReflowCommand::FrameAppended,
- startIndex);
- aShell->AppendReflowCommand(rc);
+ aState.mPendingFloaters.Clear();
+
+ // Pass on updated available space to the current line
+ if (nsnull != aState.mCurrentLine) {
+ nsLineLayout& lineLayout = *aState.mCurrentLine;
+ lineLayout.SetReflowSpace(aState.mCurrentBand.availSpace);
}
- return NS_OK;
}
-PRIntn nsBlockFrame::GetSkipSides() const
+//----------------------------------------------------------------------
+
+nsLineData*
+nsBlockFrame::GetFirstLine()
+{
+ return mLines;
+}
+
+PRIntn
+nsBlockFrame::GetSkipSides() const
{
PRIntn skip = 0;
if (nsnull != mPrevInFlow) {
@@ -2145,40 +1423,3 @@ PRIntn nsBlockFrame::GetSkipSides() const
}
return skip;
}
-
-nsHTMLFrameType nsBlockFrame::GetFrameType() const
-{
- return eHTMLFrame_Block;
-}
-
-NS_METHOD nsBlockFrame::ListTag(FILE* out) const
-{
- if ((nsnull != mGeometricParent) && IsPseudoFrame()) {
- fprintf(out, "*block<");
- nsIAtom* atom = mContent->GetTag();
- if (nsnull != atom) {
- nsAutoString tmp;
- atom->ToString(tmp);
- fputs(tmp, out);
- }
- fprintf(out, ">(%d)@%p", mIndexInParent, this);
- } else {
- nsHTMLContainerFrame::ListTag(out);
- }
- return NS_OK;
-}
-
-#ifdef NS_DEBUG
-void nsBlockFrame::DumpFlow() const
-{
-#ifdef NOISY_FLOW
- nsBlockFrame* flow = (nsBlockFrame*) mNextInFlow;
- while (flow != 0) {
- printf(" %p: [%d,%d,%c]\n",
- flow, flow->mFirstContentOffset, flow->mLastContentOffset,
- (flow->mLastContentIsComplete ? 'T' : 'F'));
- flow = (nsBlockFrame*) flow->mNextInFlow;
- }
-#endif
-}
-#endif
diff --git a/layout/html/base/src/nsHRPart.cpp b/layout/html/base/src/nsHRPart.cpp
index f1852035c969..ab7a6b9ffb05 100644
--- a/layout/html/base/src/nsHRPart.cpp
+++ b/layout/html/base/src/nsHRPart.cpp
@@ -73,9 +73,9 @@ public:
PRInt32 aIndexInParent,
nsIFrame* aParentFrame);
- NS_METHOD Paint(nsIPresContext& aPresContext,
- nsIRenderingContext& aRenderingContext,
- const nsRect& aDirtyRect);
+ NS_IMETHOD Paint(nsIPresContext& aPresContext,
+ nsIRenderingContext& aRenderingContext,
+ const nsRect& aDirtyRect);
protected:
virtual ~HRuleFrame();
diff --git a/layout/html/base/src/nsHTMLContainerFrame.h b/layout/html/base/src/nsHTMLContainerFrame.h
index 9364e3f870b9..0d2d6879e04f 100644
--- a/layout/html/base/src/nsHTMLContainerFrame.h
+++ b/layout/html/base/src/nsHTMLContainerFrame.h
@@ -42,14 +42,6 @@ public:
nsIFrame** aFrame,
PRInt32& aCursor);
-#if 0
- virtual ReflowStatus IncrementalReflow(nsIPresContext* aPresContext,
- nsReflowMetrics& aDesiredSize,
- const nsSize& aMaxSize,
- nsISpaceManager* aSpaceManager,
- nsReflowCommand& aReflowCommand);
-#endif
-
protected:
virtual ~nsHTMLContainerFrame();
@@ -59,32 +51,6 @@ protected:
const nsString& aBase,
const nsString& aURLSpec,
const nsString& aTargetSpec);
-
-#if 0
- virtual ReflowStatus ReflowAppended(nsIPresContext* aPresContext,
- nsReflowMetrics& aDesiredSize,
- const nsSize& aMaxSize,
- nsISpaceManager* aSpaceManager,
- nsReflowCommand& aReflowCommand);
-
- virtual ReflowStatus ReflowInserted(nsIPresContext* aPresContext,
- nsReflowMetrics& aDesiredSize,
- const nsSize& aMaxSize,
- nsISpaceManager* aSpaceManager,
- nsReflowCommand& aReflowCommand);
-
- virtual ReflowStatus ReflowDeleted(nsIPresContext* aPresContext,
- nsReflowMetrics& aDesiredSize,
- const nsSize& aMaxSize,
- nsISpaceManager* aSpaceManager,
- nsReflowCommand& aReflowCommand);
-
- virtual ReflowStatus ReflowChanged(nsIPresContext* aPresContext,
- nsReflowMetrics& aDesiredSize,
- const nsSize& aMaxSize,
- nsISpaceManager* aSpaceManager,
- nsReflowCommand& aReflowCommand);
-#endif
};
#endif /* nsHTMLContainerFrame_h___ */
diff --git a/layout/html/base/src/nsHTMLIIDs.cpp b/layout/html/base/src/nsHTMLIIDs.cpp
index 64f39328accc..22bbf94f8607 100644
--- a/layout/html/base/src/nsHTMLIIDs.cpp
+++ b/layout/html/base/src/nsHTMLIIDs.cpp
@@ -17,7 +17,7 @@
*/
#include "nsHTMLIIDs.h"
#include "nsIHTMLContent.h"
-#include "nsIHTMLFrameType.h"
+#include "nsBlockFrame.h"
const nsIID kIHTMLContentIID = NS_IHTMLCONTENT_IID;
-const nsIID kIHTMLFrameTypeIID = NS_IHTMLFRAMETYPE_IID;
+const nsIID kBlockFrameCID = NS_BLOCKFRAME_CID;
diff --git a/layout/html/base/src/nsHTMLIIDs.h b/layout/html/base/src/nsHTMLIIDs.h
index 8a4e3e694caf..d6853c8410da 100644
--- a/layout/html/base/src/nsHTMLIIDs.h
+++ b/layout/html/base/src/nsHTMLIIDs.h
@@ -21,6 +21,6 @@
#include "nsISupports.h"
extern const nsIID kIHTMLContentIID;
-extern const nsIID kIHTMLFrameTypeIID;
+extern const nsIID kBlockFrameCID;
#endif /* nsHTMLIIDs_h___ */
diff --git a/layout/html/base/src/nsInlineFrame.cpp b/layout/html/base/src/nsInlineFrame.cpp
index 4864674f0487..9cd92de6cf59 100644
--- a/layout/html/base/src/nsInlineFrame.cpp
+++ b/layout/html/base/src/nsInlineFrame.cpp
@@ -592,42 +592,6 @@ nsInlineFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext,
// FALLTHROUGH
case NS_STYLE_DISPLAY_INLINE:
- // XXX temporary hack to make plain BR's in inlines "work"
- // get style for break-before-after; get break-type (line, page, etc.)
- {
- nsIAtom* tag = kid->GetTag();
- if (nsHTMLAtoms::br == tag) {
- // Set break-after flag so we stop mapping children (we
- // will end up being continued if there are more children)
- breakAfter = PR_TRUE;
-
- // Get cached state for containing block frame
- // XXX how about QueryInterface(kIHTMLBlockFrameIID)? DOH!
- nsBlockReflowState* state = nsnull;
- nsIFrame* parent = mGeometricParent;
- while (nsnull != parent) {
- nsIHTMLFrameType* ft;
- nsresult status =
- parent->QueryInterface(kIHTMLFrameTypeIID, (void**) &ft);
- if (NS_OK == status) {
- nsHTMLFrameType type = ft->GetFrameType();
- if (eHTMLFrame_Block == type) {
- break;
- }
- }
- parent->GetGeometricParent(parent);
- }
- if (nsnull != parent) {
- nsIPresShell* shell = aPresContext->GetShell();
- state = (nsBlockReflowState*) shell->GetCachedData(parent);
- // XXX Of course this won't work if the inline span is nested
- // in another inline span!
- state->breakAfterChild = PR_TRUE;
- NS_RELEASE(shell);
- }
- }
- NS_IF_RELEASE(tag);
- }
kidDel = kid->GetDelegate(aPresContext);
kidFrame = kidDel->CreateFrame(aPresContext, kid, kidIndex, this);
NS_RELEASE(kidDel);
diff --git a/layout/html/base/src/nsLineLayout.cpp b/layout/html/base/src/nsLineLayout.cpp
index e2371682c2e1..d3ec7ec27864 100644
--- a/layout/html/base/src/nsLineLayout.cpp
+++ b/layout/html/base/src/nsLineLayout.cpp
@@ -841,10 +841,10 @@ nsLineLayout::CreateFrameFor(nsIContent* aKid)
nsIFrame* kidFrame;
if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) {
AbsoluteFrame::NewFrame(&kidFrame, aKid, mKidIndex, mBlock);
- kidFrame->SetStyleContext(kidSC);
+ kidFrame->SetStyleContext(mPresContext, kidSC);
} else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
PlaceholderFrame::NewFrame(&kidFrame, aKid, mKidIndex, mBlock);
- kidFrame->SetStyleContext(kidSC);
+ kidFrame->SetStyleContext(mPresContext, kidSC);
} else if (nsnull == mKidPrevInFlow) {
// Create initial frame for the child
@@ -882,7 +882,7 @@ nsLineLayout::CreateFrameFor(nsIContent* aKid)
nsFrame::NewFrame(&kidFrame, aKid, mKidIndex, mBlock);
break;
}
- kidFrame->SetStyleContext(kidSC);
+ kidFrame->SetStyleContext(mPresContext, kidSC);
} else {
// Since kid has a prev-in-flow, use that to create the next
// frame.
diff --git a/layout/html/base/src/nsListItemFrame.cpp b/layout/html/base/src/nsListItemFrame.cpp
index 78e8b5e6336e..ee6971db0dde 100644
--- a/layout/html/base/src/nsListItemFrame.cpp
+++ b/layout/html/base/src/nsListItemFrame.cpp
@@ -197,7 +197,7 @@ PRInt32 BulletFrame::GetListItemOrdinal(nsIPresContext* aCX,
if (eHTMLUnit_Integer == value.GetUnit()) {
ordinal = value.GetIntValue();
if (nsnull != state) {
- state->nextListOrdinal = ordinal + 1;
+ state->mNextListOrdinal = ordinal + 1;
}
goto done;
}
@@ -205,7 +205,7 @@ PRInt32 BulletFrame::GetListItemOrdinal(nsIPresContext* aCX,
// Get ordinal from block reflow state
if (nsnull != state) {
- ordinal = state->nextListOrdinal;
+ ordinal = state->mNextListOrdinal;
if (ordinal < 0) {
// This is the first list item and the list container doesn't
// have a "start" attribute. Get the starting ordinal value
@@ -219,7 +219,7 @@ PRInt32 BulletFrame::GetListItemOrdinal(nsIPresContext* aCX,
break;
}
}
- state->nextListOrdinal = ordinal + 1;
+ state->mNextListOrdinal = ordinal + 1;
}
done:
@@ -518,26 +518,23 @@ nsListItemFrame::GetListContainerReflowState(nsIPresContext* aCX)
nsBlockReflowState* state = nsnull;
nsIFrame* parent = mGeometricParent;
while (nsnull != parent) {
- nsIHTMLFrameType* ft;
- nsresult status = parent->QueryInterface(kIHTMLFrameTypeIID, (void**) &ft);
+ void* ft;
+ nsresult status = parent->QueryInterface(kBlockFrameCID, &ft);
if (NS_OK == status) {
- nsHTMLFrameType type = ft->GetFrameType();
- if (eHTMLFrame_Block == type) {
- // The parent is a block. See if its content object is a list
- // container. Only UL, OL, MENU or DIR can be list containers.
- // XXX need something more flexible, say style?
- nsIContent* parentContent;
+ // The parent is a block. See if its content object is a list
+ // container. Only UL, OL, MENU or DIR can be list containers.
+ // XXX need something more flexible, say style?
+ nsIContent* parentContent;
- parent->GetContent(parentContent);
- nsIAtom* tag = parentContent->GetTag();
- NS_RELEASE(parentContent);
- if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
- (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
- NS_RELEASE(tag);
- break;
- }
+ parent->GetContent(parentContent);
+ nsIAtom* tag = parentContent->GetTag();
+ NS_RELEASE(parentContent);
+ if ((tag == nsHTMLAtoms::ul) || (tag == nsHTMLAtoms::ol) ||
+ (tag == nsHTMLAtoms::menu) || (tag == nsHTMLAtoms::dir)) {
NS_RELEASE(tag);
+ break;
}
+ NS_RELEASE(tag);
}
parent->GetGeometricParent(parent);
}
@@ -549,6 +546,29 @@ nsListItemFrame::GetListContainerReflowState(nsIPresContext* aCX)
return state;
}
+void
+nsListItemFrame::InsertBullet(nsIFrame* aBullet)
+{
+ mFirstChild = aBullet;
+ mChildCount++;
+ if (nsnull == mLines) {
+ mLines = new nsLineData();
+ mLines->mFirstChild = aBullet;
+ mLines->mChildCount = 1;
+ mLines->mFirstContentOffset = 0;
+ mLines->mLastContentOffset = 0;
+ mLines->mLastContentIsComplete = PR_TRUE;
+ }
+ else {
+ mLines->mChildCount++;
+ mLines->mFirstChild = aBullet;
+ }
+ mLines->mHasBullet = PR_TRUE;
+#ifdef NS_DEBUG
+ mLines->Verify();
+#endif
+}
+
/**
* The basic approach here is pretty simple: let our base class do all
* the hard work, and after it's done, get the bullet placed. We only
@@ -579,8 +599,7 @@ NS_METHOD nsListItemFrame::ResizeReflow(nsIPresContext* aCX,
// Inside bullets get placed on the list immediately so that
// the regular reflow logic can place them.
bullet = CreateBullet(aCX);
- mFirstChild = bullet;
- mChildCount++;
+ InsertBullet(bullet);
} else {
// We already have a first child. It's the bullet (check?)
// so we don't need to do anything here
@@ -593,6 +612,9 @@ NS_METHOD nsListItemFrame::ResizeReflow(nsIPresContext* aCX,
// Pull bullet off list (we'll put it back later)
bullet = mFirstChild;
bullet->GetNextSibling(mFirstChild);
+ mLines->mFirstChild = mFirstChild;
+ mLines->mChildCount--;
+ mLines->mHasBullet = PR_FALSE;
mChildCount--;
}
}
@@ -600,9 +622,9 @@ NS_METHOD nsListItemFrame::ResizeReflow(nsIPresContext* aCX,
// Let base class do things first
nsBlockReflowState state;
- SetupState(aCX, state, aMaxSize, aMaxElementSize, aSpaceManager);
- state.firstChildIsInsideBullet = insideBullet;
- DoResizeReflow(aCX, state, aDesiredRect, aStatus);
+ InitializeState(aCX, aSpaceManager, aMaxSize, aMaxElementSize, state);
+ state.mFirstChildIsInsideBullet = insideBullet;
+ DoResizeReflow(state, aMaxSize, aDesiredRect, aStatus);
// Now place the bullet and put it at the head of the list of children
if (!insideBullet && (nsnull != bullet)) {
@@ -612,19 +634,12 @@ NS_METHOD nsListItemFrame::ResizeReflow(nsIPresContext* aCX,
// information for the first line.
PlaceOutsideBullet(bullet, aCX);
bullet->SetNextSibling(mFirstChild);
- mFirstChild = bullet;
- mChildCount++;
- if (nsnull == mLines) {
- mLines = new PRInt32[1];
- mLines[0] = 1;
- mNumLines = 1;
- } else {
- mLines[0]++;
- }
+ InsertBullet(bullet);
}
return NS_OK;
}
+#if XXX
// XXX we may need to grow to accomodate the bullet
NS_METHOD nsListItemFrame::IncrementalReflow(nsIPresContext* aCX,
nsISpaceManager* aSpaceManager,
@@ -634,8 +649,10 @@ NS_METHOD nsListItemFrame::IncrementalReflow(nsIPresContext* aCX,
ReflowStatus& aStatus)
{
aStatus = frComplete;
+ // XXX
return NS_OK;
}
+#endif
NS_METHOD nsListItemFrame::CreateContinuingFrame(nsIPresContext* aCX,
nsIFrame* aParent,
diff --git a/layout/html/base/src/nsListItemFrame.h b/layout/html/base/src/nsListItemFrame.h
index 0a0c2c26573c..66ed62e576fb 100644
--- a/layout/html/base/src/nsListItemFrame.h
+++ b/layout/html/base/src/nsListItemFrame.h
@@ -28,20 +28,13 @@ public:
PRInt32 aIndexInParent,
nsIFrame* aParent);
+ // nsIFrame
NS_IMETHOD ResizeReflow(nsIPresContext* aCX,
nsISpaceManager* aSpaceManager,
const nsSize& aMaxSize,
nsRect& aDesiredRect,
nsSize* aMaxElementSize,
ReflowStatus& aStatus);
-
- NS_IMETHOD IncrementalReflow(nsIPresContext* aCX,
- nsISpaceManager* aSpaceManager,
- const nsSize& aMaxSize,
- nsRect& aDesiredRect,
- nsReflowCommand& aReflowCommand,
- ReflowStatus& aStatus);
-
NS_IMETHOD CreateContinuingFrame(nsIPresContext* aCX,
nsIFrame* aParent,
nsIFrame*& aContinuingFrame);
@@ -62,6 +55,8 @@ protected:
nsIFrame* CreateBullet(nsIPresContext* aCX);
+ void InsertBullet(nsIFrame* aBullet);
+
virtual void PaintChildren(nsIPresContext& aCX,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
diff --git a/layout/html/base/src/nsSpacerPart.cpp b/layout/html/base/src/nsSpacerPart.cpp
index 280293f20441..4ef630a0ad28 100644
--- a/layout/html/base/src/nsSpacerPart.cpp
+++ b/layout/html/base/src/nsSpacerPart.cpp
@@ -91,9 +91,9 @@ SpacerFrame::ResizeReflow(nsIPresContext* aPresContext,
{
// Get cached state for containing block frame
nsBlockReflowState* state = nsnull;
+#if XXX
nsIFrame* parent = mGeometricParent;
while (nsnull != parent) {
- nsIHTMLFrameType* ft;
nsresult status = parent->QueryInterface(kIHTMLFrameTypeIID, (void**) &ft);
if (NS_OK == status) {
nsHTMLFrameType type = ft->GetFrameType();
@@ -108,6 +108,7 @@ SpacerFrame::ResizeReflow(nsIPresContext* aPresContext,
state = (nsBlockReflowState*) shell->GetCachedData(parent);
NS_RELEASE(shell);
}
+#endif
// By default, we have no area
aDesiredSize.width = 0;
@@ -170,8 +171,10 @@ SpacerFrame::ResizeReflow(nsIPresContext* aPresContext,
case TYPE_LINE:
if (0 != width) {
+#if XXX
state->breakBeforeChild = PR_TRUE;
state->breakAfterChild = PR_TRUE;
+#endif
aDesiredSize.height = nscoord(width * p2t);
aDesiredSize.ascent = aDesiredSize.height;
}
diff --git a/layout/html/base/src/nsTextContent.cpp b/layout/html/base/src/nsTextContent.cpp
index efe14b4bcc89..9985b3977bc5 100644
--- a/layout/html/base/src/nsTextContent.cpp
+++ b/layout/html/base/src/nsTextContent.cpp
@@ -34,6 +34,7 @@
#include "nsITimer.h"
#include "nsBlockFrame.h"
#include "prtime.h"
+#include "nsVoidArray.h"
#include "prprf.h"
#include "nsIDOMText.h"
@@ -151,7 +152,7 @@ protected:
nsSize* aMaxElementSize,
nsStyleFont& aFont,
PRInt32 aStartingOffset,
- nsBlockReflowState* aState);
+ nsLineLayout* aLineState);
ReflowStatus ReflowNormal(nsIPresContext* aCX,
nsReflowMetrics& aDesiredSize,
@@ -160,7 +161,7 @@ protected:
nsStyleFont& aFont,
nsStyleText& aTextStyle,
PRInt32 aStartingOffset,
- nsBlockReflowState* aState);
+ nsLineLayout* aLineState);
public:
PRInt32 mContentOffset;
@@ -665,23 +666,13 @@ NS_METHOD TextFrame::ResizeReflow(nsIPresContext* aCX,
}
// Get cached state for containing block frame
- nsBlockReflowState* state = nsnull;
- nsIFrame* parent = mGeometricParent;
- while (nsnull != parent) {
- nsIHTMLFrameType* ft;
- nsresult status = parent->QueryInterface(kIHTMLFrameTypeIID, (void**) &ft);
- if (NS_OK == status) {
- nsHTMLFrameType type = ft->GetFrameType();
- if (eHTMLFrame_Block == type) {
- break;
- }
+ nsLineLayout* lineLayoutState = nsnull;
+ nsBlockReflowState* state = nsBlockFrame::FindBlockReflowState(aCX, this);
+ if (nsnull != state) {
+ lineLayoutState = state->mCurrentLine;
+ if (nsnull != lineLayoutState) {
+ lineLayoutState->mReflowResult = NS_LINE_LAYOUT_REFLOW_RESULT_AWARE;
}
- parent->GetGeometricParent(parent);
- }
- if (nsnull != parent) {
- nsIPresShell* shell = aCX->GetShell();
- state = (nsBlockReflowState*) shell->GetCachedData(parent);
- NS_RELEASE(shell);
}
nsStyleFont* font =
@@ -703,13 +694,14 @@ NS_METHOD TextFrame::ResizeReflow(nsIPresContext* aCX,
if (NS_STYLE_WHITESPACE_PRE == text->mWhiteSpace) {
// Use a specialized routine for pre-formatted text
aStatus = ReflowPre(aCX, aDesiredSize, aMaxSize,
- aMaxElementSize, *font, startingOffset, state);
+ aMaxElementSize, *font, startingOffset,
+ lineLayoutState);
} else {
// Use normal wrapping routine for non-pre text (this includes
// text that is not wrapping)
aStatus = ReflowNormal(aCX, aDesiredSize, aMaxSize,
aMaxElementSize, *font, *text,
- startingOffset, state);
+ startingOffset, lineLayoutState);
}
#ifdef NOISY
@@ -731,7 +723,7 @@ TextFrame::ReflowNormal(nsIPresContext* aCX,
nsStyleFont& aFont,
nsStyleText& aTextStyle,
PRInt32 aStartingOffset,
- nsBlockReflowState* aState)
+ nsLineLayout* aLineState)
{
Text* txt = (Text*) mContent;
const PRUnichar* cp = txt->mText + aStartingOffset;
@@ -749,8 +741,8 @@ TextFrame::ReflowNormal(nsIPresContext* aCX,
// Set whitespace skip flag
PRBool skipWhitespace = PR_FALSE;
- if (nsnull != aState) {
- if (!aState->allowLeadingWhitespace) {
+ if (nsnull != aLineState) {
+ if (aLineState->mSkipLeadingWhiteSpace) {
skipWhitespace = PR_TRUE;
mFlags |= TEXT_SKIP_LEADING_WS;
}
@@ -769,6 +761,7 @@ TextFrame::ReflowNormal(nsIPresContext* aCX,
nscoord maxWidth = aMaxSize.width;
nscoord maxWordWidth = 0;
const PRUnichar* lastWordEnd = cpStart;
+ const PRUnichar* lastWordStart = cpStart;
PRBool hasMultibyte = PR_FALSE;
PRBool endsInWhitespace = PR_FALSE;
while (cp < end) {
@@ -798,6 +791,7 @@ TextFrame::ReflowNormal(nsIPresContext* aCX,
hasMultibyte = PR_TRUE;
}
const PRUnichar* wordStart = cp - 1;
+ lastWordStart = wordStart;
while (cp < end) {
ch = *cp;
if (ch >= 256) {
@@ -840,13 +834,14 @@ TextFrame::ReflowNormal(nsIPresContext* aCX,
mFlags |= TEXT_HAS_MULTIBYTE;
}
- if (nsnull != aState) {
+ if (nsnull != aLineState) {
if (0 == x) {
// Since we collapsed into nothingness (all our whitespace
- // is ignored) leave the aState->allowLeadingWhitespace
+ // is ignored) leave the aState->mSkipLeadingWhiteSpace
// flag alone since it doesn't want leading whitespace
- } else {
- aState->allowLeadingWhitespace = !endsInWhitespace;
+ }
+ else {
+ aLineState->mSkipLeadingWhiteSpace = endsInWhitespace;
}
}
@@ -889,7 +884,7 @@ TextFrame::ReflowPre(nsIPresContext* aCX,
nsSize* aMaxElementSize,
nsStyleFont& aFont,
PRInt32 aStartingOffset,
- nsBlockReflowState* aState)
+ nsLineLayout* aLineState)
{
Text* txt = (Text*) mContent;
const PRUnichar* cp = txt->mText + aStartingOffset;
@@ -903,16 +898,16 @@ TextFrame::ReflowPre(nsIPresContext* aCX,
PRBool hasMultibyte = PR_FALSE;
PRUint16 tabs = 0;
PRIntn col = 0;
- if (nsnull != aState) {
- col = aState->column;
+ if (nsnull != aLineState) {
+ col = aLineState->mColumn;
}
mColumn = (PRUint16) col;
nscoord spaceWidth = widths[' '];
while (cp < end) {
PRUnichar ch = *cp++;
if (ch == '\n') {
- if (nsnull != aState) {
- aState->breakAfterChild = PR_TRUE;
+ if (nsnull != aLineState) {
+ aLineState->mReflowResult = NS_LINE_LAYOUT_REFLOW_RESULT_BREAK_AFTER;
}
break;
}
@@ -937,8 +932,8 @@ TextFrame::ReflowPre(nsIPresContext* aCX,
}
col++;
}
- if (nsnull != aState) {
- aState->column = col;
+ if (nsnull != aLineState) {
+ aLineState->mColumn = col;
}
if (hasMultibyte) {
mFlags |= TEXT_HAS_MULTIBYTE;